Board index » cppbuilder » Re: TStringGrid or (something else)?

Re: TStringGrid or (something else)?


2006-02-09 04:14:38 AM
cppbuilder87
"poojo hackma" <poojo.com/mail>wrote in message
Quote
What I would like is a way to sort the TStringGrid so that the
folders are on top. How would I "sort" the TStringGrid entries?
TStringGrid does not natively support sorting. You would have to sort the
cells manually. If the grid only has 1 column, then you can use the grid's
Cols property to get a TStrings for the column and then manually sort it.
Otherwise, first store all of the names into a separate TStringList (or
other container of your choosing), then sort it before you fill in the grid
from that. For example:
int __fastcall MySortFunc(TStringList *List, int Index1, int Index2)
{
bool IsFolder1 = (reinterpret_cast<int>(List->Objects[Index1]) !=
0);
bool IsFolder2 = (reinterpret_cast<int>(List->Objects[Index2]) !=
0);
if( IsFolder1 && !IsFolder2 )
return -1;
else if( !IsFolder1 && IsFolder2 )
return 1;
else
return AnsiCompareStr(List->Strings[Index1],
List->Strings[Index2]);
}
{
TStringList *Items = new TStringList;
try
{
WIN32_FIND_DATA fd = {0};
HANDLE hFind = FindFirstFile("your search mask here", &fd);
if( hFind != INVALID_HANDLE_VALUE )
{
do
{
bool IsFolder = ( fd.dwFileAttributes &
FILE_ATTRIBUTE_DIRECTORY );
Items->AddObject(fd.cFileName,
reinterpret_cast<TObject*>(IsFolder));
}
while( FindNextFile(hFind, &fd) );
FindClose(hFind);
Items->CustomSort(MySortFunc);
StringGrid1->RowCount = (Items->Count+1);
StringGrid1->Cells[SomeColumn][0] = "Your header here";
for(int x = 0; x < Items->Count; ++x)
{
if( reinterpret_cast<int>(Items->Objects[x]) != 0 )
StringGrid1->Cells[SomeColumn][x+1] = "[" +
Items->Strings[x] + "]";
else
StringGrid1->Cells[SomeColumn][x+1] =
Items->Strings[x];
}
}
}
__finally {
delete Item;
}
}
Quote
Next, to get fancier, I would like to include an image to indicate that
something is a folder or a file (instead of using [%s] or %s).
You have to use the OnDrawCell event for that. In which case, you have to
keep track of which items are folder and which are not. You can use the
grid's Objects property to store per-cell information that your drawing code
can then utilize as needed. For example:
{
// get a sorted list, then ...
StringGrid1->RowCount = (Items->Count+1);
for(int x = 0; x < Items->Count; ++x)
{
StringGrid1->Cells[SomeColumn][x+1] = Items->Strings[x];
StringGrid1->Objects[SomeColumn][x+1] = Items->Objects[x];
}
}
void __fastcall TForm1::StringGrid1DrawCell(TObject* Sender, int ACol,
int ARow, const TRect &Rect, TGridDrawState State)
{
TRect R = Rect;
if( State.Contains(gdSelected) )
{
StringGrid1->Canvas->Brush->Color = clHighlight;
StringGrid1->Canvas->Font->Color = clHighlightText;
}
else
{
StringGrid1->Canvas->Brush->Color = StringGrid1->Color;
StringGrid1->Canvas->Font->Color = StringGrid1->Font->Color;
}
StringGrid1->Canvas->FillRect(R);
if( ACol == SomeColumn )
{
if( reinterpret_cast<int>(StringGrid1->Objects[ACol][ARow]) !=
0 )
// draw a folder image
else
// draw a file image
R.Left += WidthOfImage;
}
StringGrid1->Canvas->TextRect(R, R.Top+2, R.Left+2,
StringGrid1->Cells[ACol][ARow]);
}
Quote
To do this, I need an example to follow of how to override the OnDraw
method.
Quote
Is TStringGrid the best for this
It is technically possible to use a TStringGrid for it, but I would not call
it the *best* way to approach the issue.
Quote
should I use something else?
I would suggest a TListView in the vsReport style instead. You can set up
two columns, where the first column is an image. You can assign a
TImageList to the TListView and then use the TListItem::ItemIndex property
to specify which image to display for each item. For example:
{
// get a sorted list, then ...
for(int x = 0; x < Items->Count; ++x)
{
TListItem *Item = ListView1->Items->Add();
// can't use the Caption property because of the image
Item->SubItems->Add(Items->Strings[x]);
Item->ImageIndex = reinterpret_cast<int>(Items->Objects[x]);
}
}
If you have a lot of items to display, I would then suggest considering
using the TListView in virtual mode. You can then leave your item data in
the TStringList and have the TListView simply display what is in the
TStringList, using the OnData event, without copying anything into the
TListView at all.For example:
TStringList *ListItems;
__fastcall TForm1::TForm1(TComponent *Owner)
: TForm(Owner)
{
ListItems = new TStringList;
}
__fastcall TForm1::~TForm1()
{
delete ListItems;
}
{
ListItems->Clear();
// fill the sorted list, then ...
ListView1->Items->Count = Items->Count;
}
// set the OwnerData property to true beforehand...
void __fastcall TForm1::ListView1Data(TObject *Sender, TListItem *Item)
{
// can't use the TListItem::Caption property because of the image
Item->SubItems->Add(ListItems->Strings[Item->Index]);
Item->ImageIndex =
reinterpret_cast<int>(ListItems->Objects[Item->Index]);
}
Quote
My main reason for using TStringGrid is that I also use this for
displaying files over an FTP connection.
Well, that doesn't really explain why you choose TStringGrid specifically
for that.
Quote
Would another component be better? A Win32 component?
An Indy component?
Indy has nothing to do with the UI. It is strictly a communications library
only.
Ganbit
 
 

Re:Re: TStringGrid or (something else)?

That's a HUGE help, Gambit! Thanks!
 

Re:Re: TStringGrid or (something else)?

Gambit,
I created a simple project with the code entered into it like so:
int __fastcall TForm1::MySortFunc(TStringList* List, int index1, int index2)
{
bool isFolder1 = (reinterpret_cast<int>(List->Objects[index1]));
bool isFolder2 = (reinterpret_cast<int>(List->Objects[index2]));
if (isFolder1 && isFolder2)
return -1;
else if (!isFolder1 && isFolder2)
return 1;
else
return AnsiCompareStr(List->Strings[index1], List->Strings[index2]);
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormCreate(TObject *Sender) {
TStringList* Items = new TStringList;
try {
WIN32_FIND_DATA fd = {0};
HANDLE hnd = FindFirstFile("your search mask here", &fd);
if (hnd != INVALID_HANDLE_VALUE) {
do {
bool isFolder = (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);
Items->AddObject(fd.cFileName,
reinterpret_cast<TObject*>(isFolder));
} while (FindNextFile(hnd, &fd));
FindClose(hnd);
Items->CustomSort(MySortFunc);
//StringGrid1->RowCount = (Items->Count + 1);
//StringGrid1->Cells[someColumn][0] = "Name of Header";
for (int i = 0; i < Items->Count; ++i) {
if (reinterpret_cast<int>(Items->Objects[i]) != 0)
; //StringGrid1->Cells[someColumn][i + 1] = "[" +
Items->Strings[i] + "]";
else
; //StringGrid1->Cells[someColumn][i + 1] = Items->Strings[i];
}
}
} __finally {
delete Items;
}
}
I haven't added an actual TStringGrid yet, because I am planning on using
the TListView.
Anyway, I got error E2034, which I have gotten in the past but have been
unable to find ways to work around it:
E2034 Cannot convert 'int (_fastcall * (_closure )(TStringList
*,int,int))(TStringList *,int,int)' to 'int (_fastcall *)(TStringList
*,int,int)'
Is there a way to cast a class method like (_fastcall *
(_closure )(TStringList *,int,int)) above into a generic (_fastcall *)?
Does this error mean that the method is a virtual function (or something
else) that I am expected to handle differently?
poojo
 

{smallsort}

Re:Re: TStringGrid or (something else)?

"poojo hackma" <poojo.com/mail>wrote:
Quote

[...] int __fastcall TForm1::MySortFunc
Do not make MySortFunc a member of the TForm1 class ie:
int __fastcall MySortFunc( ... )
Quote
void __fastcall TForm1::FormCreate(TObject *Sender)
Never ever use OnCreate (or OnDestroy). They are Delphi
remnants that should not be used. OnCreate can execute
before the constructor and OnDestroy can execute after
the destructor - both of which is illegal in C++.
~ JD
 

Re:Re: TStringGrid or (something else)?

"poojo hackma" <poojo.com/mail>wrote in message
Quote
I created a simple project with the code entered into it like so:
That is not the code I gave you. Was there a problem using my earlier code?
Quote
int __fastcall TForm1::MySortFunc(TStringList* List, int index1, int
index2)
The sort function cannot be a non-static class member. CustomSort() expects
a standalone function.
Quote
if (isFolder1 && isFolder2)
return -1;
You left out an '!' operator:
if (isFolder1 && !isFolder2)
return -1;
Quote
void __fastcall TForm1::FormCreate(TObject *Sender) {
DO NOT use the OnCreate event in C++. It is a Delphi idiom that produces
illegal behavior in C++ as it can be triggered before the constructor. Use
the actual constructor instead.
Quote
HANDLE hnd = FindFirstFile("your search mask here", &fd);
You did not replace the search criteria with an actual mask yet.
Quote
Is there a way to cast a class method like (_fastcall *
(_closure )(TStringList *,int,int)) above into a generic (_fastcall *)?
No. You need to either 1) remove the method from the class, or 2) declare
the method as 'static' instead.
Quote
Does this error mean that the method is a virtual function (or
something else) that I am expected to handle differently?
The error means that you tried to pass a non-static class method to
something that was not expecting a class method.
Gambit
 

Re:Re: TStringGrid or (something else)?

poojo hackma wrote:
Quote
I created a simple project with the code entered into it like so:
See my replies in the attachment group.
Hans.
 

Re:Re: TStringGrid or (something else)?

You know, sometimes I think I know what I'm doing, and other times I see
evaluations of my thought process and wonder how I get anything to work at
all. :)
Thanks for the input; I'm working on the changes.