listviewdelphicopyclipboardsubitem

Copy Sub Item Of List View Delphi Into Clipboard


I have a List View with eight columns and a popup menu. [ScreenShot]image

I have this code which is work great to copy into clipboard all items and sub items when a row or all rows selected.

procedure TForm1.CopyURL1Click(Sender: TObject);
var 
  lv : TbsSkinListView;
  s : string;
  c,f,k : integer;

begin
  lv := bsSkinListView1;
  Clipboard.Clear;
  s := '';

  for f := 0 to lv.Items.Count - 1 do
  begin     
     k := 0;
     if lv.Items[f].Selected then
     begin
       s := s + Format('%s : %s, ',[lv.Columns[k].Caption, lv.Items[f].Caption]);
       for c := 0 to lv.Items[f].SubItems.Count - 1 do
       begin
         Inc(k);
         s := s + Format('%s : %s, ',[lv.Columns[k].Caption, lv.Items[f].SubItems[c]]);
       end;
      SetLength(s, Length(s) - 2);
      s := s + #$D#$A;
    end;      
  end;
  clipboard.AsText := s;
end;

What i need is I'd like to copy only the caption of column one (column "Title") with it's sub items [0], and copy the column 8 caption (column "URL") with it's sub items [7] into clipboard, when a row or all rows selected.

Also sometime sub items[7] is empty, and it shouldn't get index out of bounds (7) error message.

From my screenshot above, when i copy the 1st row, the result should return like this

Title : 10 Things You Didn't Know...  URL :   <=== this is an empty sub items [7]

when 2nd row copied:

Title : 10 Things You Didn't Know...  URL : http://www.example.com

All rows selected :

Title : 10 Things You Didn't Know...  URL : 
Title : 10 Things You Didn't Know...  URL : http://www.example.com

i have try this link but it doesn't meet or not work as i need. I'm using Delphi XE 4. How do i achieve this? Any help would be highly appreciated.


Solution

  • Your clipboard text contains all of the column values because you are looping through all of the TListItem.SubItems values. If you don't want all of the values, simply don't loop anymore. Just access the specific items you want.

    Try something more like this:

    procedure TForm1.CopyURL1Click(Sender: TObject);
    var 
      s : string;
      item: TListItem;
    begin
      s := '';
      item := bsSkinListView1.GetNextItem(nil, sdAll, [isSelected]);
      while item <> nil do
      begin     
        s := s + Format('Title : %s, ', [item.Caption]);
        if item.SubItems.Count > 7 then
          s := s + Format('URL : %s, ', [item.SubItems[7]]);
        s[Length(s)-1] := #$D;
        s[Length(s)]   := #$A;
        item := bsSkinListView1.GetNextItem(item, sdAll, [isSelected]);
      end;
      Clipboard.Open;
      try
        Clipboard.Clear;
        Clipboard.AsText := s;
      finally
        Clipboard.Close;
      end;
    end;
    

    With that said, an alternative option would be to use the TListView in virtual mode instead (OwnerData=True), where you store all of your data in a separate list. Then you can access your data list directly without having to touch the TListView at all. For example:

    type
      PMyData = ^TMyData;
      TMyData = record
        Selected: Boolean;
        Title: String;
        ...
        URL: string;
      end;
    
      TForm1 = class(TForm)
        ...
      private
        MyData: TList;
        ...
      end;
    
    ...
    
    procedure TForm1.FormCreate(Sender: TObject);
    begin
      MyData := TList.Create;
    end;
    
    procedure TForm1.FormDestroy(Sender: TObject);
    var
      I: Integer;
    begin
      for I := 0 to MyData.Count-1 do
        Dispose(PMyData(MyData[I]));
      MyData.Free;
    end;
    
    procedure TForm1.bsSkinListView1Data(Sender: TObject; Item: TListItem);
    var
      Data: PMyData;
    begin
      Data := PMyData(MyData[Item.Index]);
      Item.Caption := Data.Title;
      Item.SubItems.Add(...);
      ...
      Item.SubItems.Add(Data.URL);
      Item.Data := Data;
    end;
    
    procedure TForm1.TForm1.bsSkinListView1SelectItem(Sender: TObject; Item: TListItem; Selected: Boolean);
    var
      I: Integer;
    begin
      if Item <> nil then
        Item.Selected := Selected
      else
      begin
        for I := 0 to MyData.Count-1 do
          PMyData(MyData[I]).Selected := Selected;
      end;
    end;
    
    procedure TForm1.TForm1.bsSkinListView1DataStateChange(Sender: TObject; StartIndex, EndIndex: Integer; OldState, NewState: TItemStates);
    var
      I: Integer;
    begin
      if (OldState * [isSelected]) <> (NewState * [isSelected]) then
      begin
        for I := StartIndex to EndIndex do
          PMyData(MyData[I]).Selected := (isSelected in NewState);
      end;
    end;
    
    procedure TForm1.AddItem1Click(Sender: TObject);
    var
      Data: PMyData;
    begin
      New(Data);
      try
        Data.Selected := False;
        Data.Title := ...;
        ...
        Data.URL := ...;
        MyData.Add(Data);
      except
        Dispose(Data);
        raise;
      end;
      bsSkinListView1.Items.Count := MyData.Count;
    end;
    
    procedure TForm1.CopyURL1Click(Sender: TObject);
    var 
      s : string;
      Data: PMyData;
      I: Integer;
    begin
      s := '';
      for I := 0 to MyData.Count-1 do
      begin
        Data := PMyData(MyData[I]);
        if Data.Selected then
        begin     
          s := s + Format('Title : %s, ', [Data.Title]);
          if item.URL <> '' then
            s := s + Format('URL : %s, ', [Data.URL]);
          s[Length(s)-1] := #$D;
          s[Length(s)]   := #$A;
        end;
      end;
      Clipboard.Open;
      try
        Clipboard.Clear;
        Clipboard.AsText := s;
      finally
        Clipboard.Close;
      end;
    end;