Author: Ernesto De Spirito
How can I know which column was click in a TListView? GetItemAt only works with the
first column.
Answer:
Solve 1:
The method GetItemAt only provides the information about which ListItem (if any) is
located at the specified coordinates passed as parameters, but only works with the
first column of the TListView. The rest are ignored. If we needed to know if the
user clicked on an element in another column, we can declare a new method in a
derived class:
1 type
2 TListViewX = class(TListView)
3 public
4 function GetItemAtX(X, Y: integer; var Col: integer): TListItem;
5 end;
6
7 implementation
8
9 function TListViewX.GetItemAtX(X, Y: integer;
10 var Col: integer): TListItem;
11 var
12 i, n, RelativeX, ColStartX: Integer;
13 ListItem: TlistItem;
14 begin
15 Result := GetItemAt(X, Y);
16 if Result <> nil then
17 begin
18 Col := 0; // First column
19 end
20 else if (ViewStyle = vsReport)
21 and (TopItem <> nil) then
22 begin
23 // First, let's try to find the row
24 ListItem := GetItemAt(TopItem.Position.X, Y);
25 if ListItem <> nil then
26 begin
27 // Now let's try to find the Column
28 RelativeX := X - ListItem.Position.X - BorderWidth;
29 ColStartX := Columns[0].Width;
30 n := Columns.Count - 1;
31 for i := 1 to n do
32 begin
33 if RelativeX < ColStartX then
34 break;
35 if RelativeX <= ColStartX +
36 StringWidth(ListItem.SubItems[i - 1]) then
37 begin
38 Result := ListItem;
39 Col := i;
40 break;
41 end; //if
42 Inc(ColStartX, Columns[i].Width);
43 end; //for
44 end; //if
45 end; //if
46 end;
Casting to the new class
We don't need to intall this new component and register it in the components
palette as we explained in another article ("Adding new methods and properties
without registering new components"). Instead, any time we want to access this
method, we can just cast the object (for example ListView1) to our new class. For
example in a MouseDown event:
47
48 procedure TForm1.ListView1MouseDown(Sender: TObject;
49 Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
50 var
51 col: integer;
52 li: TListItem;
53 begin
54 li := TListViewX.GetItemAtX(x, y, col);
55 if li <> nil then
56 ShowMessage('Column #' + IntToStr(col));
57 end;
Solve 2:
58 uses
59 commctrl;
60
61 procedure TForm1.ListView1Click(Sender: TObject);
62 var
63 pt: TPoint;
64 col: Integer;
65 pos: Integer;
66 begin
67 GetCursorPos(pt);
68 pt := Listview1.ScreenToClient(pt);
69 Pos := -GetScrollPos(ListView1.Handle, SB_HORZ);
70 Col := -1;
71 while Pos < Pt.X do
72 begin
73 Inc(Col);
74 Inc(Pos, ListView_GetColumnWidth(ListView1.Handle, Col));
75 end;
76 if Col >= ListView1.Columns.Count then
77 Col := -1; {clicked past last column}
78 showmessage(inttostr(col));
79 end;
|