Search code examples
delphifiremonkeydelphi-10.3-rio

How to search a FMX.TListView header as well as items


I have a LiveBindings databound FMX.TListView with the FieldName being Stage and the FillHeaderFieldName being Production. When the app is running, I see a list of Productions using the HeaderAppearance, and within each Production, there is a list of Stages using the ItemAppearance. I've turned on SearchVisible to get the components search panel to show at the top of the list.

enter image description here

Currently, typing into the search box only filters on the Stage, and not the Production.

enter image description here

I'd like to be able to do both, and I'd like to be able to do it without making another REST call with filter parameters. I understand I would probably need to write an event handler for the OnSearchChange event, and I have this bit of code to get the search text entered:

  List := Sender as TListView;

  for I := 0 to List.Controls.Count-1 do
    if List.Controls[I].ClassType = TSearchBox then
    begin
      SearchBox := TSearchBox(List.Controls[I]);
      break;
    end;

And I think I need to set the Items.Filter property, and I used this bit of code:

  Lower := LowerCase(SearchBox.Text.Trim);

  List.Items.Filter :=
    function(X: string): Boolean
    begin
      Result:= (Lower = EmptyStr) or LowerCase(X).Contains(Lower);
    end;

One of the problems is that the ListView component is applying its filtering as soon as a character is typed, while the OnSearchChange event only fires when the searchbox loses focus.

The second problem is that, even after the event is fired and the new filter function set, nothing happens to the list.

I've confirmed that the List.Items collection in my "36" example does actually contain all 6 items - the 3 header items and the 3 detail items - so I'm unsure why the filter is not applying to the header items as it does the detail items.


Solution

  • I tried this out and found a solution. Keep in mind I don't have access to Delphi 10.3 Rio. I'm using 10.1 Berlin. Also keep in mind that what I usually do is bind in the code and not visually. But for this I stuck to visual binding.

    As a dataset I have used a TFDMemoryTable (mt1) with 2 data fields (fmt1Prod and fmt1Stage) and 1 calculated field (fmt1Search). I have the following handler to calculate the Search field:

    Procedure TForm2.mt1CalcFields(DataSet: TDataSet);
    Begin
      fmt1Search.AsString := fmt1Prod.AsString + '|' + fmt1Stage.AsString;
    End;
    

    I put some random data in the memory table OnFormCreate:

    Procedure TForm2.FormCreate(Sender: TObject);
    Var
      i1, i2: Integer;
      s1, s2: String;
    Begin
      mt1.CreateDataSet;
      For i1   := 1 To 13 Do Begin
        s1     := 'Prod' + FormatFloat('00', i1);
        For i2 := Random(6) To Random(14) Do Begin
          s2   := 'Stage' + FormatFloat('00', i2);
          mt1.Append;
          fmt1Prod.AsString  := s1;
          fmt1Stage.AsString := s2;
          mt1.Post;
        End;
      End;
    End;
    

    I have put on Form2 a TGrid and a TListView. Both are bound to the dataset. Data and calculated fields show up properly in the TGrid (just to check).

    The TListView is bound to the dataset as follows:

    Synch            <-> *
    ItemHeader.Text  <-  Prod
    ItemHeader.Break <-  Prod
    Item.Text        <-  Search
    Item.Detail      <-  Stage
    

    I did this because I cannot find a way to have the TListView searchbox work on anything but the Text of the items. Ok then... but this can be worked around though:

    • Set TListView.ItemAppeance to Custom
    • Find the TListView/ItemAppearance/Item/Text object in the structure and set Visible to False
    • Find the TListView/ItemAppearance/Item/Detail object in the structure and set Visible to True

    I'm not sure all of the above is necessary, but it works. If your TListView is editable then you will probably need to fiddle with the ItemEditAppearance too.

    Remember that with custom item appearance you can actually set the list view items to look just about anyway you want. You can add and remove labels, images and other things. It's not as powerful as designing a form, but you can do a lot with it. But all you really need here is to hide the search text and show the stage text somewhere in the item.

    And... for more sophisticated item appearance you may have to do some code binding (non sure of this though).