avue
avue

Reputation: 129

Filter a multiple column listview

The following bit of code will filter the items of a TListView control based upon what is typed in an TEdit control and it works fine if the ListView consist of a single column, if you have more then 1 column however, then the items in the other columns are destroyed when the filter is applied, so I hoping someone might know what would need to be added to the code below to preserve those columns when the ListView is filtered.

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, strutils, StdCtrls, ComCtrls;

type
  TForm1 = class(TForm)
    ListView1: TListView;
    Edit1: TEdit;
    procedure Edit1Change(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;
  StrList : TStringList;

implementation

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
var
   Index : Integer;
begin
     StrList := TStringList.Create;
     for Index := 0 to ListView1.Items.Count -1 do
        StrList.Add(ListView1.Items[Index].Caption);
end;
procedure TForm1.Edit1Change(Sender: TObject);
var
   Index : Integer;
begin
   ListView1.Clear;
   for Index := 0 to StrList.Count - 1 do
     if Pos(Edit1.Text, StrList.Strings[Index]) > 0 then
        ListView1.AddItem(StrList.Strings[Index], nil);
   if Edit1.Text = '' then
     for Index := 0 to StrList.Count - 1 do
       ListView1.AddItem(StrList.Strings[Index], nil);
end;


end.

Upvotes: 0

Views: 5580

Answers (1)

Ondrej Kelle
Ondrej Kelle

Reputation: 37221

You are clearing the listview and then you're adding to its Items without SubItems - that's why additional columns are empty.

To filter the contents of the listview it might be easier to use the listview in virtual mode where you set the count and supply the data on demand in a callback. Here's a quick example:

The form:

object Form1: TForm1
  Left = 0
  Top = 0
  Caption = 'Form1'
  ClientHeight = 282
  ClientWidth = 418
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = 'Tahoma'
  Font.Style = []
  OldCreateOrder = False
  OnCreate = FormCreate
  OnDestroy = FormDestroy
  PixelsPerInch = 96
  TextHeight = 13
  object ListView1: TListView
    Left = 8
    Top = 39
    Width = 402
    Height = 235
    Columns = <
      item
        Caption = 'Name'
        Width = 80
      end
      item
        Caption = 'Title'
        Width = 160
      end
      item
        Alignment = taRightJustify
        Caption = 'Age'
        Width = 80
      end>
    OwnerData = True
    SortType = stText
    TabOrder = 0
    ViewStyle = vsReport
    OnData = ListView1Data
  end
  object Edit1: TEdit
    Left = 8
    Top = 12
    Width = 121
    Height = 21
    TabOrder = 1
    OnChange = Edit1Change
  end
end

The code:

unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, System.Contnrs,
  Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.Grids, Vcl.ComCtrls;

type
  TDataItem = class
  private
    FAge: Integer;
    FFirstName: string;
    FLastName: string;
  public
    property Age: Integer read FAge;
    property FirstName: string read FFirstName;
    property LastName: string read FLastName;
  end;

  TForm1 = class(TForm)
    ListView1: TListView;
    Edit1: TEdit;
    procedure ListView1Data(Sender: TObject; Item: TListItem);
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure Edit1Change(Sender: TObject);
  private
    FActiveItems: TList;
    FItems: TObjectList;
    procedure AddTestData;
    procedure ApplyFilter(const S: string = '');
  public
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.AddTestData;
  procedure AddDataItem(const FirstName, LastName: string; Age: Integer);
  var
    DataItem: TDataItem;
  begin
    DataItem := TDataItem.Create;
    try
      DataItem.FFirstName := FirstName;
      DataItem.FLastName := LastName;
      DataItem.FAge := Age;
      FItems.Add(DataItem);
    except
      DataItem.Free;
      raise;
    end;
  end;
begin
  AddDataItem('John', 'Doe', 26);
  AddDataItem('Jane', 'Warwick', 29);
  AddDataItem('Stephen', 'Marley', 33);
  AddDataItem('Alice', 'Connoly', 48);
  AddDataItem('Adam', 'Spears', 63);
end;

procedure TForm1.ApplyFilter(const S: string);
var
  I: Integer;
begin
  ListView1.Items.BeginUpdate;
  try
    ListView1.Clear;
    FActiveItems.Clear;
    for I := 0 to FItems.Count - 1 do
      if (S = '') or (Pos(UpperCase(S), UpperCase(TDataItem(FItems[I]).FirstName)) <> 0) then
        FActiveItems.Add(FItems[I]);
    ListView1.Items.Count := FActiveItems.Count;
  finally
    ListView1.Items.EndUpdate;
  end;
end;

procedure TForm1.Edit1Change(Sender: TObject);
begin
  ApplyFilter((Sender as TEdit).Text);
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  FItems := TObjectList.Create;
  FActiveItems := TList.Create;
  AddTestData;
  ApplyFilter(Edit1.Text);
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  FActiveItems.Free;
  FItems.Free;
end;

procedure TForm1.ListView1Data(Sender: TObject; Item: TListItem);
var
  DataItem: TDataItem;
begin
  DataItem := FActiveItems[Item.Index];
  Item.Caption := DataItem.FirstName;
  Item.SubItems.Add(DataItem.LastName);
  Item.SubItems.Add(IntToStr(DataItem.Age));
end;

end.

Upvotes: 3

Related Questions