Delphi - TListView komponens

TListView 2. rész

forráskód letöltése
Az előző részben megnéztük, hogyan lehet elemeket és oszlopokat hozzáadni egy TListView komponenshez tervezési időben. Ebben a cikkben részletesebben megvizsgáljuk a TListColumns objektumot: hogyan lehet létrehozni, módosítani az oszlopokat futásidőben; valamint megnézzük a virtuális listák használatát. A TListView oszlopait egy-egy TListColumn objektum reprezentálja, melyeket a komponens Columns tulajdonsága fog össze. Az oszlopoknál a legfontosabb, hogy csak akkor látszanak, ha a TListView ViewStyle tulajdonsága vsReport, valamint a ShowColumnHeaders igazra van állítva.

A TListView oszlopai

A TListColumns objektum

A TListView oszlopaihoz futásidőben a Columns tulajdonságán keresztül férhetünk hozzá, amely egy TCollection-ból származó objektum. Ez annyit jelent, hogy az oszlopokat úgy kezelhetjük, mint egy tömböt, melynek elemei 0-tól Columns.Count-1-ig vannak indexelve, és a tömb minden eleme egy-egy TListColumn.
Az objektum Create() konstruktorát és a Destroy() destruktorát ne használjuk, ugyanis ezt elvégzi maga a komponens.

A TListColumns tulajdonságai

Count: integer Az oszlopok számát adja meg.
Items: TListColumn Az Items tulajdonságon keresztül férhetünk az egyes oszlopokhoz. Pl.: LV.Columns.Items[2] a TListView harmadik oszlopát adja meg.(Az indexelés 0-val kezdődik!) Az előbbi kifejezés egyenértékű a következővel: LV.Columns [2]. Ugyanakkor a LV.Column[2] is ugyanezt az eredményt adja.

A TListColumns metódusai

function Add: TListColumn;

Az Add függvénnyel új oszlopot adhatunk a komponenshez; az új oszlop a már meglévők után kerül a listába, vizuálisan a jobboldali legszélső lesz. Egy kis "trükkel" segédváltozó használata nélkül létrehozhatunk egy új oszlopot, és mindjárt beállíthatjuk a tulajdonságait is:
with LV.Columns.Add do
 begin
 Caption:='Szöveg';
 Width:=100;
 end;

procedure Delete(Index: Integer);

A megadott indexű oszlopot törölhetjük az eljárással. Pl.: LV.Columns.Delete(0) a baloldali oszlopot törli.

procedure Clear;

Az oszlopok törlése. (Azaz: while LV.Columns.Count>0 do LV.Columns.Delete(0);)

function Insert(Index: Integer):TListColumn;

Egy oszlop beszúrása a megadott pozícióba. Pl. a LV.Columns.Insert(0) beszúr egy oszlopot, amely bal oldalt jelenik meg a komponens fejlécében, a többi oszlop jobbra tolódik.

procedure BeginUpdate, procedure EndUpdate

Ezt az utasításpárost akkor használjuk, ha valami nagyobb változtatást végzünk az oszlopokon, ekkor a komponens a BeginUpdate() utáni módosításokat addig nem rajzolja ki, míg nem hívjuk meg az EndUpdate()-t. Vigyázzunk arra, hogy ezt a két utasítást párban használjuk: két, egymás utáni BeginUpdate() után két EndUpdate()-t kell használnunk!


A TListColumn objektum

Minden egyes oszlopot egy-egy TListColumn objektum reprezentál.

A TListColumns tulajdonságai

A tulajdonságok már szerepeltek az előző részben - ezek ugyanazok, mint az Object Inspector-ból módosítható tulajdonságok - de a teljesség kedvéért itt is olvashatók.


Alignment: TAlignment A szöveg pozíciójatype TAlignment = (taLeftJustify, taRightJustify, taCenter);
  • taLeftJustify balra igazított
  • taCenter középre igazított
  • taRightJustify jobbra igazított

AutoSize: boolean Ha True az oszlop automatikusan beállítja a szélességét

Caption: string Az oszlopfejléc szövege

ImageIndex: integer Csak Delphi4 és magasabb verziókban. Az oszlop felirata mellett megjeleníthető egy ábra is, melyet a komponens a SmallImages tulajdonságból vesz. Ha ImageIndex=-1, akkor nincs kép.

MaxWidth: integer Az oszlop maximális szélessége. Ha 0, nincs korlátozás.

MinWidth: integer Az oszlop minimális szélessége.

Width: integer Az oszlop szélessége. Ha az AutoSize tulajdonság true, a szélességet a komponens nem veszi figyelembe.

Az oszlopokhoz kapcsolódó események

type
TLVColumnClickEvent = procedure(Sender: TObject; Column: TListColumn) of object;
property OnColumnClick: TLVColumnClickEvent;

Az esemény akkor lép fel, ha az egyik oszlopra kattintunk, az egér bal gombjával. A ColumnClick tulajdonságnak igaznak kell lennie. A Sender adja meg a TListView komponenst, amelyen kattintottunk, a Column pedig az oszlopot.
Az eseményt pl. felhasználhatjuk arra, hogy újrarendezzük a listát. (Lásd Windows Explorer). A példaprogram a következű módon kezeli az eseményt:
procedure TfrmMain.LVColumnClick(Sender: TObject; 
     Column: TListColumn);
begin
  reverse:=(sortcolumn=column.index)and(not reverse); 
  sortcolumn:=column.index;
  filelist.sort(listcompare);
  […]
end;
Az eseménykezelő a sortcolumn globális változóba teszi az oszlop sorszámát, amelyre a felhasználó kattintott, valamint a reverse változót igazra állítja, ha fordított rendezést kell végrehajtani. Mivel a virtuális listáknál nem lép fel OnCompare() esemény, így a listát a TList.Sort() eljárással rendezzük.

type
TNotifyEvent = procedure(Sender: TObject) of object;
property OnColumnDragged: TNotifyEvent;

Akkor lép fel, ha a felhasználó az egyik oszlopnak vonszolással megváltoztatta a pozícióját. Csak Delphi4 és újabb verziók, valamint a megfelelő verziójú COMCTL32.DLL szükséges.

type
TLVColumnRClickEvent = procedure(Sender: TObject; Column: TListColumn; Point: TPoint) of object;
property OnColumnRightClick: TLVColumnRClickEvent;

Ha egy oszlopra kattintunk az egér jobb gombjával, akkor lép fel ez az esemény. A Sender azonosítja a komponenst. A Column az oszlopot, míg a Point a kattintás koordinátáit, a komponens koordinátarendszerében - tehát a komponens clienttoscreen() függvényével - át kell konvertálnunk. (Vigyázzunk arra, hogy ne csak azt írjuk, hogy ClientToScreen(Point), mert ez a Form metódusát hívná meg, és egészen más koordinátákat kapnánk!) Remekül használható az esemény egy, az oszlopokhoz kapcsolódó menü megjelenítéséhez. Pl.:
procedure TfrmMain.LVColumnRightClick(Sender: TObject; 
  Column: TListColumn;  Point: TPoint);
var x,y:integer;
begin
  x:=lv.clienttoscreen(point).x;
  y:=lv.clienttoscreen(point).y;
  pmTest.popup(x,y);
end;

Virtuális listák kezelése

A virtuális listákhoz néhány eseménykezelőt kell megismernünk, ezeket pedig megpróbáljuk a példaprogramon keresztül értelmezni.

Események

type
LVOwnerDataEvent = procedure(Sender: TObject; Item: TListItem) of object;
property OnData: TLVOwnerDataEvent;

Akkor lép fel, amikor a TListView-nek egy elem adataira van szüksége. Az Item paraméter adja meg az elemet. Az Item.Index segítségével tudhatjuk meg az elem sorszámát, ekkor a saját listánkból kikereshetjük a kívánt adatokat, és az Item megfelelő tulajdonságainak módosításaival adhatjuk meg az elem kinézetét.
procedure TfrmMain.LVData(Sender: TObject; Item: TListItem);
var p:pentry;
begin
  if item.index>(filelist.count-1) then exit;
  p:=pentry(filelist[item.index]);
  modifyentry(p);
  item.caption:=extractfilename(p^.path); 
  item.imageindex:=p^.imageindex; 
  item.SubItems.Add(extractfiledir(p^.path)); 
  item.subitems.add(format('%d kb',[(p^.size div 1024)+1])); 
end;

A program egy TList-ben tárolja az elemeket, az eseménykezelő innen veszi a szükséges paramétereket.. A ModifyEntry() keresi elő a megfelelő fájltípushoz tartozó képet.(Erről a cikk végén lesz bővebben szó). Erre azért van szükség, mert ez viszonylag időigényes feladat: ha keresés közben kérdeznénk le az ikont, a keresés időtartama két- háromszorosára is nőhet.




type
TLVOwnerDataFindEvent = procedure(Sender: TObject; Find: TItemFind; const FindString: string; const FindPosition: TPoint; FindData: Pointer; StartIndex: Integer;
Direction: TSearchDirection; Wrap: Boolean; var Index: Integer) of object;
property OnDataFind: TLVOwnerDataFindEvent;

Az esemény akkor használatos, ha virtuális listánk van, és meghívtuk a FindCaption() vagy FindData() metódusokat. A paraméterek megegyeznek a kereső metódusok paramétereivel. Miután elvégeztük a keresést, a megtalált elem sorszámát az Index paraméterben kell visszaadnunk. Ha nincs találat Index:=-1.

type
TLVOwnerDataHintEvent = procedure(Sender: TObject; StartIndex, EndIndex: Integer) of object;
property OnDataHint: TLVOwnerDataHintEvent;

Általában az OnData() előtt hívódik meg. Akkor lép fel az esemény, ha egyszerre több, egymás utáni elemet kell frissíteni. A Startindex az első elem, az EndIndex az utolsó elem sorszámát adja meg.
procedure TfrmMain.LVDataHint(Sender: TObject; StartIndex,
  EndIndex: Integer);
var i:integer;
begin
//több elem aktualizálása.
//az ondata() _előtt_ lép fel
for i:=startindex to endindex do
    begin
    if i>=filelist.count then break;
    modifyentry(pentry(filelist[i]));
    end;
end;


type
TLVOwnerDataStateChangeEvent = procedure(Sender: TObject; StartIndex, EndIndex: Integer;
OldState, NewState: TItemStates) of object;
property OnDataStateChange: TLVOwnerDataStateChangeEvent;

Akkor lép fel, ha az elem(ek) State tulajdonsága megváltozik. A Startindex az első elem, az EndIndex az utolsó elem sorszámát adja meg. Az OldState a régi állapot, a NewState pedig az új állapot.


Egyebek

Az SHgetFileInfo függvény
WINSHELLAPI DWORD WINAPI SHGetFileInfo(
    LPCTSTR pszPath,  
    DWORD dwFileAttributes,  
    SHFILEINFO FAR *psfi,  
    UINT cbFileInfo,  
    UINT uFlags 
   );

A függvény segítségével információkat kaphatunk fájlokról, fájl csoportokról. A pszPath a fájl elérési útját adja meg, itt használhatók dzsóker karakterek is. A dwFileAttributes adja meg a fájl attribútumait, a psfi egy TShFileInfo típusú struktúrára kell, hogy mutasson, a cbFileInfo pedig a struktúra méretét adja meg, azaz sizeof(TSHFileInfo). Az uFlags adja meg milyen információkra van szükségünk.
  • SHGFI_DISPLAYNAME Lekérdezi a fájl nevét. (Pl.: C:\RECYCLED č Lomtár)
  • SHGFI_ICON A fálj ikonjának leíróját a hIcon mezőjébe másolja a TSHFileInfo struktúrának. Ezt egy TIcon-nál felhasználva "szert tehetünk" a fájl ikonjára.
  • SHGFI_LARGEICON A hIcon (ill. iIcon) a nagy ikonra fog mutatni.
  • SHGFI_SMALLICON A hIcon (ill. iIcon) a kicsi ikonra fog mutatni.
  • SHGFI_SYSICONINDEX A rendszerikonokat tartalmazó ImageList-beli indexét adja meg a fájl ikonjának, amely az iIcon-ból olvasható ki.
  • SHGFI_TYPENAME Lekérdezi a fájltípus leírását. (pl.: 'Word dokumentum')

Ha z uFlags tartalmazza a SHGFI_LARGEICON, SHGFI_SMALLICON, SHGFI_SYSICONINDEX konstansok valamelyikét, a visszatérési érték a rendszer ikonokat tartalmazó ImageList leírója lesz:
with lv do
     begin
     largeimages:=timagelist.create(self);
     largeimages.handle:=SHGetFileInfo('',0,sfi,sizeof(TSHFileInfo),
                               SHGFI_ICON or SHGFI_SYSICONINDEX or
                               SHGFI_LARGEICON);
     largeimages.shareimages:=true;
     smallimages:=timagelist.create(self);
     smallimages.handle:=SHGetFileInfo('',0,sfi,sizeof(TSHFileInfo),
                              SHGFI_ICON or SHGFI_SYSICONINDEX or
                              SHGFI_SMALLICON);
     smallimages.shareimages:=true;
     end;

TListView cikksorozat