delphilistvieweditedit-in-placesubitem

In-place editing of a subitem in a TListView


I have a ListView with 3 columns and would like to edit the third column, aka Subitem[1]. If I set ListView.ReadOnly to False, it allows me to edit the caption of the selected item. Is there an easy way to do the same thing for the subitem? I would like to stay away from adding a borderless control on top that does the editing.


Solution

  • You can Edit a subitem of the listview (in report mode) using a TEdit, a custom message and handling the OnClick event of the ListView.

    Try this sample

    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;