如何找到未选中的下一个ListView项?

问题描述 投票:1回答:1

我想在ListView中搜索下一个未选择的项目,但只能使用Windows API。

我尝试使用ListView_FindItem宏,但它不起作用。结果始终为-1:

function TNewListView.NextUnselected(I: Integer): Integer;
var FindInfo: TLVFindInfo;
    ItemInfo: TLVItem;
begin
 if not HandleAllocated then Exit(-1)
 else begin
   FillChar(ItemInfo, SizeOf(ItemInfo), 0);
   ItemInfo.mask:= LVIF_STATE;
   ItemInfo.state:= 0;
   ItemInfo.stateMask:= LVIS_SELECTED;

   FillChar(FindInfo, SizeOf(FindInfo), 0);
   FindInfo.flags:= LVFI_PARAM;
   FindInfo.lParam:= LPARAM(@ItemInfo);
   Result:= ListView_FindItem(Handle, I, FindInfo);
 end;
listview delphi delphi-2009
1个回答
4
投票

你使用ListView_FindItem()标志调用LVFI_PARAM

Lvfi_prm

搜索此结构的lParam成员与项目的lParam结构的LVITEM成员之间的匹配。

这告诉ListView将指定的TLVFindInfo.lParam值与每个列表项的lParam进行比较,直到找到匹配项。

如果您在非虚拟模式(TListView)中使用OwnerData=False,列表项的lParam值将保存其对应的TListItem对象指针。

如果您在虚拟模式(TListView)中使用OwnerData=True,则列表项的lParam值始终为0。

ListView_FindItem()(以及潜在的LVM_FINDITEM消息)可以通过其Caption(全部或部分),lParam 1或其位置搜索列表项,但不能搜索任何其他内容。

1:例如,TListItems.IndexOf()方法使用ListView_FindItem()使用TListItem搜索返回指定lParam对象的索引(仅在非虚拟模式下工作,其中每个项目的lParamTListItem对象指针)。

您也尝试执行lParam搜索,但您使用的是错误的lParam值来搜索!您将TLVFindInfo.lParam值设置为指向本地TLVItem变量的指针,因此LVFI_PARAM比较将永远不会找到匹配的列表项。这就是为什么你总是得到-1的结果。

ListView_FindItem()在您的示例中基本上执行以下逻辑:

function ListView_FindItem(hWnd: HWND; iStart: Integer; const plvfi: TLVFindInfo): Integer;
var
  lvi: TLVItem;
begin
  for Result := iStart+1 to ListView_GetItemCount(hWnd)-1 do
  begin
    FillChar(lvi, SizeOf(lvi), 0);
    lvi.iIndex := Result;
    lvi.mask = LVIF_PARAM;
    ListView_GetItem(hWnd, lvi);
    if lvi.lParam = plvfi.lParam then // <-- NEVER FINDS A MATCH!
      Exit;
  end;
  Result := -1;
end;

如您所见,本地TLVItem变量的内容根本不会被使用,因此将TLVItem字段设置为什么并不重要。

您期望ListView_FindItem()基本上执行以下逻辑,这不是它的工作方式,并且没有文档以这种方式工作:

function ListView_FindItem(hWnd: HWND; iStart: Integer; const plvfi: TLVFindInfo): Integer;
var
  lvi: TLVItem;
begin
  for Result := iStart+1 to ListView_GetItemCount(hWnd)-1 do
  begin
    FillChar(lvi, SizeOf(lvi), 0);
    lvi.iIndex := Result;
    lvi.mask = LVIF_STATE;
    lvi.stateMask := PLVItem(plvfi.lParam)^.stateMask;
    ListView_GetItem(hWnd, lvi);
    if lvi.state = PLVItem(plvfi.lParam)^.state then // <-- BUZZ, WRONG!
      Exit;
  end;
  Result := -1;
end;

因此,您根本无法使用ListView_FindItem() / LVM_FINDITEM按州搜索项目,他们不支持这种搜索。

您可能想尝试使用ListView_GetNextItem() / LVM_GETNEXTITEM

搜索具有指定属性并与指定项具有指定关系的列表视图项。

但是,它们只能用于搜索已启用指定特征的列表项(例如启用了LVNI_SELECTED)。它们不能用于查找具有指定特征的ABSENCE的项目(例如禁用LVNI_SELECTED)。

因此,要做你想做的事,你只需要手动迭代列表项,使用ListView_GetItem()ListView_GetItemState()来检索每个项目的当前状态,直到找到你要找的东西。

例如:

function TNewListView.NextUnselected(StartIndex: Integer): Integer;
begin
  if HandleAllocated then
  begin
    for Result := StartIndex+1 to ListView_GetItemCount(Handle)-1 do
    begin
      if (ListView_GetItemState(Handle, Result, LVIS_SELECTED) and LVIS_SELECTED) = 0 then
        Exit;
    end;

    // if you want to implement wrap-around searching, uncomment this...
    {
    for Result := 0 to StartIndex-1 do
    begin
      if (ListView_GetItemState(Handle, Result, LVIS_SELECTED) and LVIS_SELECTED) = 0 then
        Exit;
    end;
    }
  end;
  Result := -1;
end;

要么:

function TNewListView.NextUnselected(StartIndex: Integer): Integer;

  function IsNotSelected(Index: Integer): Boolean;
  var
    ItemInfo: TLVItem;
  begin
    FillChar(ItemInfo, SizeOf(ItemInfo), 0);
    ItemInfo.iItem := Index;
    ItemInfo.mask := LVIF_STATE;
    ItemInfo.stateMask := LVIS_SELECTED;
    ListView_GetItem(Handle, ItemInfo);
    Result := (ItemInfo.state and LVIS_SELECTED) = 0;
  end;

begin
  if HandleAllocated then
  begin
    for Result := StartIndex+1 to ListView_GetItemCount(Handle)-1 do
    begin
      if IsNotSelected(Result) then
        Exit;
    end;

    // if you want to implement wrap-around searching, uncomment this...
    {
    for Result := 0 to StartIndex-1 do
    begin
      if IsNotSelected(Result) then
        Exit;
    end;
    }
  end;
  Result := -1;
end;

这两种方法都适合您的尝试。

© www.soinside.com 2019 - 2024. All rights reserved.