Wir wollen uns nun kurz ansehen, wie man Einträge programmgesteuert markieren kann und wie man ihre Markierung umkehrt.
Wozu soll das gut sein?
Ein typisches Beispiel wäre ein Dateimanager. Sie haben die Möglichkeit eine Datei auszuwählen, ein paar Dateien ... oder eben auch alle. Und wenn Sie z.B. alle Dateien bis auf eine bestimmte löschen wollen, dann ist es eindeutig schneller, wenn Sie diese eine Datei auswählen und diese Auswahl dann quasi umdrehen.
Um Ärger zu vermeiden (derart, dass Sie denken: "Hey, das funktioniert irgendwie nicht!"), spendieren wir unserem List-View-Control ein neues Stilattribut:
hLV := CreateWindowEx(WS_EX_CLIENTEDGE,WC_LISTVIEW,nil,
WS_VISIBLE or WS_CHILD or LVS_SHOWSELALWAYS, { ... } );
Das Attribut LVS_SHOWSELALWAYS sorgt nur dafür, dass die aktuelle Auswahl immer sichtbar bleibt, egal ob die List-View den Fokus hat oder nicht.
Nun aber zum eigentlichen Thema -
Unser Beispiel hat zwei neue Menüpunkte, "Alles markieren" und "Markierung umkehren". Beide rufen die selbe Prozedur auf, denn das Prinzip ist das selbe; nur ein bool-Parameter entscheidet, wie sich die Prozedur verhält. In dieser Prozedur wird zunächst der aktuelle Status jedes Eintrags abgefragt. Ich habe mich für die Benutzung des Makros "ListView_GetItemState" entschieden, was aber eigentlich nur eine Kapselung der Nachricht "LVM_GETITEMSTATE" ist. In beiden Fällen wird der Index des jeweiligen Eintrags benötigt. Dazu kommt ein Flag (oder eine Kombination mehrerer Flags), damit wir auch erfahren was uns interessiert! Der Aufruf sieht also so aus:
uRes := ListView_GetItemState(hLV,i,LVIS_SELECTED);
"i" ist dabei der Index des jeweiligen Eintrags, und LVIS_SELECTED sagt aus, dass wir uns für die Markierung des Eintrags interessieren. Weiterhin möglich wären:
LVIS_CUT LVIS_DROPHILITED LVIS_FOCUSED LVIS_OVERLAYMASK LVIS_STATEIMAGEMASK
Diese Flags müssten, wenn Sie mehrere nutzen wollen, or-verknüpft werden, etwa
uRes := ListView_GetItemState(hLV,i,LVIS_SELECTED or LVIS_FOCUSED);
Das bedeutet aber auch, dass Sie für die Prüfung nicht einfach auf Gleichheit testen können. Wir müssen also herausfinden, ob das Flag, das uns interessiert, im Rückgabeergebnis des Makros enthalten ist. Aber eigentlich interessiert uns ja, ob das Flag nicht im Rückgabeergebnis enthalten ist, also:
if(uRes and LVIS_SELECTED = 0) then ;
Wie würde eigentlich der Aufruf der o.g. Nachricht aussehen, wenn wir nicht das Makro verwenden wollen? Nun, einfach so:
uRes := SendMessage(hLV,LVM_GETITEMSTATE,i,LVIS_SELECTED);
Um den Status eines Eintrags zu ändern, verwenden wir das Gegenstück zu obigem Makro: "ListView_SetItemState". Das Makro erwartet zunächst (abgesehen vom Handle der List-View) wieder den Index des jeweiligen Eintrags. Der dritte Parameter entscheidet, ob in unserem Fall die Markierung gesetzt oder aufgehoben werden soll. Der vierte Parameter ist wieder das Flag, das uns interessiert (die Markierung eben!).
ListView_SetItemState(hLV,i,LVIS_SELECTED,LVIS_SELECTED);
Mit dieser Anweisung würde ein Eintrag markiert werden. Um seine Markierung aufzuheben, verwenden Sie als dritten Parameter eine Null anstelle von LVIS_SELECTED:
ListView_SetItemState(hLV,i,0,LVIS_SELECTED);
In unserem Fall ist also entscheidend, ob ein Item markiert ist oder nicht. Ist es nicht markiert, wird es markiert. Ist es markiert, wird diese Auswahl aufgehoben. Nach diesem Prinzip arbeitet der Befehl "Markierung umkehren". Bei der Funktion "Alles markieren" wird der ermittelte Status ignoriert und generell die Markierung gesetzt.
Folgendes ist anzumerken:
Das selbe passiert natürlich auch im Beispielprogramm. Allerdings sieht es dort evtl. etwas merkwürdig aus, da ich eine if-Abfrage eingespart habe. Ich benutze stattdessen ein bool-Array:
const
fStateState : array[boolean]of cardinal = (0,LVIS_SELECTED);
Dieses Array hat nur zwei Zustandsformen: true (= LVIS_SELECTED) oder false (= 0). Anstelle eine umständliche Abfrage zu schreiben wie:
if(uRes and LVIS_SELECTED = 0) or (not fTurnSelection) then ListView_SetItemState(hLV,i,LVIS_SELECTED,LVIS_SELECTED) else ListView_SetItemState(hLV,i,0,LVIS_SELECTED);
übergebe ich dem Array lediglich den ermittelten Status des Eintrags und die bool-Variable aus dem Prozedurkopf als or-Verknüpfung:
ListView_SetItemState(hLV,i,
fStateState[(uRes and LVIS_SELECTED = 0) or (not fTurnSelection)],
LVIS_SELECTED);
Das Ergebnis ist das selbe, nur die if-Verzweigung entfällt.
Probieren Sie es einfach aus!
Starten Sie das Beispielprogramm und markieren Sie z.B. das erste Item, das dritte, das fünfte, usw. Dann benutzen Sie die Funktion "Markierung umkehren". Oder markieren Sie gar nichts, und wählen Sie dann "Alles markieren".