Wie lösen Sie dieses Problem mit LostFocus / LostKeyboardFocus?

8

Ok, ich habe ein Steuerelement mit einer IsEditing-Eigenschaft, die aus Gründen des Arguments eine Standardvorlage hat, die normalerweise ein Textblock ist, aber wenn IsEditing wahr ist, tauscht sie ein Textfeld für die direkte Bearbeitung aus. Jetzt, wenn das Steuerelement den Fokus verliert, sollte es, wenn es noch bearbeitet, den Bearbeitungsmodus verlassen und in der TextBlock-Vorlage zurück wechseln. Ziemlich geradlinig, richtig?

Denken Sie an das Verhalten beim Umbenennen einer Datei in Windows Explorer oder auf Ihrem Desktop (das ist das gleiche, was ich weiß ...) Das ist das Verhalten, das wir wollen.

Das Problem ist, dass Sie das LostFocus-Ereignis nicht verwenden können, da LostFocus nicht ausgelöst wird, wenn Sie zu einem anderen Fenster wechseln (oder einem Element, das ein FocusManager ist), da das Steuerelement immer noch einen logischen Fokus hat / p>

Wenn Sie stattdessen LostKeyboardFocus verwenden, während dies das Problem "anderes FocusManager" löst, haben Sie jetzt ein neues: Wenn Sie gerade bearbeiten und Sie mit der rechten Maustaste auf das Textfeld klicken, wird das Kontextmenü angezeigt jetzt hat Tastaturfokus, verliert Ihre Kontrolle Tastaturfokus, fällt aus Bearbeitungsmodus und schließt das Kontextmenü, verwirrend den Benutzer!

Jetzt habe ich versucht, ein Flag zu setzen, um den LostKeyboardFocus zu ignorieren, kurz bevor das Menü geöffnet wird, und dann dieses Fiag im LostKeyboardFocus-Ereignis zu verwenden, um es aus dem Bearbeitungsmodus herauszutreten oder nicht, aber wenn das Menü geöffnet ist und ich klicke An anderer Stelle in der App, da das Steuerelement selbst keinen Tastaturfokus mehr hatte (das Menü hatte es), erhält das Steuerelement nie ein weiteres LostKeyboardFocus-Ereignis, so dass es im Bearbeitungsmodus bleibt. (Möglicherweise muss ich einen Haken hinzufügen, wenn das Menü geschlossen wird, um zu sehen, was den Fokus hat, und dann manuell aus EditMode herauskicken, wenn es nicht das Steuerelement ist. Das scheint vielversprechend.)

Also ... hat jemand eine Idee, wie ich dieses Verhalten erfolgreich programmieren kann?

Markieren

    
MarqueIV 01.05.2011, 19:31
quelle

6 Antworten

8

Ok ... das war "Spaß" wie beim Programmierer-Spaß. Ein echter Schmerz im Keester, um es herauszufinden, aber mit einem schönen, riesigen Lächeln auf meinem Gesicht, das ich tat. (Zeit, etwas IcyHot für meine Schulter zu bekommen, wenn ich bedenke, dass ich es selbst so hart klopfe!: P)

Wie auch immer, es ist eine mehrstufige Sache, aber es ist überraschend einfach, wenn Sie alles herausgefunden haben. Die kurze Version ist, dass Sie beide LostFocus und LostKeyboardFocus verwenden müssen, nicht die eine oder die andere.

LostFocus ist einfach. Wenn Sie dieses Ereignis erhalten, setzen Sie IsEditing auf false. Fertig und fertig.

Kontextmenüs und verlorener Tastaturfokus

LostKeyboardFocus ist etwas komplizierter, da das Kontextmenü für Ihr Steuerelement das Steuerelement auf dem Steuerelement selbst auslösen kann (dh wenn das Kontextmenü für Ihr Steuerelement geöffnet wird, hat das Steuerelement immer noch den Fokus, verliert jedoch den Tastaturfokus und somit LostKeyboardFocus feuert.)

Um dieses Verhalten zu behandeln, überschreiben Sie ContextMenuOpening (oder behandeln das Ereignis) und legen ein Flag auf Klassenebene fest, das angibt, dass das Menü geöffnet wird. (Ich benutze bool _ContextMenuIsOpening .) Dann in der LostKeyboardFocus override (oder Ereignis), überprüfen Sie das Flag und wenn es gesetzt ist, löschen Sie es einfach und nichts anderes tun. Wenn dies jedoch nicht festgelegt ist, bedeutet dies, dass neben dem Öffnen des Kontextmenüs das Steuerelement den Tastaturfokus verliert. In diesem Fall möchten Sie IsEditing auf false setzen.

Bereits geöffnete Kontextmenüs

Jetzt gibt es ein seltsames Verhalten, dass, wenn das Kontextmenü für ein Steuerelement geöffnet ist und das Steuerelement den Tastaturfokus bereits verloren hat, wie oben beschrieben, wenn Sie auf eine andere Stelle in der Anwendung klicken, bevor das neue Steuerelement den Fokus erhält Tastaturfokus zuerst, aber nur für den Bruchteil einer Sekunde, dann gibt er es sofort an das neue Steuerelement zurück.

Dies funktioniert tatsächlich zu unserem Vorteil, da dies bedeutet, dass wir auch ein weiteres LostKeyboardFocus -Ereignis erhalten, aber dieses Mal wird das _ContextMenuOpening-Flag auf false gesetzt und genau wie oben beschrieben, wird unser LostKeyboardFocus -Handler% setzen co_de% zu false, was genau das ist, was wir wollen. Ich liebe Serendipity!

Wenn nun der Fokus einfach auf das Steuerelement verschoben wurde, auf das Sie geklickt haben, ohne zuerst den Fokus auf das Steuerelement mit dem Kontextmenü zurückzusetzen, müssen wir das IsEditing -Ereignis einhängen und überprüfen, welches Steuerelement es ist Wenn wir den Fokus als nächstes bekommen, dann würden wir ContextMenuClosing nur auf false setzen, wenn das Steuerelement, das demnächst in den Fokus gestellt wird, nicht das Kontextmenü ist, also haben wir dort eine Kugel ausgewichen.

Vorbehalt: Standardkontextmenüs

Jetzt gibt es auch die Einschränkung, dass wenn Sie etwas wie eine Textbox verwenden und nicht explizit Ihr eigenes Kontextmenü darauf gesetzt haben, dann nicht das IsEditing -Ereignis, welches überraschte mich. Dies kann jedoch leicht behoben werden, indem einfach ein neues Kontextmenü mit denselben Standardbefehlen wie das Standardkontextmenü (z. B. Ausschneiden, Kopieren, Einfügen usw.) erstellt und der Textbox zugewiesen wird. Es sieht genau so aus, aber jetzt bekommen Sie das Ereignis, das Sie brauchen, um die Flagge zu setzen.

Aber selbst dort haben Sie ein Problem, als würden Sie ein wiederverwendbares Steuerelement eines Drittanbieters erstellen und der Benutzer dieses Steuerelements möchte ein eigenes Kontextmenü haben. Sie könnten versehentlich Ihre höhere Priorität festlegen und Sie " Überschreibe ihre!

Da das Textfeld eigentlich ein Element in der Vorlage ContextMenuOpening für mein Steuerelement ist, habe ich einfach ein neues DP auf dem äußeren Steuerelement namens IsEditing hinzugefügt, das ich dann über ein internes% -Angebot an das Textfeld anbinde. co_de% style, dann habe ich einen IsEditingContextMenu in diesem Stil hinzugefügt, der den Wert von TextBox auf dem äußeren Steuerelement überprüft. Wenn es null ist, setze ich das oben erstellte Standardmenü, das in einer Ressource gespeichert ist.

Hier ist der interne Stil für das Textfeld (Das Element namens "Root" repräsentiert das äußere Steuerelement, das der Benutzer tatsächlich in sein XAML einfügt) ...

%Vor%

Beachten Sie, dass Sie die anfängliche Kontext-Menü-Bindung im Stil festlegen müssen, nicht direkt in der Textbox, sonst wird der DataTrigger des Stils durch den direkt gesetzten Wert ersetzt, wodurch der Trigger unbrauchbar wird und Sie sofort wieder auf Platz eins sind Die Person verwendet 'Null' für das Kontextmenü. (Wenn Sie das Menü unterdrücken möchten, würden Sie 'null' sowieso nicht verwenden. Sie würden es auf ein leeres Menü setzen, da Null 'Standard verwenden' bedeutet)

Nun kann der Benutzer die normale Eigenschaft DataTrigger verwenden, wenn IsEditingContextMenu falsch ist ... sie können ContextMenu verwenden, wenn IsEditing wahr ist, und wenn sie kein IsEditing angegeben haben, das interne Der von uns definierte Standardwert wird für das Textfeld verwendet. Da das Kontextmenü des Textfelds niemals null sein kann, wird immer IsEditingContextMenu ausgelöst, und daher funktioniert die Logik zur Unterstützung dieses Verhaltens.

Wie ich schon sagte ... ECHTER Schmerz in der Dose, das alles herauszufinden, aber verdammt, wenn ich hier kein wirklich cooles Gefühl der Erfüllung habe.

Ich hoffe, das hilft anderen hier mit dem gleichen Problem. Fühlen Sie sich frei, hier zu antworten, oder schicken Sie mir Fragen.

Markieren

    
MarqueIV 02.05.2011, 00:54
quelle
3

Sie suchen leider eine einfache Lösung für ein komplexes Problem. Das Problem, das einfach gesagt wird, ist, intelligente Auto-Commit-Benutzeroberflächensteuerelemente zu haben, die ein Minimum an Interaktion erfordern und "das Richtige tun", wenn Sie von ihnen "wegschalten".

Der Grund, warum es komplex ist, liegt darin, dass das, was richtig ist, vom Kontext der Anwendung abhängt. Der Ansatz von WPF besteht darin, Ihnen logische Fokus- und Tastaturfokuskonzepte zu geben und Ihnen die Entscheidung zu überlassen, wie Sie in Ihrer Situation das Richtige für Sie tun können.

Was passiert, wenn das Kontextmenü geöffnet wird? Was passiert, wenn das Anwendungsmenü geöffnet wird? Was passiert, wenn der Fokus auf eine andere Anwendung umgeschaltet wird? Was passiert, wenn ein Popup geöffnet wird, das zum lokalen Steuerelement gehört? Was passiert, wenn der Benutzer die Eingabetaste drückt, um einen Dialog zu schließen? All diese Situationen können gehandhabt werden, aber sie verschwinden alle, wenn Sie einen Commit-Button haben oder der Benutzer Enter zum Commit drücken muss.

Sie haben also drei Möglichkeiten:

  • Lassen Sie das Steuerelement im Bearbeitungszustand, wenn es den logischen Fokus hat
  • Fügen Sie einen expliziten Commit- oder Apply-Mechanismus hinzu
  • Behandle alle unordentlichen Fälle, die auftreten, wenn du versuchst, Auto-Commit zu unterstützen
Rick Sladkey 01.05.2011 22:17
quelle
0

Ich bin mir über das Problem mit dem Kontextmenü nicht sicher, aber ich habe versucht, etwas Ähnliches zu tun und festgestellt, dass die Verwendung der Mauserfassung Ihnen (fast) das Verhalten gibt, nach dem Sie suchen:

Siehe die Antwort hier: Wie kann ein Steuerelement einen Mausklick außerhalb dieses Steuerelements handhaben?

    
Jonny Leeds 01.02.2013 11:02
quelle
0

Nicht sicher, aber das könnte hilfreich sein. Ich hatte ein ähnliches Problem mit der Editable-Combobox. Mein Problem war, dass ich OnLostFocus Override-Methode verwendete, die nicht aufgerufen wurde. Fix hatte ich einen Rückruf an LostFocus Event angebracht und es hat alles gut funktioniert.

    
gauravvc 05.02.2015 01:03
quelle
0

Ich bin hier auf meiner Suche nach einer Lösung für ein ähnliches Problem durchgegangen: Ich habe eine ListBox , die den Fokus verliert, wenn ContextMenu geöffnet wird, und das möchte ich nicht.

Meine einfache Lösung bestand darin, Focusable auf False zu setzen, sowohl für ContextMenu als auch für MenuItem s:

%Vor%

Hoffe das hilft zukünftigen Suchenden ...

    
TheDark 23.06.2015 18:05
quelle
0

Wäre es nicht einfach einfacher:

%Vor%     
Saragani 11.05.2016 04:53
quelle