Reputation: 85
After setting ListView in virtual mode ListView1.Selected.Top always returns 0. I'm using that property on double click on list view to show edit box at that position.
How can I resolve this?
Example of .pas and .dfm files are here. I want to open edit box on position where it is double clicked.
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ComCtrls, Vcl.StdCtrls,Generics.Collections,Generics.Defaults;
type
TLVData = record
Column0: string;
Column1: string;
Column2: string;
end;
type
TForm1 = class(TForm)
ListView1: TListView;
Edit1: TEdit;
procedure ListView1DblClick(Sender: TObject);
procedure NewEntry(i: integer);
procedure FormShow(Sender: TObject);
procedure ListView1Data(Sender: TObject; Item: TListItem);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
LVDataList : TList<TLVData>;
implementation
{$R *.dfm}
procedure TForm1.NewEntry(i: integer);
var
LVData:TLVData;
begin
if not Assigned(LVDataList) then LVDataList := TList<TLVData>.Create;
LVData.Column0 := 'Column0:' + IntToStr(i);
LVData.Column1 := 'Column1:' + IntToStr(i);
LVData.Column2 := 'Column2:' + IntToStr(i);
LVDataList.Add(LVData);
end;
procedure TForm1.FormShow(Sender: TObject);
var
i: Integer;
begin
for i := 0 to 9 do
NewEntry(i);
ListView1.Items.Count := LVDataList.Count;
end;
procedure TForm1.ListView1Data(Sender: TObject; Item: TListItem);
var i: integer;
begin
if not Assigned(Item) then Exit;
Item.Caption := LVDataList.Items[Item.Index].Column0;
Item.SubItems.Add(LVDataList.Items[Item.Index].Column0);
Item.SubItems.Add(LVDataList.Items[Item.Index].Column1);
Item.SubItems.Add(LVDataList.Items[Item.Index].Column2);
end;
procedure TForm1.ListView1DblClick(Sender: TObject);
begin
Edit1.Text:=ListView1.Selected.SubItems[0];
Edit1.Top:=ListView1.Top+ListView1.Selected.Top-2;
Edit1.Width:=100;
Edit1.Show;
Edit1.SetFocus;
end;
end.
And .dfm:
object Form1: TForm1
Left = 0
Top = 0
Caption = 'Form1'
ClientHeight = 412
ClientWidth = 784
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'Tahoma'
Font.Style = []
OldCreateOrder = False
OnShow = FormShow
PixelsPerInch = 96
TextHeight = 13
object ListView1: TListView
Left = 0
Top = 0
Width = 784
Height = 412
Align = alClient
Columns = <
item
AutoSize = True
Caption = 'Column0'
MinWidth = 100
end
item
AutoSize = True
Caption = 'Column1'
MinWidth = 100
end
item
AutoSize = True
Caption = 'Column2'
MinWidth = 100
end>
GridLines = True
HideSelection = False
MultiSelect = True
OwnerData = True
ReadOnly = True
RowSelect = True
TabOrder = 0
ViewStyle = vsReport
OnData = ListView1Data
OnDblClick = ListView1DblClick
end
object Edit1: TEdit
Left = 360
Top = 168
Width = 121
Height = 21
TabOrder = 1
Text = 'Edit1'
Visible = False
end
end
Upvotes: 1
Views: 1023
Reputation: 12292
I can reproduce your problem. I found a workaround: use the display rectangle of the selected item:
procedure TForm1.ListView1DblClick(Sender: TObject);
var
Rect : TRect;
begin
Rect := ListView1.Selected.DisplayRect(drBounds);
Edit1.Text := ListView1.Selected.SubItems[0];
Edit1.Top := ListView1.Top + Rect.Top - 1;
Edit1.Width :=100;
Edit1.Show;
Edit1.SetFocus;
end;
If you want to get the subitem, then you have to iterate thru the columns to find on which one the user clicked. We need to mouse position so we install an OnMouseDown event handler to save the mouse coordinates and use that to find the column.
private
FMouseDown : TPoint;
procedure TForm1.ListView1MouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
FMouseDown.X := X;
FMouseDown.Y := Y;
end;
procedure TForm1.ListView1DblClick(Sender: TObject);
var
Rect : TRect;
I : Integer;
X1, X2 : Integer;
Col : Integer;
begin
if not Assigned(ListView1.Selected) then
Exit;
Rect := ListView1.Selected.DisplayRect(drBounds);
X1 := 0;
X2 := 0;
Col := -1;
for I := 0 to ListView1.Columns.Count - 1 do begin
X2 := X2 + ListView1.Columns[0].Width;
if (FMouseDown.X >= X1) and (FMouseDown.X < X2) then begin
Col := I;
break;
end;
X1 := X2;
end;
if Col < 0 then
Exit;
Edit1.Text := ListView1.Selected.SubItems[0];
Edit1.Top := ListView1.Top + Rect.Top - 1;
Edit1.Left := X1;
Edit1.Width := X2 - X1; // Same width as column
Edit1.Show;
Edit1.SetFocus;
end;
Upvotes: 1