2011年7月3日星期日

Group Items in Delphi's TListView Control



The TListView Delphi control displays and manages a list of items, displayed in columns, vertically or horizontally, with small or large icons.


Windows XP, and later versions, support grouping items in a list view. With the grouping feature of the ListView control, related sets of items can be displayed in groups. Groups are separated on the screen by horizontal group headers that contain the group titles.

Up until Delphi 2009, groups are not directly supported by the implementation of the TListView control.

Grouping Items in a TListView

If you are using Delphi 2007, Delphi 7 or any other previous version, you can also have grouping functionality in the TListView control.

The idea is to mimic item grouping with expandable and collapsible group items.

We'll hijack the Data property of the TListItem object. Items in a list view are presented by a TListItem object - used to specify the caption of the item, its glyph and other visual (and behavior) elements. The Data property specifies any application-specific data associated with the list item.

Note that the grouping implementation presented here leaves enough space for customization for your application-specific needs.

To follow along, have a ListView control on a form displaying its items in details view (ViewSytle = vsReport).

For the sake of simplicity we'll have two columns ("title" and "value") in the list view.

Download the sample project

Each list item will be assigned with a custom object, either a TGroupItem or a TItem. The TGroup item holds a group of TItem items. Group items can be expanded or collapsed to show or hide their "sub-items".

Normal list view items will be described with a TItem class:

 TItem = class
private
fTitle: string;
fValue: string;
public
constructor Create(const title, value : string) ;
property Title: string read fTitle;
property Value : string read fValue;
end;

An item that will be used to hold a collection of items is described by the TGroupItem class:

 TGroupItem = class
private
fItems : TObjectList;
fCaption: string;
fListItem: TListItem;
fExpanded: boolean;
function GetItems: TObjectList;
public
constructor Create(const caption : string; const numberOfSubItems : integer) ;
destructor Destroy; override;

procedure Expand;
procedure Collapse;

property Expanded : boolean read fExpanded;
property Caption : string read fCaption;
property Items : TObjectList read GetItems;
property ListItem : TListItem read fListItem write fListItem;
end;
The FillListViewGroups adds items to the list view:
 procedure TForm1.FillListViewGroups;

procedure AddGroupItem(gi : TGroupItem) ;
var
li : TListItem;
begin
li := lvGroups.Items.Add;

li.Caption := gi.Caption;
li.ImageIndex := 1; //collapsed

li.Data := gi;
gi.ListItem := li; //link "back"
end;
begin
ClearListViewGroups;

AddGroupItem(TGroupItem.Create('Group A', 3)) ;
AddGroupItem(TGroupItem.Create('Group B', 1)) ;
AddGroupItem(TGroupItem.Create('Group C', 4)) ;
AddGroupItem(TGroupItem.Create('Group D', 5)) ;
end;
A list item is added to the list view with its Data property pointing to an instance of a TGroupItem.

In the constructor we add dummy sub-items:

 constructor TGroupItem.Create(const caption: string; const numberOfSubItems : integer) ;
var
cnt : integer;
begin
fCaption := caption;

for cnt := 1 to numberOfSubItems do
begin
Items.Add(TItem.Create(caption + ' item ' + IntToStr(cnt), IntToStr(cnt))) ;
end;
end;
Note that each TItem object added to the Items property of the TGroupItem will be presented as a normal list view item in the list view. Next, we need the functionality to expand or collapse groups. The OnDblClick event of the TListView control is used to handle this:
 //handles TListView OnDblClick event
procedure TForm1.lvGroupsDblClick(Sender: TObject) ;
var
hts : THitTests;
gi : TGroupItem;
begin
hts := lvGroups.GetHitTestInfoAt(lvGroups.ScreenToClient(Mouse.CursorPos).X, lvGroups.ScreenToClient(Mouse.CursorPos).y) ;

if (lvGroups.Selected <> nil) then
begin
if TObject(lvGroups.Selected.Data) is (TGroupItem) then
begin
gi := TGroupItem(lvGroups.Selected.Data) ;

if NOT gi.Expanded then
gi.Expand
else
gi.Collapse;
end;
end;
end;
The selected (double clicked) list view item is determined using the GetHitTestInfoAt method, if the list item is a "group item" the Expand or Collapse method is called.

Expand method will display sub-items, collapse will "hide" sub-items.

Here's the Expand method:

 procedure TGroupItem.Expand;
var
cnt : integer;
item : TItem;
begin
if Expanded then Exit;

ListItem.ImageIndex := 0;
fExpanded := true;

for cnt := 0 to -1 + Items.Count do
begin
item := TItem(Items[cnt]) ;
with TListView(ListItem.ListView).Items.Insert(1 + cnt + ListItem.Index) do
begin
Caption := item.Title;
SubItems.Add(item.Value) ;
Data := item;
ImageIndex := -1;
end;
end;
end;

Note: the accompanying source code to this article includes the Collapse method and other relevant event handlers.

The OnAdvancedCustomDrawItem event is used to customize (bold) the drawing of group items.

A TImageList is used to hold "+" and "-" glyphs to present expanded or collapsed group items.

Source: http://delphi.about.com/od/vclusing/a/delphi-list-view-groups-expand-collapse-items.htm

沒有留言:

發佈留言