我想在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;
答案 0 :(得分:4)
您正在使用LVFI_PARAM
标志呼叫ListView_FindItem()
:
LVFI_PARAM
搜索此结构的
lParam
成员与项目的lParam
结构的LVITEM
成员之间的匹配项。
这告诉ListView将指定的TLVFindInfo.lParam
值原样与每个列表项的lParam
进行比较,直到找到匹配项。
如果您以非虚拟模式(TListView
)使用OwnerData=False
,则列表项的lParam
值将保留其对应的TListItem
对象指针。
如果您在{strong>虚拟模式(TListView
)下使用OwnerData=True
,则列表项的lParam
值始终为0。
ListView_FindItem()
(和基础LVM_FINDITEM
消息)可以通过其Caption
(全部或部分),其lParam
1 < / sup>或它的位置,但没有其他。
1:例如,TListItems.IndexOf()
方法使用ListView_FindItem()
通过TListItem
搜索返回lParam
对象的索引(仅适用于非虚拟模式,其中每个项目的lParam
是TListItem
对象指针)。
您也尝试执行lParam
搜索,但是您正在使用 WRONG 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
变量的 contents 从未使用过,因此将TLVItem
字段设置为什么都没有关系。
您期望 ListView_FindItem()
本质上执行以下逻辑,这不是它的工作方式,也不是该文件的工作方式:< / p>
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;
这两种方法都可以满足您的尝试。