#listview #delphi
Вопрос:
После настройки ListView в виртуальном режиме ListView1.Выбран.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
Комментарии:
1. Что вы делаете с этой информацией? Знание этого может помочь людям придумать альтернативу.
2. Я показываю окно редактирования в этой позиции.
3. Не могли бы вы привести минимальный, но полный, воспроизводимый пример, с которым мы могли бы поиграть? Отредактируйте свой вопрос, чтобы опубликовать файлы .pas и .dfm для этого примера.
4. Добавлен простой пример.
5. При использовании виртуального режима
TListView
необходимо использовать временноеTListItem
значение для удовлетворения таких свойствSelected
, какItems[]
, и т.д. Я нахожу, что это, как правило , вызывает тонкие/нежелательные побочные эффекты, поэтому я обычно просто игнорирую такие свойства в виртуальном режиме и сразу обращаюсь к API Win32, чтобы получить необходимую мне информацию, напримерListView_GetNextItem(LVNI_SELECTED)
, иListView_GetItemRect()
т. Д
Ответ №1:
Я могу воспроизвести вашу проблему. Я нашел обходной путь: используйте прямоугольник отображения выбранного элемента:
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;
Комментарии:
1. Дополнительный вопрос: Как получить левую позицию столбца? Сетка находится в режиме выбора строк. Есть ли лучший способ рассчитать его по ширине каждого столбца?
2. Отредактировал мой ответ. Кстати, в следующий раз, когда вы спросите, вам следует лучше сформулировать свой вопрос.