Вы звоните ListView_FindItem()
, используя флаг LVFI_PARAM
:
LVFI_PARAM
Поиск соответствия между элементом lParam
этой структуры и элементом lParam
структуры LVITEM
элемента.
Это говорит ListView сравнивать указанное TLVFindInfo.lParam
значение как есть с lParam
каждого элемента списка, пока не найдет совпадение.
Если вы используете TListView
в не виртуальном режиме (OwnerData=False
), значение lParam
элемента списка содержит соответствующий TListItem
указатель объекта.
Если вы используете TListView
в режиме virtual (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
). Их нельзя использовать для поиска предмета, у которого ОТСУТСТВИЕ указанных характеристик (например, отключено 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;
Оба подхода работают на то, что вы пытаетесь.