GetDefaultAccelerator der unbekannte Helfer

Ich bin Tastatur-Fan und ich achte in meinen Anwendungen immer darauf, dass ein Benutzer meine Anwendungen einfach mit der mit der Tastatur bedienen kann. Accelerator sind in Windows für den Entwickler hier ein einfaches Tool, Funktionen über die Tastatur einfach verfügbar zu machen.

Tastatur Acceleratoren werden in der MFC in CFrameWnd geladen und PreTranslateMessage angewendet. Üblicherweise passiert das Laden direkt wenn LoadFrame ausgeführt wird, oder wenn in einer MDI-Anwendung das Childframe mit dem entsprechenden Document-Template erzeugt wird.

Allerdings wird es spannend, wenn man den Accelerator wechseln will aufgrund verschiedener Ansichten oder Zustände in der Anwendung. Denn ein CMainFrame weiß ja eigentlich nichts von den Dokumententypen oder dessen Zustand. Und ich fände es eigentlich nicht schön entsprechenden Code imFrame zu verankern.

Zum Glück ist das auch nicht nötig, denn es gibt schon immer die zwei netten undokumentierten Funktionen CFrameWnd::GetDefaultAccelerator und CDocument::GetDefaultAccelerator.

Sobald eine Tastatureingabe in CFrameWnd::PreTranslateMessage ankommt wird CFrameWnd::GetDefaultAccelerator aufgerufen. Nur wenn diese Funktion NULL zurückgibt wird der im Frame gespeicherte m_hAccel verwendet. Nun und CFrameWnd::GetDefaultAccelerator ruft nun CDocument::GetDefaultAccelerator für ds aktuelle Dokument auf.
Damit kann nun das Dokument selbst über den Accelerator bestimmen, der verwendet werden soll. Wird NULL zurückgegeben wird der Accelerator des Frames verwendet.

In der MFC-Next ist das Verhalten etwas anders, weil dort der Keyboard Handler eingeschaltet ist, aber letzten Endes wird GetDefaultAccelerator auch von dort aufgerufen.

Was macht man eigentlich gegen unerwünschte Übersetzung von Acceleratoren?

Accelerator sind ein einfach gutes Hilfsmittel um Funktionen in einem Programm über die Tastatur verfügbar zu machen.
Behandelt werden Accelerator in CFrameWnd::PreTranslateMessage. D.h. bevor eine Eingabe-Nachricht aus der Message Queue an ein Fenster ausgeliefert wird, bekommt das Fenster selbst und jedes Parent des Fensters die Nachricht zur Behandlung in PreTranslateMessage angeboten.

Aber das kann auch zu einem kleinen Problem werden. Nehmen wir an, wir haben eine Datenbank Anwendung und Bild-Hoch/Runter werden über Accelerator für das Blättern in den Datensätzen definiert. Soweit OK.

Was aber wenn man nun ein Inplace Control hat, oder eine kleine dynamische Listbox, die man einblendet und in der man nun auch blättern will? Dann wird der Accelerator im CFrameWnd zum Tastenschlucker und in dem Moment führen Bild-Hoch/Runter nicht zu dem gewünschten Blättern im Popupfenster.

Wie geht man nun vor?
Wenn man im View nun in PreTranslateMessage FALSE zurückgibt frisst der Accelerator im CFrameWnd die Taste. Gibt man TRUE zurück, dann wird die Nachricht nicht ausgeliefert. Der Trick ist eigentlich ganz simpel. Wenn alle PreTranslateMessage Funktionen FALSE zurückgeben wird die Nachricht ausgeliefert indem die beiden Funktionen TranslateMessage und DispatchMessage aufgrufen werden.
Das Ganze kann man abkürzen. Im PreTranslateMessage Handler des Fensters selbst  kann man nun einfach TranslateMessage und DispatchMessage aufrufen und nun TRUE zurückgeben. Das Fenster selbst oder evtl. sein Parent sorgt dafür, dass es die gewünschte Nachricht bekommt wie man es erwartet und der Accelerator im CFrameWnd wird umgangen.

CDialog::SetDefID und DM_SETDEFID, des Tastaturfreunds Liebling

Die Frage um die Eingabe-Taste in Dialogen und wie man diese „missbraucht“ (sage ich mal provokant) ist eine regelmäßige Frage in allen Foren.

Die Intention ist oft klar. Man möchte mit der Eingabetaste eine bestimmte Aktion verbinden, die evtl. sehr oft ausgeführt werden soll und nicht den Dialog schließen.
Die üblichen Wege sind schon mehrfach diskutiert worden und auch in meinem Blog finden sich dazu ein Artikel .

Zu kurz kommt bei dieser Diskussion die Funktion CDialog::SetDefID bzw. DM_SETDEFID Nachricht.
Was macht diese Funktion/Nachricht?
Sie definieren die Button-ID, die als Default-Aktion in einem Dialog ausgelöst werden soll und das ist nichts anderes als die Aktion die geschehen, soll wenn die Eingabe-Taste gedrückt wird.
Viele Entwickler definieren einfach OnOK um. Aber das eigentlich tolle ist mit SetDefID den Button in Abhängigkeit der Daten umzusetzen und das hat auch einen visuellen Effekt für den Nutzer.

Mal ein Beispiel:
Wir haben einen Dialog mit zwei List Views. Links Elemente die zur Auswahl stehen, rechts die Elemente in der Reihenfolge, die der Benutzer ausgewählt hat.
Der Mausschubser wird einfach die Einträge auf der linken oder rechten Seite doppelklicken und damit auswählen oder entfernen. Entsprechende Buttons für Hinzufügen und Entfernen wird es auch geben. Man kann also auch links oder rechts markieren und dann den Hinzufügen oder Entfernen Schalter nutzen.

Dem Tastaturnutzer können wir helfen indem wir intelligent CDialog::SetDefID / DM_SETDEFID verwenden. Die Vorgehensweise ist einfach.

  • Wir richten uns nur danach in welchem List View wir uns befinden, d.h. befinden wir uns im linken List View steuern wir den Hinzufügen Schalter, und im rechten List View steuern wir den Entfernen Schalter.
  • Wird also im linken List View ein Item ausgewählt, setzen wir mit CDialog::SetDefID / DM_SETDEFID die ID des Hinzufügen Schalters.
  • In dem Moment wird der Hinzufügen Schalter zum Default-Button. Der Nutzer kann nun die Eingabe-Taste drücken und die Items werden in die rechte Box verschoben.
  • Links liegt jetzt nun noch der Fokus, aber es sind keine Items mehr markiert. D.h. wir setzen nun den Default Button zurück auf IDOK.
  • Jetzt kann der Nutzer erneut ein Item markieren. Der Default-Button wird wieder der Hinzufügen/Entfernen Schalter und die Eingabetaste macht was der Nutzer gerne hätte.
  • Ist kein Item mehr markiert schließt die Eingabetaste wieder über IDOK den Dialog.

Ohne Maus kann man also mit den Pfeiltasten, der Leertaste (evtl. Strg-Taste) und der Eingabetaste diesen Dialog bedienen. Und das sogar intuitiv, denn der entsprechende Default Button wird ja in der UI schön umrandet und hervorgehoben.
Das freut jeden Tastaturfreund. Und man muss gar nichts groß machen mit der Behandlung Eingabetaste.

Damit das Ganze nicht so abstrakt ist, habe ich ein kleines Sample gebaut, dass diese Anwendung zeigt. Es hat keine Implementierung für Drag&Drop aber es macht deutlich, wie man dem Tastaturnutzer entgegen kommen kann indem man die Controls geschickt aktiviert.

Vista: Wo ist bitte die Option „Unterstrichene Buchstaben für Tastatur Navigation ausblenden“ geblieben

Ich bin Tastatur Fan. Wenn ich es vermeiden kann benutze ich nicht die Maus. Aus diesem Grund möchte ich natürlich auch immer die Acceleratoren (unterstrichenen Buchstaben) in Menüs oder Dialogen sehen.
Leider ist seit Windows XP die Standardeinstellung so, dass man erst durch Drücken der ALT-Taste die Acceleratoren sichtbar macht.

Unter XP findet man das ganze unter

  • Systemsteuerung -> Darstellung und Design -> Anzeige -> Darstellung -> Effekte:
    Unterstrichene Buchstaben für Tastatur Navigation ausblenden (mit ALT-Taste einblenden)

Leider hat ist diese Option unter Windows Vista nicht mehr dort zu finden.
Dank Windows Vista Suche nach „Tastenkombination“ führte mich das direkt zu:

  • Systemsteuerung -> Erleichterte Bedienung -> Center für erleichterte Bedienung -> Bedienung der Tastatur erleichtern:
    Tastenkombinationen und Zugriffstasten unterstreichen

Wie öffnet man in den Office-Produkten das Kontextmenü für Smarttags?

Ich bin Tastaturnutzer und ich vermeide die Nutzung der Maus weitgehend , wenn ich arbeite. Also möchte ich auch möglichst viele Funktionen der Produkte, die ich nutze, mit der Tastatur bedienen.

In der letzten Zeit war ich wieder etwas öfters am schreiben von Dokumentationen und immer wieder habe ich Text aus anderen Dokumenten in ein neues Dokument eingefügt. Nur passte die Formatierung nicht. Word bietet einem ja gleich einen Smarttag beim Einfügen an, der einem auch erlaubt direkt die Formatierung des neuen Textes an  die alte Formatierung anzupassen, oder eben nur den Text zu übernehmen. Aber deshalb die Maus benutzen?
Gibt es also einen Shortcut für diese Smarttags?
Nach einigem stöbern in der Office Onliine Doku, fand ich dieses nützliche Helferlein:

Alt+Umschalt+F10

Am Rande sei erwähnt, dass sich damit natürlich auch Smarttags in Excel und Outlook genauso bedienen lassen…

VS-Tipps & Tricks: Einfaches Navigieren mit Strg+-

Vielleicht ist einigen schon aufgefallen, dass es die Schalter Navigate Backward und Navigate Forward im Standard-Toolbar von Visual Studio gibt. Diese Funktionen werden über die Hotkeys Strg+- (Bindestrich) und Strg+Umschalt+- ausgelöst.

Bei dieser Funktion werden Go-Back-Markierungen in einer Liste vermerkt. Hierbei wird nicht jede Cursorbewegung aufgezeichnet, sondern Bewegungen, die über eine größere Distanz erfolgen.

D.h. man kann mit Strg+- sofort an die Position zurückspringen, die man soeben verlassen hat. Die Umkehrfunktion wird wie gewohnt mit der Umschalt-Taste ausgelöst, Umschalt+Strg+-.

Solche Go-Back-Marken werden in den folgenden Fällen gesetzt.

  • Öffnet man eine neue Datei, dann wird die Position in der aktuellen Datei, als Go-Back-Marke gespeichert. Das Wechseln in eine andere Datei (Strg+Tab) setzt keine Go-Back-Marke (was ich verwunderlich finde).
  • Jede Löschoperation nach einer Cursorbewegung setzt eine Go-Back-Marke.
  • Eine Textsuche (Strg+F) setzt eine Go-Back-Marke an der Fundstelle.
  • Inkrementelle Suche (ob vorwärts oder rückwärts ist egal), trägt eine Go-Back-Marke in die Liste ein und gleichfalls, wenn die inkrementelle Suche beendet wird.
  • Verwendet man GotoLine (Strg+G) oder bewegt den Cursor mehr als 10 Zeilen von der aktuellen Position weg, wird eine Go-Back-Marke an der neuen Position gesetzt. Dies gilt auch wenn dies durch eine Suche ausgelöst wird, die mehr als 10 Zeilen weiterspringt. In diesem Fall wird auch die Startposition als Go-Back-Marke gespeichert.
  • Jeder Klick mit der linken bzw. rechten Maustaste platziert eine Go-Back-Marke an der alten Cursor-Position. Weitere Mausklicks ohne Cursor-Operationen zwischen drin platziert keine neue Go-Back-Marke.
  • Jeder Step-Into beim Debuggen löst auch setzt auch eine Go-Back-Marke.

Sehr nett ist auch die Möglichkeit den gesamten Text von der aktuellen Position bis zurück zur letzten Go-Back-Marke zu selektieren. Dies erfolgt über den Hotkey Strg++ (Pluszeichen).

Ausgesprochen nützlich finde ich diese Funktion auch beim debuggen. Man kann sofort an die Stelle zurückspringen von der man soeben kam, ohne das Call-Stack-Fenster zu verwenden.

BTW: Diese Funktionen habe ich erst durch das versehentliche Auslösen der Kombination Strg++ vor längerer Zeit entdeckt 🙂

Der IE 7 und weiterrolleneiner Bildschirmseite…

Da arbeite ich schon seit Jahren mit dem IE, und schätze mich als Tastaturfan ein, der alle möglichen Hotkeys nutzt. Aber jetzt musste mir meine elfjährige Tochter Lea den simpelsten Trick zeigen, wie man auf einer Internetseite einen Bildschirmauschnitt weiter rollen kann… mit der Leertaste! 😯

Und es kommt noch besser mit Umschalt+Leertaste kann man eine Bildschirmseite hoch blättern.

Mir waren diese Hotkeys aus dem Adobe Acrobat Reader bekannt, aber dass der IE diese Hotkeys auch benutzt war mir neu. Vielleicht auch einem von Euch…

VS-Tipps & Tricks: Edit.GotoBrace (Strg+´) kann noch mehr

Mit Strg+´ kann man ja bekannterweise die nächste passende schließende bzw. geöffnete Klammer finden. Mit Umschalt+Strg+´ kann man diesen Bereich auch direkt markieren. Diese Funktion ist nicht unbedingt eine Neuigkeit.

Viele wissen jedoch nicht, dass man auch passende #if, #ifdef, #ifndef, #else, #elif, #endif Direktiven damit anspringen kann. Man muss dazu das Caret einfach nur auf die entsprechende Direktive platzieren und mit Strg+´ springt man zum nächsten #else, #endif oder wieder an den Kopf des ersten #if Blockes.

Auch in diesem Fall kann man auf einfache Weise mit Umschalt+Strg+´ den entsprechenden Codeblock markieren.

VS-Tipps & Tricks: Die Combobox im Property Fenster des Dialog Editors

Unscheinbar aber sehr effektiv zum Ändern von großen Dialogen ist die Combobox im Eigenschaftsfenster.
In den meisten Fällen steht in dieser Combobox nur der Name des Objektes, das ausgewählt ist und eine weitere Auswahl ist dann oft hier nicht möglich.

Editiert man jedoch einen Dialog, dann kann man mit dieser Combobox, sehr effektiv und schnell Controls gezielt auswählen. Besonders wenn man Controls übereinander legt weil diese ein oder ausgeblendet werden wird es schwierig diese Controls gezielt mit der Maus zu markieren.
Mit der Combobox kein Problem.
Die Reihenfolge in der Combobox ist die alphabetisch sortiert nach den Namen der Control IDs. Auch das erleichtert die Auswahl, wenn man ein Control direkt anhand seiner ID finden will. Hat man den Dialog nicht designed und will ein Control identifizieren, dass durch einen bestimmten Code gesteuert wird, ist das mit dieser Combobox ein Klax.

Auch wenn man die Maus nicht verwenden will kann man mit dieser Combobox schnell navigieren:
Alt+Eingabetaste, wählt das Eigenschaftsfenster, mit 3 x Tab oder 2 x Umschalt+Tab kommt man in die Combobox, die man dann mit den Cursor-Tasten, bzw. Alt+Pfeilrunter bedienen kann. Mit der Escape-Taste kommt man zurück in die Editoroberfläche.

VS Tipps & Tricks: Kontextmenü für Refactor Befehle in VA-X ab Build 1557

Die Refactoring Befehle in VA-X sind ja schon länger bekannt und sind wirklich ein prima Feature (Build 1534 seit 09.2006).

Nur war es manchmal etwas trickreich an diese Befehle zu kommen. Meistens habe ich die Maus verwendet und über dem Symbol platziert und gewartet, bis das entsprechende Dropdown Symbol aus VA-X erschien. Intuitiv und schnell war das nicht.

Seit dem Build 1557 (052007) finden sich die Refactoring Befehle direkt als erster Menüpunkt im normalen Kontextmenü und sind damit auch über die Tastatur direkt mit der Kontextmenütaste (oder Umschalt+F10) zu erreichen. Ein Grund mehr sich diese Funktionen wirklich mal anzusehen. Sie sind wirklich einen Tipp wert!
Meine Favoriten im Refactoring Menü sind immer wieder Find References und Rename.

Und für alle die meinen ich habe was vergessen oder übersehen: 😉

  • Es gab zwar seit dem Build 1540 einen entsprechenden Befehl den man einem Hotkey zuordnen konnte, aber allzu viele Hotkeys passen in meinen Kopf auch nicht rein.
  • Ja und man kam an diese Befehle auch über Umschalt+Kontextmenütaste erhalten. Nur ist das komplette VA-X Menü leider etwas unaufgeräumt und groß.