Den "in place"-Editor verwenden


Sie kennen den so genannten "in place"-Editor sicher schon aus anderen Programmen. Der Windows Explorer ist ein gutes Beispiel. Sie klicken einmal auf einen Dateinamen bzw. benutzen die Taste F2, und es erscheint eine Art Eingabefeld, das sich direkt im Control befindet:



Für diese Fähigkeit ist zuerst ein Stilattribut erforderlich, das beim Erzeugen der List-View angegeben werden muss: LVS_EDITLABELS:

hLV := CreateWindowEx(WS_EX_CLIENTEDGE,WC_LISTVIEW,nil,
  WS_VISIBLE or WS_CHILD or LVS_REPORT or { neu -> } LVS_EDITLABELS or
  LVS_SHOWSELALWAYS,0,0,100,100,wnd,IDC_LV,hInstance,nil);

Streng genommen könnte man jetzt sagen: das war´s.
Wirklich!

Die List-View stellt uns den "in place"-Editor bereit, nur nutzt uns das nicht viel, da wir mit ihm nichts weiter anfangen. Es fehlt also noch ein Schritt, und der heißt wie üblich: wir müssen auf eine Nachricht reagieren. Eigentlich sind es sogar zwei: "LVN_BEGINLABELEDIT" und "LVN_ENDLABELEDIT".

Die Namen sagen es eigentlich schon: "LVN_BEGINLABELEDIT" benachrichtigt das Parent-Fenster der List-View, dass mit dem Editieren einer Bezeichnung (= eines Labels) begonnen wird. Demzufolge informiert "LVN_ENDLABELEDIT" darüber, dass die Bearbeitung abgeschlossen ist oder abgebrochen wurde. Beide Nachrichten werden via "WM_NOTIFY" gesendet.

WM_NOTIFY:
  case PNMHdr(lParam)^.code of
    LVN_BEGINLABELEDIT:
      { ... }
    LVN_ENDLABELEDIT:
      { ... }
  end;

Der lParam ist in beiden Fällen ein Zeiger auf das Record TLVDispInfo, wobei uns die item-Membervariable interessiert, da Sie den alten bzw. neuen Text unseres Labels enthält. Natürlich enthält sie mehr Infos, aber hauptsächlich geht es uns ja um den Text. Folgendes ist zu sagen:

Es ist nicht erforderlich, beide Nachrichten für eine "Vorher-Nachher-Show" der Labelbezeichnung abzufangen. Den neuen Text müssen Sie selbst setzen. Folgendes Codebeispiel würde Ihnen (am Beispiel von "LVN_ENDLABELEDIT") die alte und die neue Bezeichnung anzeigen, aber eben nichts in der List-View ändern:

LVN_ENDLABELEDIT:
  if(PLVDispInfo(lp)^.item.pszText <> '') then
  begin
    i := ListView_GetNextItem(hwndFrom,-1,LVNI_FOCUSED);
    if(i > -1) then
    begin
      ZeroMemory(@buf,sizeof(buf));
      ListView_GetItemText(hwndFrom,i,0,buf,sizeof(buf));
MessageBox(0,pchar(Format('"%s" vs. "%s"', [buf,PLVDispInfo(lp)^.item.pszText])),nil,0); end; end;

Wollen Sie die Bezeichnung nun tatsächlich ändern, geben Sie als Rückgabewert true an (oder im Fall einer "WndProc"-Nachrichtenfunktion jeden anderen Wert als Null). Ansonsten muss das Ergebnis false (Null) sein

      Result := 1;


Auf ESC reagieren

Als Ergänzung muss noch erwähnt werden, dass der Anwender das Ändern der Bezeichnung auch mit ESC abbrechen kann. In dem Fall enthält die Membervariable pszText nil. Sie sollten daher die Rückgabe prüfen, und im genannten Fall von nil false als Funktionsergebnis zurückliefern.

if(PLVDispInfo(lp)^.item.pszText <> nil) then
  { ... }
else
  Result := 0;


Wie war das mit F2?

Angesprochen habe ich es ja bereits: es besteht neben dem Anklicken natürlich auch die Möglichkeit ein Label über ein Menü, einen Button, einen Shortcut, usw. zu ändern. Die List-View kennt dazu die Nachricht "LVM_EDITLABEL", oder Sie verwenden das Makro "ListView_EditLabel", das diese Nachricht kapselt. Das Ergebnis ist das selbe, Sie müssen sich nur für einen Weg entscheiden.

Das Beispielprogramm verwendet sowohl Menü als auch den Shortcut F2 zum Ändern. Sehen wir uns den Menüteil an, denn er enthält den eigentlich produktiven Code. Zuerst sollten wir feststellen, ob überhaupt ein Eintrag ausgewählt ist:

i := ListView_GetNextItem(hLV,-1,LVNI_FOCUSED);
if(i > -1) then
begin
  { ... }
end;

Wenn das der Fall ist ("x" ist in dem Fall größer als -1), dann geben wir der List-View den Fokus:

SetFocus(hLV);

und rufen entweder die o.g. Nachricht oder das Makro auf. Ich habe mich für das Makro entschieden, aber (wie gesagt!) das bleibt Ihnen überlassen:

ListView_EditLabel(hLV,i);

Sie geben das Fenster-Handle der List-View und den Wert des selektierten Eintrags an. Bei der Nachricht würde es so aussehen, dass Sie den Wert ("x") als wParam angeben müssen:

SendMessage(hLV,LVM_EDITLABEL,i,0);

Beim Shortcut schreiben wir nun nicht alles noch einmal; wir generieren stattdessen eine Klick-Nachricht und rufen damit den Menücode auf:

SendMessage(wnd,WM_COMMAND,MAKEWPARAM(IDM_EDITLABEL,BN_CLICKED),0);


Alternative zum Shortcut

Wenn Sie keinen Shortcut benutzen wollen, steht Ihnen auch noch die List-View selbst zur Verfügung. Wenn Sie die Nachricht "LVN_KEYDOWN" bearbeiten, können Sie ebenfalls auf die Taste F2 reagieren. Das Ergebnis ist das gleiche, nur Sie sparen den Shortcut ein.

"LVN_KEYDOWN" wird als Teil von "WM_NOTIFY" gesendet und muss dementsprechend bearbeitet werden:

WM_NOTIFY:
  case PNMHdr(lp)^.code of
    LVN_KEYDOWN:
      { ... }
  end;

Den Tastencode kommen wir dann mit einem Zeiger auf eine TLVKeyDown-Variable, wobei wvKey die benötigte Membervariable ist:

if(PLVKeyDown(lp)^.wvKey = VK_F2) then
  { ... }

Da es sich aber (wie gesagt!) um einen Zeiger handelt, verwenden wir in dem Fall also PLVKeyDown.