In diesem Kapitel wollen wir uns noch die so genannten "Chevrons" anschauen. Dabei handelt es sich um die kleinen Buttons mit dem Doppelpfeil am rechten Rand eines Bandes, die nur zu sehen sind, wenn das im Band enthaltene Control nicht vollständig dargestellt werden kann. Bekannt ist dieses Verhalten von Toolbars, Menüs u.ä. Elementen.
Allerdings setzt es min. die Version 5.80 der "comctl32.dll" voraus. Im Zweifelsfall hilft hier ein Update des Internet Explorer. Natürlich muss auch Ihre Delphi-Version aktuell genug sein und die neuen Konstanten, Nachrichten und Records kennen. Für Besitzer von Delphi 5 liegt eine spezielle Unit ("CommCtrl_Fragment.pas") bei, die diese Angaben enthält. Besitzer älterer Versionen können diese Unit testweise einsetzen, und Besitzer neuerer Versionen benötigen sie wahrscheinlich gar nicht mehr. Ich gehe also davon aus, dass Sie (auf die eine oder andere Weise) über die entsprechenden Deklarationen verfügen.
Aus dem Grund rüsten wir unser Beispielprogramm mit einer zusätzlichen Toolbar aus. Das bietet gleich einen Vorteil: ich kann und muss Sie auf eine Besonderheit hinweisen. Diese Besonderheit findet sich ein bisschen versteckt im PSDK:
Auf gut Deutsch: Toolbars, die sich im Band einer Rebar befinden, müssen die Stilattribute CCS_NORESIZE und CCS_NOPARENTALIGN benutzen, weil das Rebar-Control die Position und Größe der Toolbar kontrolliert. Wenn Sie diese Attribute vergessen oder weglassen, erhalten Sie alle möglichen Ergebnisse, nur wird die Toolbar nie im Rebar-Control erscheinen. Oder sagen wir: nicht fehlerfrei! Unser Aufruf sieht daher so aus:
hwndChild := CreateWindowEx(0,TOOLBARCLASSNAME,nil,WS_CHILD or WS_VISIBLE or CCS_NODIVIDER or CCS_NORESIZE or CCS_NOPARENTALIGN or TBSTYLE_FLAT,0,0,0,0,hwndRebar,IDC_TOOLBAR,hInstance,nil);
Als Parent wird wieder, wie gehabt, das Rebar-Control angegeben. Es macht aber auch keinen Unterschied wenn Sie direkt das Handle des Hauptfensters angeben ("hwndParent" im Beispielprogramm).
Das Erzeugen der Buttons und Bitmaps einer Toolbar soll hier ebenfalls nicht das Thema sein. Dafür möchte ich Sie auf das Toolbar-Tutorial verweisen. Wir widmen uns lieber den Chevrons -
Um den kleinen Button anzeigen zu können, müssen wir zuerst eine Idealgröße für das Band festlegen. Wird diese Idealgröße unterschritten, erscheint rechts der Chevron und weist den Benutzer darauf hin, dass einige Elemente verborgen sind. Um die Idealgröße aber überhaupt beim Erzeugen des Bandes angeben zu können, ist das zusätzliche Flag RBBIM_IDEALSIZE erforderlich:
rbbi.fMask := { ... } or RBBIM_IDEALSIZE;
Damit steht uns die Membervariable cxIdeal zum Befüllen zur Verfügung. Ich habe die Idealgröße der Toolbar vorher in einer Schleife berechnen lassen, so dass die tatsächliche Breite jedes Buttons (auch von Separatoren, die ja naturgemäß schmaler sind) berücksichtigt wird:
iIdeal := 0; for i := 0 to SendMessage(hwndChild,TB_BUTTONCOUNT,0,0) - 1 do begin SendMessage(hwndChild,TB_GETITEMRECT,WPARAM(i),LPARAM(@rc)); inc(iIdeal,(rc.Right - rc.Left)); end;
Diesen Wert übergeben wir nun an die Membervariable des Records:
rbbi.cxIdeal := iIdeal;
Zu guter Letzt geben wir ein zusätzliches Stilattribut an, damit der Chevron auch tatsächlich erscheint:
rbbi.fStyle := { ... } or RBBS_USECHEVRON;
Das Band wird nun wie bereits beschrieben eingefügt. Wenn wir das Programm nun einmal starten und die Breite der Toolbar verringern, -voilą- präsentiert sie sich wie folgt:

Zum Popup-Menü komme ich gleich, doch zuvor noch ein Hinweis, wieder speziell für die Toolbar: Sie können die Toolbar ja ganz nach Belieben vergrößern oder verkleinern. Dabei kann es passieren, dass einige Buttons nur teilweise zu sehen sind, wenn die Toolbar (eigentlich eher das Band der Rebar) zu klein wird. Da das merkwürdig aussieht, sorgen wir mit dem erweiterten Stilattribut TBSTYLE_EX_HIDECLIPPEDBUTTONS dafür, dass solche "abgeschnittenen" Buttons komplett verschwinden:
SendMessage(hwndChild,TB_SETEXTENDEDSTYLE,0,
TBSTYLE_EX_HIDECLIPPEDBUTTONS);
Wie kann man den Chevron nun verwenden? Die Anwendung muss ermitteln, welche der Toolbar-Buttons nicht sichtbar sind und deren Befehle in dem Popup-Menü anbieten. Microsofts PSDK bietet unter dem Indexeintrag "Creating an Internet Explorer-style Toolbar" eine kurze Anleitung ("Handling Chevrons") dafür. Allerdings ist anzumerken, dass sie bei mir nur eingeschränkt funktioniert hat. Ich war sozusagen zu kleinen "Bocksprüngen" gezwungen, bevor ich am Ziel ankam. Doch sehen wir es uns an -
TB_ISBUTTONHIDDEN", die uns hier leider nicht hilft. Unsere Buttons sind zwar nicht zu sehen, allerdings sind sie nicht versteckt (im Sinne der Definition der genannten Nachricht). Daher müssen wir anders an die Sache herangehen.RB_GETRECT" die Abmessungen des Bandes herauszufinden, das den Chevron anzeigt. Das tun wir:
SendMessage(hRB,RB_GETRECT,WPARAM(PNMRebarChevron(lp)^.uBand),
LPARAM(@rc1));TB_BUTTONCOUNT" verwendet, die sich der Einfachheit halber in einer for-Schleife benutzen lässt. Die nächsten Schritte spielen sich also innerhalb dieser Zeilen ab:
for i := 0 to SendMessage(hTB,TB_BUTTONCOUNT,0,0) - 1 do begin { ... } end;
SendMessage(hTB,TB_GETITEMRECT,WPARAM(i),LPARAM(@rc2));IntersectRect" verglichen. Das Funktionsergebnis interessiert uns hierbei nicht, uns geht es um den ersten Parameter der Funktion: er liefert ein Rechteck (TRect) zurück, das sozusagen den gemeinsam genutzten Teil der beiden angegebenen Rechtecke enthält
IntersectRect(vis,rc1,rc2);Machen wir ein Beispiel. Im folgenden Bild sehen Sie, dass der "Speichern"-Button nicht vollständig sichtbar ist. Der Übersichtlichkeit wegen habe ich in der unteren Toolbar den Button komplett dargestellt, so dass man auch sieht, welche Bereiche des Bandes er normalerweise überschreitet:

IntersectRect", würde aber nur der sichtbare Teil (wie in der oberen Toolbar) zurückgeliefert werden.EqualRect" benutzt wird. Hierbei interessiert uns der Rückgabewert allerdings wieder, denn wenn die beiden Rechtecke unterschiedlich sind (Ergebnis false), bedeutet das, der Button ist nicht oder nicht vollständig zu sehen und muss als Item in das Popup-Menü eingetragen werden. Sind beide Rechtecke identisch (Ergebnis true), heißt das selbstverständlich, der Button ist vollständig sichtbar:
if(not EqualRect(vis,rc2)) and (tb.fsStyle <> BTNS_SEP) then AppendMenu(hm,MF_STRING,tb.idCommand,pchar(pText));Sie sehen hier auch gleich noch die Kontrollabfrage, ob es sich bei dem Button um einen Separator handelt. Der Grund liegt auf der Hand: Separatoren müssen nun wirklich nicht ins Menü. Theoretisch wäre das aber auch kein Problem, da es bei Menüs ja vergleichbares gibt. :o)
Soweit die Anleitung von Microsoft im PSDK. Wenn Sie sie nachvollziehen, dann werden Sie (vielleicht) bemerken, dass die Anzeige der Menüeinträge nicht wirklich funktioniert. Das Beispielprogramm enthält dazu den Compilerschalter PSDK, der alle meine zusätzlichen Schritte ausklammert, so dass Sie es gern probieren können.
Folgendes ist zu sagen: die Koordinaten der Toolbar-Buttons werden sich in diesem Experiment nie ändern. Das liegt ganz einfach daran, dass sie relativ zur Toolbar zurückgeliefert werden. Egal wohin Sie die Anwendung auch schieben, der erste Button wird immer die Maße (0,0,54,36) besitzen. (Die Werte hängen natürlich von den benutzten Stilattributen ab und können bei Ihnen anders sein.)
Da die Toolbar nun standardmäßig das dritte erzeugte Control ist und sich daher auch im dritten Band (und in der zweiten Zeile) befindet, werden die Abmessungen der Toolbar-Buttons in den meisten Fällen scheinbar außerhalb des Bandes liegen. Die Funktion "IntersectRect" würde in solchen Fällen als gemeinsam genutztes Rechteck immer (0,0,0,0) liefern.
Für einigermaßen brauchbare Ergebnisse müssten Sie die Toolbar nach ganz oben (an den Anfang der Rebar) schieben, so dass sie zum ersten Control wird. Dann kann es aber passieren (und bei mir ist es passiert!), dass ein Button nicht mehr sichtbar war und trotzdem nicht im Popup-Menü auftauchte. Das lag daran, dass bei der Ermittlung des Band-Rechtecks auch der Platz für den Text und die Breite des Chevrons berücksichtigt wurden. Das heißt also:
RB_GETBANDBORDERS"
SendMessage(hRB,RB_GETBANDBORDERS,WPARAM(PNMRebarChevron(lp)^.uBand), LPARAM(@vis));Leider können wir diese Nachricht nicht allein verwenden, da sie wirklich nur die Grenzwerte liefert. Diese Werte kombinieren wir aber mit dem in Schritt 1 ermittelten Band-Rechteck. Dabei werden die Werte der linken oberen Ecke addiert, die der rechten unteren allerdings subtrahiert, wodurch wir das Band-Rechteck verkleinern:
inc(rc1.Left,vis.Left); inc(rc1.Top,vis.Top); dec(rc1.Right,vis.Right); dec(rc1.Bottom,vis.Bottom);Und weil der Chevron auch Platz belegt, der fälschlich als verfügbar betrachtet wird, ziehen wir seine Breite ebenfalls ab:
dec(rc1.Right, PNMRebarChevron(lp)^.rc.Right - PNMRebarChevron(lp)^.rc.Left);
OffsetRect(rc2,rc1.Left,rc1.Top);
Glauben Sie es ruhig: nach diesen beiden Ergänzungen funktioniert das Popup-Menü des Chevrons wie gewünscht und zeigt die Toolbar-Buttons an, die momentan nicht zu sehen sind.
Und damit sind wir beim Thema: der Anzeige des Popup-Menüs. Nachdem ich ein bisschen abgeschweift bin, nun zum eigentlichen Kern. Wird der Chevron angeklickt, löst er die Benachrichtigung "RBN_CHEVRONPUSHED" aus, die als Teil von "WM_NOTIFY" bearbeitet werden kann. Der lparam zeigt dabei auf ein Record vom Typ
Die Koordinaten sind allerdings Client-abhängig und müssen erst in gültige Bildschirmwerte umgewandelt werden. Also weisen wir sie der Einfachheit halber einer TPoint-Variablen zu:
p.X := PNMRebarChevron(lp)^.rc.Left; p.Y := PNMRebarChevron(lp)^.rc.Bottom;
Umgewandelt werden Sie dann mit der Funktion "ClientToScreen", von der wir u.a. auch die Anzeige des Menüs abhängig machen:
if(ClientToScreen(wnd,p)) and (GetMenuItemCount(hm) > 0) then TrackPopupMenu(hm,TPM_LEFTALIGN,p.X,p.Y,0,wnd,nil);