Delphi-TListView在虚拟模式下的问题

Delphi-TListView在虚拟模式下的问题,listview,delphi,Listview,Delphi,在虚拟模式下设置ListView后,ListView1.Selected.Top始终返回0。我在双击列表视图时使用该属性在该位置显示编辑框 我如何解决这个问题 下面是.pas和.dfm文件的示例。我想在双击的位置打开编辑框 unit Unit1; interface uses Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, Vcl.Cont

在虚拟模式下设置ListView后,ListView1.Selected.Top始终返回0。我在双击列表视图时使用该属性在该位置显示编辑框

我如何解决这个问题

下面是.pas和.dfm文件的示例。我想在双击的位置打开编辑框

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.
单元1;
接口
使用
Winapi.Windows、Winapi.Messages、System.SysUtils、System.Variants、System.Classes、Vcl.Graphics、,
控件、窗体、对话框、控件、集合、默认值;
类型
TLVData=记录
第0列:字符串;
第1列:字符串;
第2列:字符串;
结束;
类型
TForm1=类(TForm)
ListView1:TListView;
编辑1:TEdit;
过程列表视图1DBLclick(发送方:ToObject);
程序NewEntry(i:整数);
程序表单显示(发送方:TObject);
过程列表视图1数据(发送方:ToObject;项:TListItem);
私有的
{私有声明}
公众的
{公开声明}
结束;
变量
表1:TForm1;
LVDataList:TList;
实施
{$R*.dfm}
过程TForm1.NewEntry(i:整数);
变量
LVData:TLVData;
开始
如果未分配(LVDataList),则LVDataList:=TList.Create;
LVData.Column0:=“Column0:”+IntToStr(i);
LVData.Column1:=“Column1:”+IntToStr(i);
LVData.Column2:='Column2:'+IntToStr(i);
LVDataList.Add(LVData);
结束;
程序TForm1.FormShow(发送方:TObject);
变量
i:整数;
开始
对于i:=0到9 do
新条目(i);
ListView1.Items.Count:=LVDataList.Count;
结束;
过程TForm1.ListView1数据(发送方:ToObject;项:TListItem);
varⅠ:整数;
开始
如果未分配(项目),则退出;
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);
结束;
程序TForm1.ListView1DblClick(发送方:ToObject);
开始
Edit1.Text:=ListView1.Selected.SubItems[0];
Edit1.Top:=ListView1.Top+ListView1.Selected.Top-2;
编辑1.宽度:=100;
编辑1.表演;
Edit1.SetFocus;
结束;
结束。
和.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
对象格式1:t格式1
左=0
Top=0
标题='Form1'
ClientHeight=412
ClientWidth=784
颜色=clBtnFace
Font.Charset=默认字符集
Font.Color=clWindowText
字体高度=-11
Font.Name='Tahoma'
Font.Style=[]
OldCreateOrder=False
OnShow=FormShow
PixelsPerInch=96
text高度=13
对象列表视图1:TListView
左=0
Top=0
宽度=784
高度=412
Align=alClient
列=<
项目
自动调整大小=真
标题='Column0'
最小宽度=100
结束
项目
自动调整大小=真
标题='Column1'
最小宽度=100
结束
项目
自动调整大小=真
标题='Column2'
最小宽度=100
结束>
网格线=真
HideSelection=False
多选=真
OwnerData=True
只读=真
RowSelect=True
TabOrder=0
ViewStyle=vsReport
OnData=ListView1Data
OnDblClick=ListView1DblClick
结束
对象Edit1:TEdit
左=360
Top=168
宽度=121
高度=21
TabOrder=1
Text='Edit1'
可见=假
结束
结束

我可以重现您的问题。我找到了一个解决方法:使用所选项目的显示矩形:

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;
如果要获取子项,则必须遍历列以查找用户单击的列。我们需要确定鼠标的位置,以便安装OnMouseDown事件处理程序来保存鼠标坐标,并使用它来查找列

  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;
private
FMouseDown:TPoint;
程序TForm1.ListView1MouseDown(发送方:ToObject;按钮:TMouseButton;
移位:t移位状态;X,Y:整数);
开始
FMouseDown.X:=X;
FMouseDown.Y:=Y;
结束;
程序TForm1.ListView1DblClick(发送方:ToObject);
变量
Rect:TRect;
I:整数;
X1,X2:整数;
Col:整数;
开始
如果未分配(ListView1.选中),则
出口
Rect:=ListView1.Selected.DisplayRect(drBounds);
X1:=0;
X2:=0;
Col:=-1;
对于I:=0到ListView1.Columns.Count-1,请不要开始
X2:=X2+ListView1。列[0]。宽度;
如果(FMouseDown.X>=X1)和(FMouseDown.X
你在用这些信息做什么。知道这一点可能会帮助人们想出一个替代方案。我在那个位置显示编辑框。你们能提供一个最小但完整的,可复制的例子,我们可以玩吗?编辑您的问题以发布该示例的.pas和.dfm文件。添加了简单示例。在使用虚拟模式时,
TListView
必须使用临时的
TListItem
,以满足
选定的
项[]
等属性。我发现这往往会导致微妙/不必要的副作用,因此,我通常在虚拟模式下忽略这些属性,直接转到Win32 API以获取所需的信息,如
ListView\u GetNextItem(LVNI\u SELECTED)
ListView\u GetItemRect()
,等等。附加问题:如何获取列的左位置?网格处于行选择模式。有什么更好的方法可以从每一列的宽度计算出来吗?编辑了我的答案。顺便说一句,下次你提问的时候,你应该更好地阐述你的问题。