Hiding items in TListBox while filtering by String

后端 未结 2 557
深忆病人
深忆病人 2021-01-13 06:02

Short Version: Is there any way to control or modify LisBox items individually? for example set their Visible property to False separately. I foun

2条回答
  •  栀梦
    栀梦 (楼主)
    2021-01-13 06:50

    The items of a VCL listbox, List Box in the API, does not have any visibility property. The only option for not showing an item is to delete it.

    You can use the control in virtual mode however, where there are no items at all. You decide what data to keep, what to display. That's LBS_NODATA window style in the API. In VCL, set the style property to lbVirtual.

    Extremely simplified example follows.

    Let's keep an array of records, one record per virtual item.

    type
      TListItem = record
        FileName: string;
        Visible: Boolean;
      end;
    
      TListItems = array of TListItem;
    

    You can extend the fields as per your requirements. Visibility is one of the main concerns in the question, I added that. You'd probably add something that represents the original name so that you know what name have been changed, etc..

    Have one array per listbox. This example contains one listbox.

    var
      ListItems: TListItems;
    

    Better make it a field though, this is for demonstration only.

    Required units.

    uses
      ioutils, types;
    

    Some initialization at form creation. Empty the filter edit. Set listbox style accordingly. Fill up some file names. All items will be visible at startup.

    procedure TForm1.FormCreate(Sender: TObject);
    var
      ListFiles: TStringDynArray;
      i: Integer;
    begin
      ListFiles := ioutils.TDirectory.GetFiles(TDirectory.GetCurrentDirectory);
    
      SetLength(ListItems, Length(ListFiles));
      for i := 0 to High(ListItems) do begin
        ListItems[i].FileName := ListFiles[i];
        ListItems[i].Visible := True;
      end;
    
      ListBox1.Style := lbVirtual;
      ListBox1.Count := Length(ListFiles);
    
      Edit1.Text := '';
    end;
    

    In virtual mode the listbox is only interested in the Count property. That will arrange how many items will show, accordingly the scrollable area.

    Here's the filter part, this is case sensitive.

    procedure TForm1.Edit1Change(Sender: TObject);
    var
      Text: string;
      Cnt: Integer;
      i: Integer;
    begin
      Text := Edit1.Text;
      if Text = '' then begin
        for i := 0 to High(ListItems) do
          ListItems[i].Visible := True;
        Cnt := Length(ListItems);
      end else begin
        Cnt := 0;
        for i := 0 to High(ListItems) do begin
          ListItems[i].Visible := Pos(Text, ListItems[i].FileName) > 0;
          if ListItems[i].Visible then
            Inc(Cnt);
        end;
      end;
      ListBox1.Count := Cnt;
    end;
    

    The special case in the edit's OnChange is that when the text is empty. Then all items will show. Otherwise code is from the question. Here we also keep the total number of visible items, so that we can update the listbox accordingly.

    Now the only interesting part, listbox demands data.

    procedure TForm1.ListBox1Data(Control: TWinControl; Index: Integer;
      var Data: string);
    var
      VisibleIndex: Integer;
      i: Integer;
    begin
      VisibleIndex := -1;
      for i := 0 to High(ListItems) do begin
        if ListItems[i].Visible then
          Inc(VisibleIndex);
        if VisibleIndex = Index then begin
          Data := ListItems[i].FileName;
          Break;
        end;
      end;
    end;
    

    What happens here is that the listbox requires an item to show providing its index. We loop through the master list counting visible items to find out which one matches that index, and supply its text.

自定义标题
段落格式
字体
字号
代码语言
提交回复
热议问题