首页 > 解决方案 > Delphi - 虚拟模式下的 TListView 问题

问题描述

在虚拟模式下设置 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.

和.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

标签: listviewdelphi

解决方案


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

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;

推荐阅读