Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/delphi/8.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/oop/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Delphi 在TListView中就地编辑子项_Delphi_Listview_Edit_Edit In Place_Subitem - Fatal编程技术网

Delphi 在TListView中就地编辑子项

Delphi 在TListView中就地编辑子项,delphi,listview,edit,edit-in-place,subitem,Delphi,Listview,Edit,Edit In Place,Subitem,我有一个包含3列的ListView,希望编辑第三列,即子项[1]。如果我将ListView.ReadOnly设置为True,则允许我编辑所选项目的标题。是否有一种简单的方法可以对子项执行相同的操作?我不想在顶部添加一个无边界控件来进行编辑。我在CodeCentral上编写了示例代码,展示了如何进行编辑 更新: 以下是应立即编译的更新版本: 单元1; 接口 使用 窗口、消息、系统工具、变体、类、图形、, 控件、窗体、对话框、控件; 类型 TForm1=类(TForm) ListView1:TLi

我有一个包含3列的ListView,希望编辑第三列,即子项[1]。如果我将ListView.ReadOnly设置为True,则允许我编辑所选项目的标题。是否有一种简单的方法可以对子项执行相同的操作?我不想在顶部添加一个无边界控件来进行编辑。

我在CodeCentral上编写了示例代码,展示了如何进行编辑

更新:

以下是应立即编译的更新版本:

单元1;
接口
使用
窗口、消息、系统工具、变体、类、图形、,
控件、窗体、对话框、控件;
类型
TForm1=类(TForm)
ListView1:TListView;
过程ListView1编辑(发送方:ToObject;项:TListItem;变量AllowEdit:Boolean);
过程ListView1已编辑(发送方:ToObject;项:TListItem;变量S:字符串);
过程ListView1MouseDown(发送方:ToObject;按钮:TMouseButton;Shift:TShiftState;X,Y:整数);
过程ListView1DrawItem(发送方:TCustomListView;项:TListItem;Rect:TRect;状态:TOwnerDrawState);
私有的
{私有声明}
ColumnToEdit:整数;
OldListViewEditProc:指针;
hListViewEditWnd:HWND;
ListViewEditWndProcPtr:指针;
过程ListViewEditWndProc(变量消息:TMessage);
公众的
{公开声明}
构造函数创建(所有者:TComponent);推翻
毁灭者毁灭;推翻
结束;
变量
表1:TForm1;
实施
使用
Commctrl;
{$R*.dfm}
类型
TListViewCoord=记录
项目:整数;
列:整数;
结束;
TLVGetColumnAt=函数(项:TListItem;常数:TPoint):整数;
TLVGetColumnRect=函数(项:TListItem;ColumnIndex:Integer;变量Rect:TRect):布尔;
TLVGetIndexesAt=函数(ListView:TCustomListView;const-Pt:TPoint;var-Coord:TListViewCoord):布尔;
//TCustomListViewAccess提供对TCustomListView受保护成员的访问
TCustomListViewAccess=class(TCustomListView);
变量
//这些将根据所使用的COMCTL32.DLL版本进行分配
GetColumnAt:TLVGetColumnAt=nil;
GetColumnRect:TLVGetColumnRect=nil;
GetIndexesAt:TLVGetIndexesAt=nil;
//---------------------------------------------------------------------------
//GetComCtl32Version
//
//用途:帮助函数,用于确定加载的CommCtrl32.dll的版本。
//---------------------------------------------------------------------------
变量
COMCTL32版本:DWORD=0;
函数GetComCtl32Version:DWORD;
类型
DLLVERSIONINFO=打包记录
cbSize:DWORD;
主要版本:DWORD;
dwMinorVersion:DWORD;
dwBuildNumber:DWORD;
dwPlatformID:DWORD;
结束;
DLLGETVERSIONPROC=函数(var-dvi:DLLVERSIONINFO):整数;stdcall;
变量
hComCtrl32:HMODULE;
lpDllGetVersion:DLLGETVERSIONPROC;
dvi:DLLVERSIONINFO;
文件名:字符的数组[0..MAX_PATH];
德沃德;
dwSize:DWORD;
pData:指针;
pVersion:指针;
uiLen:UINT;
开始
如果ComCtl32Version=0,则
开始
hComCtrl32:=GetModuleHandle('comctl32.dll');
如果hComCtrl32 0,则
开始
@lpDllGetVersion:=GetProcAddress(hComCtrl32,'DllGetVersion');
如果@lpDllGetVersion为零,则
开始
零内存(@dvi,SizeOf(dvi));
dvi.cbSize:=SizeOf(dvi);
如果lpDllGetVersion(dvi)>=0,则
ComCtl32Version:=MAKELONG(Word(dvi.dwMinorVersion)、Word(dvi.dwmajorvision));
结束;
如果ComCtl32Version=0,则
开始
零内存(@FileName[0],SizeOf(FileName));
如果GetModuleFileName(hComCtrl32,文件名,最大路径)为0,则
开始
dwHandle:=0;
dwSize:=getFileVersionInfo大小(文件名,dwHandle);
如果dwSize为0,则
开始
GetMem(pData,dwSize);
尝试
如果GetFileVersionInfo(文件名、dwHandle、dwSize、pData),则
开始
pVersion:=零;
uiLen:=0;
如果是VerQueryValue(pData,'\',pVersion,uiLen),则
开始
使用PVSFixedFileInfo(PVVersion)^do
ComCtl32Version:=MAKELONG(LOWORD(dwFileVersionMS)、HIWORD(dwFileVersionMS));
结束;
结束;
最后
FreeMem(pData);
结束;
结束;
结束;
结束;
结束;
结束;
结果:=COMCTL32版本;
结束;
//---------------------------------------------------------------------------
//手动获取列
//
//用途:返回指定坐标处的列索引,
//相对于指定的项目
//---------------------------------------------------------------------------
功能手册\u GetColumnAt(项目:TListItem;常数:TPoint):整数;
变量
LV:TCustomListViewAccess;
R:TRect;
I:整数;
开始
LV:=TCustomListViewAccess(Item.ListView);
//确定当前列值的尺寸,以及
//查看坐标是否在列值内
//获取整个项目的尺寸
R:=Item.DisplayRect(drBounds);
//在所有列中循环查找单击的值
对于I:=0到LV.Columns.Count-1 do
开始
右栏:=(左栏+左栏[I]宽度);
如果PtInRect(R,Pt),则
开始
结果:=I;
出口
结束;
右左:=右;
结束;
结果:=-1;
结束;
//---------------------------------------------------------------------------
//手动_GetColumnRect
//
//目的:计算指定柱的尺寸,
//相对于指定的项目
//---------------------------------------------------------------------------
函数手册\u GetColumnRect(项:TListItem;ColumnIndex:Integer;变量Rect:TRect):布尔;
变量
LV:TCustomListViewAccess;
I:整数;
开始
结果:=假;
LV:=TCustomListViewAccess(Item.ListView);
//确保索引在有效范围内
如果(列索引)
Const
  USER_EDITLISTVIEW = WM_USER + 666;

type
  TForm1 = class(TForm)
    ListView1: TListView;
    procedure FormCreate(Sender: TObject);
    procedure ListView1Click(Sender: TObject);
  private
    ListViewEditor: TEdit;
    LItem: TListitem;
    procedure UserEditListView( Var Message: TMessage ); message USER_EDITLISTVIEW;
    procedure ListViewEditorExit(Sender: TObject);
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

uses
  CommCtrl;
const
  EDIT_COLUMN = 2; //Index of the column to Edit

procedure TForm1.FormCreate(Sender: TObject);
Var
  I : Integer;
  Item : TListItem;
begin
  for I := 0 to 9 do
  begin
   Item:=ListView1.Items.Add;
   Item.Caption:=Format('%d.%d',[i,1]);
   Item.SubItems.Add(Format('%d.%d',[i,2]));
   Item.SubItems.Add(Format('%d.%d',[i,3]));
  end;

  //create the TEdit and assign the OnExit event
  ListViewEditor:=TEdit.Create(Self);
  ListViewEditor.Parent:=ListView1;
  ListViewEditor.OnExit:=ListViewEditorExit;
  ListViewEditor.Visible:=False;
end;

procedure TForm1.ListView1Click(Sender: TObject);
var
  LPoint: TPoint;
  LVHitTestInfo: TLVHitTestInfo;
begin
  LPoint:= listview1.ScreenToClient(Mouse.CursorPos);
  ZeroMemory( @LVHitTestInfo, SizeOf(LVHitTestInfo));
  LVHitTestInfo.pt := LPoint;
  //Check if the click was made in the column to edit
  If (ListView1.perform( LVM_SUBITEMHITTEST, 0, LPARAM(@LVHitTestInfo))<>-1) and ( LVHitTestInfo.iSubItem = EDIT_COLUMN ) Then
    PostMessage( self.Handle, USER_EDITLISTVIEW, LVHitTestInfo.iItem, 0 )
  else
    ListViewEditor.Visible:=False; //hide the TEdit 
end;

procedure TForm1.ListViewEditorExit(Sender: TObject);
begin
  If Assigned(LItem) Then
  Begin
    //assign the vslue of the TEdit to the Subitem
    LItem.SubItems[ EDIT_COLUMN-1 ] := ListViewEditor.Text;
    LItem := nil;
  End;
end;

procedure TForm1.UserEditListView(var Message: TMessage);
var
  LRect: TRect;
begin
  LRect.Top := EDIT_COLUMN;
  LRect.Left:= LVIR_BOUNDS;
  listview1.Perform( LVM_GETSUBITEMRECT, Message.wparam,  LPARAM(@LRect) );
  MapWindowPoints( listview1.Handle, ListViewEditor.Parent.Handle, LRect, 2 );
  //get the current Item to edit
  LItem := listview1.Items[ Message.wparam ];
  //set the text of the Edit 
  ListViewEditor.Text := LItem.Subitems[ EDIT_COLUMN-1];
  //set the bounds of the TEdit
  ListViewEditor.BoundsRect := LRect; 
  //Show the TEdit
  ListViewEditor.Visible:=True;
end;
unit EditableListView;

interface

uses
  Messages,
  Classes, StdCtrls, ComCtrls, System.Types,
  Generics.Collections;

Const
  ELV_EDIT = WM_USER + 16;

type
  TEditableListView = class(TListView)
  private
    FEditable: TList<integer>;

    FEditor: TEdit;
    FItem: TListItem;
    FEditColumn: integer;

    procedure EditListView(var AMessage: TMessage); message ELV_EDIT;

    procedure EditExit(Sender: TObject);
    procedure EditKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);

    procedure DoEdit;

    procedure CleanupEditable;
    function GetEditable(const I: integer): boolean;
    procedure SetEditable(const I: integer; const Value: boolean);
  protected
    procedure Click; override;
    function DoMouseWheel(Shift: TShiftState; WheelDelta: Integer; MousePos: TPoint): Boolean; override;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;

    property Editable[const I: integer]: boolean read GetEditable write SetEditable;
  end;

implementation

uses
  Windows, SysUtils, CommCtrl, Controls;

{ TEditableListView }

constructor TEditableListView.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);

  FEditable := TList<integer>.Create;

  FEditor := TEdit.Create(self);
  FEditor.Parent := self;
  FEditor.OnExit := EditExit;
  FEditor.OnKeyDown := EditKeyDown;
  FEditor.Visible := false;

  ViewStyle := vsReport;   // Default to vsReport instead of vsIcon
end;

destructor TEditableListView.Destroy;
begin
  FEditable.Free;

  inherited Destroy;
end;

procedure TEditableListView.DoEdit;
begin
  if Assigned(FItem) Then
  begin
    // assign the value of the TEdit to the Subitem
    if FEditColumn = 0 then
      FItem.Caption := FEditor.Text
    else if FEditColumn > 0 then
      FItem.SubItems[FEditColumn - 1] := FEditor.Text;
  end;
end;

function TEditableListView.DoMouseWheel(Shift: TShiftState; WheelDelta: Integer; MousePos: TPoint): Boolean;
begin
  DoEdit;
  FEditor.Visible := false;
  SetFocus;

  Result := inherited DoMouseWheel(Shift, WheelDelta, MousePos);
end;

procedure TEditableListView.CleanupEditable;
var
  I: integer;
begin
  for I := FEditable.Count - 1 downto 0 do
  begin
    if not Assigned(Columns.FindItemID(FEditable[I])) then
      FEditable.Delete(I);
  end;
end;

procedure TEditableListView.Click;
var
  LPoint: TPoint;
  LVHitTestInfo: TLVHitTestInfo;
begin
  LPoint := ScreenToClient(Mouse.CursorPos);
  FillChar(LVHitTestInfo, SizeOf(LVHitTestInfo), 0);
  LVHitTestInfo.pt := LPoint;
  // Check if the click was made in the column to edit
  if (perform(LVM_SUBITEMHITTEST, 0, LPARAM(@LVHitTestInfo)) <> -1) Then
    PostMessage(self.Handle, ELV_EDIT, LVHitTestInfo.iItem, LVHitTestInfo.iSubItem)
  else
    FEditor.Visible := false; //hide the TEdit

  inherited Click;
end;

procedure TEditableListView.EditExit(Sender: TObject);
begin
  DoEdit;
end;

procedure TEditableListView.EditKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
var
  lNextRow, lNextCol: integer;
begin
  if Key in [VK_RETURN, VK_TAB, VK_LEFT, VK_RIGHT, VK_UP, VK_DOWN] then
  begin
    DoEdit;

    lNextRow := FItem.Index;
    lNextCol := FEditColumn;
    case Key of
      VK_RETURN,
      VK_DOWN:
        lNextRow := lNextRow + 1;
      VK_UP:
        lNextRow := lNextRow - 1;
      VK_TAB,
      VK_RIGHT:
        lNextCol := lNextCol + 1;
      VK_LEFT:
        lNextCol := lNextCol - 1;
    end;

    if not ( (Key = VK_RIGHT) and (FEditor.SelStart+FEditor.SelLength < Length(FEditor.Text)) )
   and not ( (Key = VK_LEFT) and (FEditor.SelStart+FEditor.SelLength > 0) ) then
    begin
      Key := 0;

      if (lNextRow >= 0) and (lNextRow < Items.Count)
     and (lNextCol >= 0) and (lNextCol < Columns.Count) then
        PostMessage(self.Handle, ELV_EDIT, lNextRow, lNextCol);
    end;
  end;
end;

procedure TEditableListView.EditListView(var AMessage: TMessage);
var
  LRect: TRect;
begin
  if Editable[AMessage.LParam] then
  begin
    LRect.Top := AMessage.LParam;
    LRect.Left:= LVIR_BOUNDS;
    Perform(LVM_GETSUBITEMRECT, AMessage.wparam, LPARAM(@LRect));
    //get the current Item to edit
    FItem := Items[AMessage.wparam];
    FEditColumn := AMessage.LParam;
    //set the text of the Edit
    if FEditColumn = 0 then
      FEditor.Text := FItem.Caption
    else if FEditColumn > 0 then
      FEditor.Text := FItem.Subitems[FEditColumn-1]
    else
      FEditor.Text := '';
    //set the bounds of the TEdit
    FEditor.BoundsRect := LRect;
    //Show the TEdit
    FEditor.Visible := true;
    FEditor.SetFocus;
    FEditor.SelectAll;
  end
  else
    FEditor.Visible := false;
end;

function TEditableListView.GetEditable(const I: integer): boolean;
begin
  if (I > 0) and (I < Columns.Count) then
    Result := FEditable.IndexOf(Columns[I].ID) >= 0
  else
    Result := false;
  CleanupEditable;
end;

procedure TEditableListView.SetEditable(const I: integer; const Value: boolean);
var
  Lix: integer;
begin
  if (I > 0) and (I < Columns.Count) then
  begin
    Lix := FEditable.IndexOf(Columns[I].ID);
    if Value and (Lix < 0)then
      FEditable.Add(Columns[I].ID)
    else if not Value and (Lix >= 0) then
      FEditable.Delete(Lix);
  end;
  CleanupEditable;
end;

end.
unit UnitEditableListView;

interface

uses
  Winapi.Windows,
  Winapi.Messages,
  Winapi.CommCtrl,
  System.Classes,
  Vcl.ComCtrls,
  Vcl.StdCtrls;

type
  ///
  /// Based on: https://stackoverflow.com/a/10836109
  ///
  TListView = class(Vcl.ComCtrls.TListView)
  strict private
    FListViewEditor: TEdit;
    FEditorItemIndex, FEditorSubItemIndex: Integer;
    FCursorPos: TPoint;

    // Create native item
    function CreateItem(Index: Integer; ListItem: TListItem): TLVItem;
    // Free TEdit
    procedure FreeEditorItemInstance;
    // Invalidate cursor position
    procedure ResetCursorPos;

    {
      TEdit Events
    }
    procedure ListViewEditorExit(Sender: TObject);
    procedure ListViewEditorKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
    procedure ListViewEditorKeyPress(Sender: TObject; var Key: Char);
    {
      Override Events
    }
    procedure Click; override;
    procedure KeyDown(var Key: Word; Shift: TShiftState); override;

    {
      Windows Events
    }
    { TODO -cenhancement : Scroll edit control with listview }
    procedure WMMouseWheel(var Message: TWMMouseWheel); message WM_MOUSEWHEEL;
    procedure WMHScroll(var Message: TWMHScroll); message WM_HSCROLL;
    procedure WMVScroll(var Message: TWMVScroll); message WM_VSCROLL;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    ///
    /// Start edition on local position
    ///
    procedure EditCaptionAt(Point: TPoint);
  end;

implementation

uses
  Vcl.Controls;

{ TListView }

procedure TListView.Click;
begin
  inherited;
  // Get current point
  FCursorPos := ScreenToClient(Mouse.CursorPos);
  FreeEditorItemInstance;
end;

constructor TListView.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  // Create the TEdit and assign the OnExit event
  FListViewEditor := TEdit.Create(AOwner);
  with FListViewEditor do
  begin
    Parent := Self;
    OnKeyDown := ListViewEditorKeyDown;
    OnKeyPress := ListViewEditorKeyPress;
    OnExit := ListViewEditorExit;
    Visible := False;
  end;

end;

destructor TListView.Destroy;
begin
  // Free TEdit
  FListViewEditor.Free;
  inherited;
end;

procedure TListView.EditCaptionAt(Point: TPoint);
var
  Rect: TRect;
  CursorPos: TPoint;
  HitTestInfo: TLVHitTestInfo;
  CurrentItem: TListItem;
begin
  // Set position to handle
  HitTestInfo.pt := Point;

  // Get item select
  if ListView_SubItemHitTest(Handle, @HitTestInfo) = -1 then
    Exit;

  with HitTestInfo do
  begin
    FEditorItemIndex := iItem;
    FEditorSubItemIndex := iSubItem;
  end;

  // Nothing?
  if (FEditorItemIndex < 0) or (FEditorItemIndex >= Items.Count) then
    Exit;

  if FEditorSubItemIndex < 0 then
    Exit;

  CurrentItem := Items[ItemIndex];

  if not CanEdit(CurrentItem) then
    Exit;

  // Get bounds
  ListView_GetSubItemRect(Handle, FEditorItemIndex, FEditorSubItemIndex, LVIR_LABEL, @Rect);

  // set the text of the Edit
  if FEditorSubItemIndex = 0 then
    FListViewEditor.Text := CurrentItem.Caption
  else
  begin
    FListViewEditor.Text := CurrentItem.SubItems[FEditorSubItemIndex - 1];
  end;
  // Set the bounds of the TEdit
  FListViewEditor.BoundsRect := Rect;
  // Show the TEdit
  FListViewEditor.Visible := True;
  // Set focus
  FListViewEditor.SetFocus;
end;

procedure TListView.ResetCursorPos;
begin
  // Free cursos pos
  FCursorPos := Point(-1, -1);
end;

procedure TListView.FreeEditorItemInstance;
begin
  FEditorItemIndex := -1;
  FEditorSubItemIndex := -1;
  FListViewEditor.Visible := False; // Hide the TEdit
end;

procedure TListView.KeyDown(var Key: Word; Shift: TShiftState);
begin
  inherited KeyDown(Key, Shift);

  // F2 key start edit
  if (Key = VK_F2) then
    EditCaptionAt(FCursorPos);
end;

///
/// Create a LVItem
///
function TListView.CreateItem(Index: Integer; ListItem: TListItem): TLVItem;
begin
  with Result do
  begin
    mask := LVIF_PARAM or LVIF_IMAGE or LVIF_GROUPID;
    iItem := index;
    iSubItem := 0;
    iImage := I_IMAGECALLBACK;
    iGroupId := -1;
    pszText := PChar(ListItem.Caption);
{$IFDEF CLR}
    lParam := ListItem.GetHashCode;
{$ELSE}
    lParam := Winapi.Windows.lParam(ListItem);
{$ENDIF}
  end;
end;

procedure TListView.ListViewEditorExit(Sender: TObject);
begin
  // I have an instance?
  if FEditorItemIndex = -1 then
    Exit;

  // Assign the value of the TEdit to the Subitem
  if FEditorSubItemIndex = 0 then
    Items[FEditorItemIndex].Caption := FListViewEditor.Text
  else
    Items[FEditorItemIndex].SubItems[FEditorSubItemIndex - 1] := FListViewEditor.Text;

  // Raise OnEdited event
  Edit(CreateItem(FEditorItemIndex, Items[FEditorItemIndex]));

  // Free instanse
  FreeEditorItemInstance;
end;

procedure TListView.ListViewEditorKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
begin
  // ESCAPE key exit of editor
  if Key = VK_ESCAPE then
    FreeEditorItemInstance;
end;

procedure TListView.ListViewEditorKeyPress(Sender: TObject; var Key: Char);
begin
  // Update item on press ENTER
  if (Key = #$0A) or (Key = #$0D) then
    FListViewEditor.OnExit(Sender);
end;

procedure TListView.WMHScroll(var Message: TWMHScroll);
begin
  inherited;
  // Reset cursos pos
  ResetCursorPos;
  // Free instanse
  FreeEditorItemInstance;
end;

procedure TListView.WMMouseWheel(var Message: TWMMouseWheel);
begin
  inherited;
  // Reset cursos pos
  ResetCursorPos;
  // Free instanse
  FreeEditorItemInstance;
end;

procedure TListView.WMVScroll(var Message: TWMVScroll);
begin
  inherited;
  // Reset cursos pos
  ResetCursorPos;
  // Free instanse
  FreeEditorItemInstance;
end;

end.