Pominięcie zaznaczania nagłówka grupy listy TListView

1

Witam w Nowym Roku 😀
Rozruszajmy forum Delphi... Mam taki, dziwny i nietypowy problem.

Moja aplikacja wyświetla na liście (TListView) elementy w grupach, po których użytkownik może przechodzić (wybrać dowolny z nich) przy pomocy klawiatury (klawisz Enter włacza aplikację, klawisz Spacja uruchamia inną akcję, klawisze strzałek zaznaczają kolejne elementy...).

Oto przykładowa lista:

var
   Group : TListGroup;
   
   LV_Apps.GroupView := True;
   LV_Apps.Groups.Clear;

   // Preffered Applications
   Group := LV_Apps.Groups.Add;
   Group.State := [lgsNormal];
   Group.Header := 'Preffered applications';
   Group.Subtitle := 'This section shows all applications that support given extension';
   Group.GroupID := 0;

   // Other Applications
   Group := LV_Apps.Groups.Add;
   Group.State := [lgsNormal];
   Group.Header := 'Other Applications';
   Group.Subtitle := 'This section shows other applications';
   Group.GroupID := 1;

Mam zatem listę z 2 grupami, każda z nich zawiera jakieś elementy (tutaj aplikacje do uruchomienia).

Pytanie za 100 punktów - jak wyłączyć zaznaczanie nagłówka grupy (nagłówek grupy chcę wyświetlać)?

Jak zrobić, żeby używając klawiatury (klawisze VK_UP, VK_DOWN) móc zaznaczać
tylko elementy - a nie grupy (patrz obrazek, napis w czerwonej ramce) - po prostu pomijać nagłówek grupy przy przechodzeniu po elementach listy.

screenshot-20240124143414.png

Proszę o pomoc.
Dziękuję!
-Pawel

3

Nie wiem czy da się inaczej ale rozwiązanie za pomocą subclasingu działa.

type
  TListView = class(Vcl.ComCtrls.TListView)
  protected
     procedure WMKeyDown(var Msg: TWMKeyDown); message WM_KEYDOWN;
  end;

//reszta kodu

procedure TListView.WMKeyDown(var Msg: TWMKeyDown);
var
  index: Integer;
begin
  if ((Msg.CharCode in [VK_UP, VK_DOWN]) and (Self.Items.Count > 0)) then
  begin
     index:= Self.ItemIndex;
     if (index = -1) then
        Self.ItemIndex:= 0
     else
     begin
       if Msg.CharCode = VK_DOWN then
       begin
          if (index <  Self.Items.Count - 1) then
            Self.ItemIndex:= Self.ItemIndex + 1;
       end
       else
       begin
         if (index > 0) then
           Self.ItemIndex:= Self.ItemIndex - 1;
       end;
     end;
     Msg.Result:= 0;
  end
  else
    inherited;
end;
0

@kAzek
Twój kod działa, robi to co powinien, czyli pomija nazwę nagłówka grupy.
Ale, jeszcze nie wiem dlaczego, ale poruszanie się po elementach listy jest nieco dziwne (nie da się zjechać z elementu jednej grupy do drugiego - trzeba "przelecieć" wszystkie z danej grupy).
Dzięki, sprytne rozwiązanie.

1

@Pepe Trochę zrobiłem ale też nie działa chyba do końca jak powinno, bo z niewiadomych powodów Delphi (a może w ogóle WinApi choć wątpię bo w nim przecież jest do ListView_GetNextItemIndex flaga LVNI_SAMEGROUPONLY której Delphi nie zna a teoretycznie ona miała za to odpowiadać) potrafi za pomocą odpowiednika tej funkcji GetNextItem znaleźć następny / poprzedni Item tylko w danej grupie. Nie chce mi się i nie mam czasu sprawdzać jak by z tym było w czystym WinApi, bo myślę że efekt jest w miarę zadowalający.

procedure TListView.WMKeyDown(var Msg: TWMKeyDown);
var
  currentItem, nextItem: TListItem;
begin
  if ((Msg.CharCode in [VK_UP, VK_DOWN]) and (Self.Items.Count > 0)) then
  begin
     currentItem:= Self.Selected;
     if (currentItem = nil) then
       Self.ItemIndex:= 0
     else
     begin
         if Msg.CharCode = VK_UP then
         begin
           nextItem:= Self.GetNextItem(currentItem, TSearchDirection.sdAbove, []);
           if (nextItem = nil) then
              nextItem:= Self.GetNextItem(currentItem, TSearchDirection.sdLeft, []);
           if (nextItem = nil) then
           begin
             if (currentItem.Index > 0) then
               nextItem:= Self.Items[currentItem.Index - 1];
           end;
         end
         else
         begin
           nextItem:= Self.GetNextItem(currentItem, TSearchDirection.sdBelow, []);
           if (nextItem = nil) then
             nextItem:= Self.GetNextItem(currentItem, TSearchDirection.sdRight, []);
           if (nextItem = nil) then
           begin
             if (currentItem.Index < Self.Items.Count - 1) then
               nextItem:= Self.Items[currentItem.Index + 1];
           end;
         end;
         if (nextItem <> nil) then
         begin
           currentItem.Selected:= False;
           currentItem.Focused:= False;
           nextItem.Selected:= True;
           nextItem.Focused:= True;
           nextItem.MakeVisible(False);
         end;
     end;
     Msg.Result:= 0;
  end
  else
    inherited;
end;

EDIT:
Nawet z ciekawości jednak sprawdziłem w WinApi

const
   LVNI_SAMEGROUPONLY = $80;
var
  tagLVITEMINDEX: TLVItemIndex;

//---- cut ----
SendMessage(Self.Handle, LVM_GETNEXTITEMINDEX, Integer(@tagLVITEMINDEX), LVNI_ABOVE or (not LVNI_SAMEGROUPONLY));

z flagą not LVNI_SAMEGROUPONLY jak i w ogóle bez niej też zwraca tagLVITEMINDEX.iItem (to powinien być index znalezionego) jako -1 jeżeli nie ma nic powyżej w danej grupie czyli zachowuje się tak samo jak tego odpowiednik GetNextItem z Delphi.

0

Sprawdziłem. Rzeczywiście, działa to nieco dziwnie.
Mam 3 grupy aplikacji. Z jednej do drugiej przeskakuje prawidłowo, ale jak jest w pierwszej, to nie zaczyna od 1 aplikacji drugiej grupy, tylko gdzieś od środka.
Do 3 grupy w ogóle nie przechodzi... no ale idea jest, spróbuję w tym pogrzebać, a nuż coś wymyślę... nie chodzi, żeby ktoś mi to napisał tylko pokazał co i gdzie.
Dziękuję za pomoc i twój czas!
Pozdrawiam,
-Pawel

1 użytkowników online, w tym zalogowanych: 0, gości: 1