TreeView, ListView i właściwość Data

frickle

Niniejszy artykuł traktuje o mało znanej właściwości DATA komponentów TTreeNode i TListItem, będących elementami komponentów TTreeView i TListView.
Właściwość data jest typu Pointer czyli jest wskaźnikiem amorficznym. Oznacza to, że może wskazywać na dowolny typ danych. Jest to bardzo przydatne, gdy tworzymy np. Listę opartą na zawartości obiektów naszej własnej klasy. Ale do czego może nam się ta właściwość przydać?
Załóżmy, że mamy w naszym systemie klasę:

type TPracownik = class
  imie : string;
  nazwisko : string;
  wiek : byte;
  stanowisko : string;
  identyfikator : integer;
  ...
end;

Chcemy wyświetlić listę pracowników, filtrować ją, ale na dodatek mieć łatwy dostęp do obiektów klasy pracownik.
Załóżmy zatem, że mamy 1000 obiektów naszej klasy, które przechowujemy w tablicy.

var
  pracownicy : array of TPracownik;

Wyświetlenie danych z takiej tablicy nie nastręczy wielkich problemów. Wyfiltrowanie ich również. Ale co się stanie, gdy z wyfiltrowanej listy chcemy wyciągnąć dane jakiegoś pracownika? Poruszanie się po indeksach tablicy odpada. Możemy stworzyć dodatkową pomocniczą tablicę - ale to dość nieeleganckie. I tu z pomocą przychodzi właściwość data

Wypełnienie naszej listy wyglądać będzie mniej więcej tak:

procedure WypelnijListe;
var
  i : integer;
  item : TListItem;
begin
  for i:=low(pracownicy) to high(pracownicy) do
    begin
      if spelnionywarunekfiltru then
        begin
          item := ListView1.items.add;
          item.caption := pracownicy[i].imie;
          item.subitems.add(pracownicy[i].nazwisko);
          //i tutaj zajmujemy się naszą właściwością data
          item.data := @pracownicy[i]; //wskaźnik do konkretnego obiektu
        end;
    end;
end;

Co oznacza zapis:

item.data := @pracownicy[i];

???

Otóż mówi on mniej więcej tyle:
Weź element nr <font color="red">i</span> z tablicy <font color="red">pracownicy</span>, pobierz jego wskaźnik, i przypisz ten wskaźnik do właściwości <font color="red">data </span>elementu <font color="red">item</span>.

Od teraz, w elemencie listy mamy ukryty wskaźnik do obiektu który jest opisywany przez ten element.

Teraz, niezależnie od tego czy lista jest przefiltrowana czy też nie, możemy bezpośrednio odwołać się do obiektu który na przykład klikniemy. Załóżmy, że przy wybraniu elementu listy, chcemy wyświetlić szczegółowe dane w TMemo. W procedurze obsługi zdarzenia WYBORU ELEMENTU naszego ListView wpisujemy taki oto kod:

  memo1.clear; //wyczyszczenie kontrolki
  memo1.lines.add(TPracownik(ListView1.Selected.data^).Imie);
  memo1.lines.add(TPracownik(ListView1.Selected.data^).nazwisko);
  memo1.lines.add(inttostr(TPracownik(ListView1.Selected.data^).wiek));
  memo1.lines.add(TPracownik(ListView1.Selected.data^).stanowisko);
  memo1.lines.add(inttostr(TPracownik(ListView1.Selected.data^).identyfikator));

A teraz troszkę wyjaśnień. Coż to jest ten tajemniczy zapis:

  TPracownik(ListView1.Selected.data^)...

Otóż w tej konstrukcji, w pierwszej części wykonujemy rzutowanie, czyli mówimy kompilatorowi, że to co jest w nawiasach, ma traktować jako typ TPracownik. W nawiasie, pobieramy właściwość data z wybranego (zaznaczonego) elementu listy, a ponieważ jest on wskaźnikiem, to musimy zastosować operator wyłuskania ^. Powoduje on, że zwrócony zostaje obiekt na który ten wskaźnik wskazuje. Wynikiem całego zamieszania, jest obiekt typu TPracownik przyporządkowany do elementu listy.
Jak widać, teraz już nic prostszego niż wyciągnąć z tego obiektu interesujące nas dane lub wywołać jego metodę.

Z elementami drzewa postępuje się w sposób analogiczny.

Życzę owocnej współpracy z właściwością data.

2 komentarzy

nie wiem czy mało znanej... jest w helpie i czasem samo się narzuca jako rozwiązanie...

skoro pracownik jest klasą to nie ma sensu dodawać @pracownicy[i], a wystarczy pracownicy[i], wtedy potem nie trzeba używać "^", można tak bo zmienna klasa jest referencją na obiekt