Frage

OK, ich habe eine Steuerung, die eine ieditierende Eigenschaft hat, die aus Arguments willen über eine Standardvorlage verfügt, die normalerweise einen Textblock ist. Wenn es jedoch wahr ist, wechselt sie in einem Textfeld zur Einstellung zur Bearbeitung von Platzierung. Wenn die Kontrolle nun den Fokus verliert, soll es, wenn sie noch bearbeitet, aus dem Bearbeitungsmodus abfallen und in die Textblock -Vorlage zurückzutauschen. Ziemlich einfach, oder?

Denken Sie an das Verhalten der Umbenennung 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, wenn Sie zu einem anderen Fenster (oder einem FocusManager) wechseln, nicht ausfeuert, da die Kontrolle noch logisch fokussiert ist, sodass dies nicht funktioniert.

Wenn Sie stattdessen LostKeyboardFocus verwenden, während Sie das Problem "Anderer FocusManager" lösen, haben Sie jetzt eine neue: Wenn Sie bearbeiten und mit der rechten Maustaste auf das Textfeld klicken, um das Kontextmenü anzuzeigen, da das Kontextmenü jetzt die Tastatur enthält Focus, Ihr Steuerelement verliert den Tastaturfokus, fällt aus dem Bearbeitungsmodus und schließt das Kontextmenü, verwirrt den Benutzer!

Jetzt habe ich versucht, ein Flag festzulegen, um den LostKeyboardFocus kurz vor dem Öffnen des Menüs zu ignorieren, und diese Fiag im Ereignis von LostKeyboardFocus zu verwenden, um zu bestimmen, es aus dem Bearbeitungsmodus herauszulassen oder nicht. 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 eine Überprüfung hinzufügen, wenn das Menü schließt, um zu sehen, was Fokus hat, dann manuell aus Editmode herausschmeißen, wenn es nicht die Kontrolle ist. Das erscheint vielversprechend.)

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

Markieren

War es hilfreich?

Lösung

Ok ... das hat "Spaß" wie in Programmierer-Fun gemacht. Ein echter Schmerz im Keester, um es herauszufinden, aber mit einem schönen riesigen Lächeln im Gesicht, das ich tat. (Zeit, etwas Icyhot für meine Schulter zu bekommen, wenn man bedenkt, dass ich es so hart habe!: P)

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

LostFocus ist einfach. Wann immer Sie dieses Ereignis erhalten, setzen Sie IsEditing zu falsch. Gemacht und gemacht.

Kontextmenüs und verlorener Tastaturfokus

LostKeyboardFocus ist etwas schwieriger, da das Kontextmenü für Ihre Steuerung das auf die Steuerung selbst abfeuern kann (dh wenn das Kontextmenü für Ihre Steuerung geöffnet ist, hat die Steuerung immer noch Fokus, verliert jedoch den Tastaturfokus und somit. LostKeyboardFocus Feuer.)

Um mit diesem Verhalten umzugehen, überschreiben Sie ContextMenuOpening (oder verarbeiten Sie das Ereignis) und setzen Sie ein Flag auf Klassenebene, das angibt, dass das Menü geöffnet wird. (Ich benutze bool _ContextMenuIsOpening.) Dann in der LostKeyboardFocus Überschreiben (oder Ereignis), Sie überprüfen dieses Flag und wenn es festgelegt ist, löschen Sie es einfach und tun nichts anderes. Wenn es jedoch nicht festgelegt ist, bedeutet dies, dass etwas anderes als die Öffnung des Kontextmenüs dazu führt, dass die Steuerung den Tastaturfokus verliert. In diesem Fall möchten Sie also festlegen IsEditing zu falsch.

Bereits offene Kontextmenüs

Jetzt gibt es ein seltsames Verhalten, dass, wenn das Kontextmenü für ein Steuerelement geöffnet ist und das Steuerelement bereits wie oben beschrieben den Fokus der Tastatur verloren hat , aber nur für einen Sekundenbruchteil, dann gibt es es sofort der neuen Kontrolle.

Dies funktioniert hier tatsächlich zu unserem Vorteil, da dies bedeutet, dass wir auch einen anderen bekommen werden LostKeyboardFocus Ereignis, aber diesmal wird das Flag _contextMenuopening auf false und genau wie oben beschrieben, unsere LostKeyboardFocus Handler wird dann eingestellt IsEditing zu falsch, was genau das ist, was wir wollen. Ich liebe Serendipity!

Jetzt ließ sich der Fokus einfach auf die Steuerung verschoben, auf die Sie geklickt haben ContextMenuClosing Ereignis und Überprüfung, welche Kontrolle als nächstes Fokus erhalten wird, dann haben wir nur festgelegt IsEditing zu falsch, wenn die bald fokussierte Kontrolle nicht diejenige war, die das Kontextmenü hervorbrachte, also haben wir dort im Grunde aus einer Kugel ausgewichen.

Vorbehalt: Standardkontextmenüs

Jetzt gibt es auch die Einschränkung, dass Sie, wenn Sie so etwas wie ein Textfeld verwenden und Ihr eigenes Kontextmenü nicht explizit darauf einstellen, dann Sie nicht bekommen das ContextMenuOpening Ereignis, was mich überraschte. Dies ist jedoch leicht zu reparieren, indem einfach ein neues Kontextmenü mit denselben Standardbefehlen wie das Standard -Kontextmenü (z. B. Schnitt, Kopieren, Einfügen usw.) erstellt und dem Textfeld zugewiesen wird. Es sieht genauso aus, aber jetzt erhalten Sie das Ereignis, das Sie zum Einstellen der Flagge benötigen.

Selbst dort haben Sie jedoch ein Problem, als ob Sie eine von Drittanbietern respektable Kontrolle erstellen und der Benutzer dieser Steuerung ein eigenes Kontextmenü haben möchte. Möglicherweise setzen Sie Ihr versehentlich auf einen höheren Vorrang und Sie überschreiben ihre, die ihre außerordentlich überschreiben. !

Der Weg, das war, da die Textbox tatsächlich ein Element in der ist IsEditing Vorlage für meine Kontrolle, ich habe einfach einen neuen DP auf der äußeren Steuerung hinzugefügt, die aufgerufen wurde IsEditingContextMenu was ich dann über ein intern an das Textfeld binden TextBox Stil, dann habe ich a hinzugefügt DataTrigger in diesem Stil, der den Wert von überprüft IsEditingContextMenu Bei der äußeren Steuerung und wenn es sich null handelt, habe ich das von mir oben erstellte Standardmenü festgelegt, das in einer Ressource gespeichert ist.

Hier ist der interne Stil für das Textfeld (das Element mit dem Namen 'Root' repräsentiert die äußere Steuerung, die der Benutzer tatsächlich in seinen XAML einfügt) ...

<Style x:Key="InlineTextbox" TargetType="TextBox">

    <Setter Property="OverridesDefaultStyle" Value="True"/>
    <Setter Property="FocusVisualStyle"      Value="{x:Null}" />
    <Setter Property="ContextMenu"           Value="{Binding IsEditingContextMenu, ElementName=Root}" />

    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type TextBoxBase}">

                <Border Background="White" BorderBrush="LightGray" BorderThickness="1" CornerRadius="1">
                    <ScrollViewer x:Name="PART_ContentHost" />
                </Border>

            </ControlTemplate>
        </Setter.Value>
    </Setter>

    <Style.Triggers>
        <DataTrigger Binding="{Binding IsEditingContextMenu, RelativeSource={RelativeSource AncestorType=local:EditableTextBlock}}" Value="{x:Null}">
            <Setter Property="ContextMenu">
                <Setter.Value>
                    <ContextMenu>
                        <MenuItem Command="ApplicationCommands.Cut" />
                        <MenuItem Command="ApplicationCommands.Copy" />
                        <MenuItem Command="ApplicationCommands.Paste" />
                    </ContextMenu>
                </Setter.Value>
            </Setter>
        </DataTrigger>
    </Style.Triggers>

</Style>

Beachten Sie, dass Sie das anfängliche Kontextmenü-Bindung im Stil festlegen müssen, nicht direkt in der Textbox oder der DataTrigger des Stils wird durch den direkt gesetzten Wert ersetzt, der den Trigger unbrauchbar macht, und Sie sind direkt wieder auf Platz eins, wenn die Person verwendet 'null' für das Kontextmenü. (Wenn Sie das Menü unterdrücken möchten, würden Sie 'null' sowieso nicht verwenden.

Jetzt kann der Benutzer den regulären Gebrauch verwenden ContextMenu Eigentum wann IsEditing ist falsch ... sie können die verwenden IsEditingContextMenu Wann ist Isediting wahr und wenn sie keine angegeben haben IsEditingContextMenu, Die interne Standardeinstellung, die wir definiert haben, wird für das Textfeld verwendet. Da das Kontextmenü der Textbox niemals null sein kann, ist es sein ContextMenuOpening Feuert immer und daher funktioniert die Logik, um dieses Verhalten zu unterstützen.

Wie ich schon sagte ... echte Schmerzen in der Dose, die alles herausfinden, aber verdammt, wenn ich hier kein wirklich cooles Gefühl der Leistung habe.

Ich hoffe, dies hilft anderen hier mit dem gleichen Problem. Fühlen Sie sich frei, hier zu antworten oder mich mit Fragen zu schalten.

Markieren

Andere Tipps

Leider suchen Sie nach einer einfachen Lösung für ein komplexes Problem. Das Problem ist einfach, dass Smart Auto-Commiting-Benutzeroberflächen-Steuerelemente, die ein Minimum an Interaktion erfordern, und "das Richtige tun", wenn Sie von ihnen "wegschalten".

Der Grund, warum es komplex ist, ist, dass das Richtige vom Kontext der Anwendung abhängt. Der Ansatz, den WPF verfolgt, besteht darin, Ihnen den logischen Fokus- und Tastaturfokuskonzepten zu geben und Sie zu entscheiden, wie Sie in Ihrer Situation das Richtige für Sie tun können.

Was ist, wenn das Kontextmenü geöffnet wird? Was sollte passieren, wenn das Anwendungsmenü geöffnet wird? Was ist, wenn der Fokus auf eine andere Anwendung umgestellt wird? Was ist, wenn ein Popup zur lokalen Kontrolle geöffnet wird? Was ist, wenn der Benutzer die Eingabetaste drückt, um einen Dialog zu schließen? Alle diese Situationen können behandelt werden, aber sie verschwinden, wenn Sie eine Commit -Taste haben oder der Benutzer die Eingabetaste drücken muss, um festzulegen.

Sie haben also drei Möglichkeiten:

  • Lassen Sie die Kontrolle im Bearbeitungszustand bleiben, wenn sie den logischen Fokus hat
  • Fügen Sie einen expliziten Commit hinzu oder wenden Sie Mechanismus an
  • Behandeln Sie alle unordentlichen Fälle, die entstehen, wenn Sie versuchen, Auto-Commit zu unterstützen

Wäre es nicht einfach einfacher:

    void txtBox_LostKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
    {
        TextBox txtBox = (sender as TextBox);

        if (e.NewFocus is ContextMenu && (e.NewFocus as ContextMenu).PlacementTarget == txtBox)
        {
            return;
        }

        // Rest of code for existing edit mode here...
    }

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

Siehe die Antwort hier: Wie kann ein Steuerelement eine Maus umgehen, klicken Sie auf diese Steuerung?

Ich bin mir nicht sicher, aber das könnte hilfreich sein. Ich hatte ein ähnliches Problem mit der editablen Kombination. Mein Problem war, dass ich die Onlostfocus -Override -Methode verwendet habe, die nicht aufgerufen wurde. Fix war, dass ich einen Rückruf an das Ereignis von LostFocus beigefügt hatte und es alles gut funktionierte.

Ich habe hier bei meiner Suche nach einer Lösung für ein ähnliches Problem durchgeführt: Ich habe eine ListBox was den Fokus verliert, wenn die ContextMenu öffnet sich, und ich möchte nicht, dass das passiert.

Meine einfache Lösung war es zu setzen Focusable zu False, beide für die ContextMenu und sein MenuItems:

<ContextMenu x:Key="QueryResultsMenu" Focusable="False">
    <ContextMenu.Resources>
        <Style TargetType="MenuItem">
            <Setter Property="Focusable" Value="False"/>
        </Style>
    </ContextMenu.Resources>
    <MenuItem ... />
</ContextMenu>

Hoffe das hilft zukünftigen Suchenden ...

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top