我想在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_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
对象的索引(仅在非虚拟模式下工作,其中每个项目的lParam
是TListItem
对象指针)。
您也尝试执行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;
这两种方法都适合您的尝试。