Reputation: 909
There is something that I didn't understand with TListBox and TListBoxItem in Delphi 10.2 Tokyo.
Some values (TListBoxItem) are load to my ListBox, when the first letter change I add a TListBoxGroupHeader.
procedure TForm1.Button1Click(Sender: TObject);
var
lbItem: TListBoxItem;
Letter: string;
ListBoxGroupHeader: TListBoxGroupHeader;
i: integer;
ListValue: TStringList;
begin
Letter := '';
ListValue := TStringList.Create;
try
ListValue.Add('Germany');
ListValue.Add('Georgie');
ListValue.Add('France');
ListValue.Add('Venezuela');
ListValue.Add('Poland');
ListValue.Add('Russia');
ListValue.Add('Sweden');
ListValue.Add('Denmark');
ListBox1.BeginUpdate;
for i := 0 to ListValue.Count - 1 do
begin
if Letter <> Copy(ListValue[i], 0, 1).ToUpper then
begin
ListBoxGroupHeader := TListBoxGroupHeader.Create(ListBox1);
ListBoxGroupHeader.Text := Copy(ListValue[i], 0, 1).ToUpper;
ListBox1.AddObject(ListBoxGroupHeader);
end;
lbItem := TListBoxItem.Create(ListBox1);
lbItem.Text := ListValue[i];
lbItem.Tag := i;
ListBox1.AddObject(lbItem);
Letter := Copy(ListValue[i], 0, 1).ToUpper;
end;
finally
ListBox1.EndUpdate;
FreeAndNil(ListValue);
end;
end;
I use a TEdit to search in this ListBox. That's here that I have a problem. If ListBoxItem contain the content of the Edit I set Visible to True, else I set it to False.
procedure TForm1.Edit1ChangeTracking(Sender: TObject);
var
i : integer;
ListBoxItem: TListBoxItem;
begin
ListBox1.BeginUpdate;
try
for i := 0 to ListBox1.Items.Count - 1 do
begin
if ListBox1.ListItems[i] is TListBoxItem then
begin
ListBoxItem := TListBoxItem(ListBox1.ListItems[i]);
if Edit1.Text.Trim = '' then
begin
ListBoxItem.Visible := True
end
else
begin
if ListBox1.ListItems[i] is TListBoxGroupHeader then
ListBoxItem.Visible := False
else
ListBoxItem.Visible := ListBoxItem.Text.ToLower.Contains(Edit1.Text.Trim.ToLower);
end;
end;
end;
finally
ListBox1.EndUpdate;
end;
end;
The first GroupHeader (letter G) is always visible ! and it's look like there is a ListBoxItem behind the GroupHeader.. When I use a checkpoint Visible is set to false .. so I didn't understand..
If I write the letter "V" I only see the GroupHeader with letter "G".
I have evene try to change the text value if it's a GroupHeader.
if ListBox1.ListItems[i] is TListBoxGroupHeader then
ListBoxItem.Text := '>>' + ListBoxItem.Text + '<<'
Thats change text but not for the first GroupHeader (letter G) ...
Don't know if I use it bad, or if it's a bug ??
Upvotes: 1
Views: 1220
Reputation: 7912
I could have reproduce what you've described and it has something to do with hiding header whilst keeping item under that header visible. In such case application shows header rather than the item. I haven't checked what's wrong inside but it seems it is not what you want. IMHO you want to keep visible items that match to a search text with their respective header and hide only headers with no items under.
If that is so, try this:
procedure FilterItems(const Text: string; ListBox: TListBox);
var
I: Integer; { ← loop variable }
Hide: Boolean; { ← flag indicating if we want to hide the last header we passed }
Item: TListBoxItem; { ← currently iterated item }
Head: TListBoxGroupHeader; { ← last header item we passed during iteration }
begin
Head := nil;
Hide := True;
ListBox.BeginUpdate;
try
{ if search text is empty, show all items }
if Text.IsEmpty then
for I := 0 to ListBox.Content.ControlsCount - 1 do
ListBox.ListItems[I].Visible := True
else
{ otherwise compare text in non header items }
begin
for I := 0 to ListBox.Content.ControlsCount - 1 do
begin
Item := ListBox.ListItems[I];
{ if the iterated item is header }
if Item is TListBoxGroupHeader then
begin
{ set the previous header visibility by at least one visible item }
if Assigned(Head) then
Head.Visible := not Hide;
{ assume hiding this header and store its reference }
Hide := True;
Head := TListBoxGroupHeader(Item);
end
else
{ if the iterated item is a regular item }
if Item is TListBoxItem then
begin
{ set the item visibility by matching text; if the item remains visible, it
means we don't want to hide the header, so set our flag variable as well }
if Item.Text.ToLower.Contains(Text) then
begin
Hide := False;
Item.Visible := True;
end
else
Item.Visible := False;
end;
end;
{ the iteration finished, so now setup visibility of the last header we passed }
if Assigned(Head) then
Head.Visible := not Hide;
end;
finally
ListBox.EndUpdate;
end;
end;
procedure TForm1.Edit1ChangeTracking(Sender: TObject);
begin
FilterItems(Edit1.Text.Trim.ToLower, ListBox1);
end;
Upvotes: 1