Question

I try to do an action for each selected listitem but it won't work. This is what i tried:

var
TLsttem:TListItem;
begin
for TLsttem in form1.listview1.Selected do
begin
...
end;
end;

Now i get this error:

[dcc32 Error] MSGBox.pas(50): E2431 for-in statement cannot operate on collection type 'TListItem' because 'TListItem' does not contain a member for 'GetEnumerator', or it is inaccessible

How can i fix this?

EDIT: I now tried this script:

TLsttem := form1.ListView1.Selected;
    while TLsttem <> nil do
    begin
    showmessage('Test');
    TLsttem := form1.ListView1.GetNextItem(TLsttem, sdAll, [isSelected]);
  end;

But i only get 1 message, how can i fix this?

Was it helpful?

Solution

This example is from documentation, GetNextItem:

procedure TForm1.Button1Click(Sender: TObject);
var
  Item: TListItem;
begin
  Item := ListView1.Selected;
  while Item <> nil do
  begin
    ListBox1.Items.Add(Item.Caption);
    Item := ListView1.GetNextItem(Item, sdAll, [isSelected]);
  end;
end; 

TListView.Selected gets the first selected item, while GetNextItem unwinds more selected items.

enter image description here


As noted by David, it is possible to wrap this logic into an enumerator, so that a for .. in loop could be used. The easiest thing is to put this enumeration into a class helper, much like the answer from @StefanGlienke.

type
  TSelectedListItemsEnumerator = record
  private
    FListView: TListView;
    FItem: TListItem;
  public
    constructor Create(aListView: TListView);
    function GetEnumerator: TSelectedListItemsEnumerator;
    function MoveNext: Boolean;
    property Current: TListItem read FItem;
  end;

  TListViewHelper = class helper for TListView
  private
    function GetSelectedItems: TSelectedListItemsEnumerator;
  public
    property SelectedItems: TSelectedListItemsEnumerator
      read GetSelectedItems;
  end;

{ TSelectedListItemsEnumerator }

constructor TSelectedListItemsEnumerator.Create(aListView: TListView);
begin
  FListView := AListView;
end;

function TSelectedListItemsEnumerator.GetEnumerator: TSelectedListItemsEnumerator;
begin
  FItem := nil;
  Result := Self;
end;

function TSelectedListItemsEnumerator.MoveNext: Boolean;
begin
  FItem := FListView.GetNextItem(FItem,sdAll,[isSelected]);
  Result := (FItem <> nil);
end;

{ TListViewHelper }

function TListViewHelper.GetSelectedItems: TSelectedListItemsEnumerator;
begin
  Result := TSelectedListItemsEnumerator.Create(Self);
end;

procedure TForm1.Button2Click(Sender: TObject);
var
  Item: TListItem;
begin
  for Item in ListView1.SelectedItems do
  begin
    Memo1.Lines.Add(Item.Caption);
  end;
end;

OTHER TIPS

While for this example it might be overkill here is a way to extend the TListView class with a SelectedItem property that is enumerable with a for-in loop.

In cases where the condition inside the loop might be more complex than just checking a property or by actually providing a filter delegate this can be very powerful.

type
  TSelectedListItemsEnumerator = class(TListItemsEnumerator)
  public
    function MoveNext: Boolean;
  end;

  TSelectedListItemsEnumerable = record
  private
    FListItems: TListItems;
  public
    constructor Create(AListItems: TListItems);
    function GetEnumerator: TSelectedListItemsEnumerator;
  end;

  TListViewHelper = class helper for TListView
  private
    function GetSelectedItems: TSelectedListItemsEnumerable;
  public
    property SelectedItems: TSelectedListItemsEnumerable
      read GetSelectedItems;
  end;

{ TSelectedListItemsEnumerator }

function TSelectedListItemsEnumerator.MoveNext: Boolean;
begin
  repeat
    Result := inherited;
  until not Result or Current.Selected;
end;

{ TSelectedListItemsEnumerable }

constructor TSelectedListItemsEnumerable.Create(AListItems: TListItems);
begin
  FListItems := AListItems;
end;

function TSelectedListItemsEnumerable.GetEnumerator: TSelectedListItemsEnumerator;
begin
  Result := TSelectedListItemsEnumerator.Create(fListItems);
end;

{ TListViewHelper }

function TListViewHelper.GetSelectedItems: TSelectedListItemsEnumerable;
begin
  Result := TSelectedListItemsEnumerable.Create(Items);
end;

You can then use it like this:

procedure TForm1.Button1Click(Sender: TObject);
var
  item: TListItem;
begin
  for item in ListView1.SelectedItems do
  begin
    ShowMessage(item.Caption);
  end;
end;

This works perfectly fine for me in XE5 (VCL):

var
  Item: TListItem;
begin
  for Item in ListView1.Items do
  begin
    if Item.Selected then
    begin
      // Do something with the item
    end;
  end;
end;
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top