如何找到下一个未选中的 ListView 项?
How to find the next ListView item that is not selected?
我想在 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;
您正在使用 LVFI_PARAM
标志调用 ListView_FindItem()
:
LVFI_PARAM
Searches for a match between this structure's lParam
member and the lParam
member of an item's LVITEM
structure.
告诉 ListView 将指定的 TLVFindInfo.lParam
值 按原样 与每个列表项的 lParam
进行比较,直到找到匹配项。
如果您在 非虚拟 模式 (OwnerData=False
) 中使用 TListView
,列表项的 lParam
值将保留其对应的值TListItem
对象指针。
如果您在 虚拟 模式 (OwnerData=True
) 中使用 TListView
,则列表项的 lParam
值始终为 0。
ListView_FindItem()
(以及底层的 LVM_FINDITEM
消息)可以通过 Caption
(全部或部分)、lParam
[=104] 来搜索列表项=]1,或它的位置,但 没有别的。
1:例如TListItems.IndexOf()
方法使用ListView_FindItem()
到return指定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
变量的 contents 根本未被使用,因此您设置 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
:
Searches for a list-view item that has the specified properties and bears the specified relationship to a specified item.
但是,它们只能用于搜索启用了指定特征的列表项(例如启用了 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;
这两种方法都适用于您的尝试。
我想在 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;
您正在使用 LVFI_PARAM
标志调用 ListView_FindItem()
:
LVFI_PARAM
Searches for a match between this structure's
lParam
member and thelParam
member of an item'sLVITEM
structure.
告诉 ListView 将指定的 TLVFindInfo.lParam
值 按原样 与每个列表项的 lParam
进行比较,直到找到匹配项。
如果您在 非虚拟 模式 (OwnerData=False
) 中使用 TListView
,列表项的 lParam
值将保留其对应的值TListItem
对象指针。
如果您在 虚拟 模式 (OwnerData=True
) 中使用 TListView
,则列表项的 lParam
值始终为 0。
ListView_FindItem()
(以及底层的 LVM_FINDITEM
消息)可以通过 Caption
(全部或部分)、lParam
[=104] 来搜索列表项=]1,或它的位置,但 没有别的。
1:例如TListItems.IndexOf()
方法使用ListView_FindItem()
到return指定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
变量的 contents 根本未被使用,因此您设置 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
:
Searches for a list-view item that has the specified properties and bears the specified relationship to a specified item.
但是,它们只能用于搜索启用了指定特征的列表项(例如启用了 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;
这两种方法都适用于您的尝试。