wake-up-neo.com

KeyBinding in UserControl funktioniert nicht, wenn TextBox den Fokus hat

Die folgende Situation. Ich habe ein UserControl mit fünf Tastenkombinationen. Wenn das Textfeld den Fokus hat, werden die Tastenkombinationen von UserControl nicht mehr ausgelöst.

Gibt es eine Möglichkeit, dieses "Problem" zu beheben?

<UserControl.InputBindings>
    <KeyBinding Key="PageDown" Modifiers="Control" Command="{Binding NextCommand}"></KeyBinding>
    <KeyBinding Key="PageUp" Modifiers="Control" Command="{Binding PreviousCommand}"></KeyBinding>
    <KeyBinding Key="End" Modifiers="Control"  Command="{Binding LastCommand}"></KeyBinding>
    <KeyBinding Key="Home" Modifiers="Control" Command="{Binding FirstCommand}"></KeyBinding>
    <KeyBinding Key="F" Modifiers="Control" Command="{Binding SetFocusCommand}"></KeyBinding>
</UserControl.InputBindings>
<TextBox Text="{Binding FilterText, UpdateSourceTrigger=PropertyChanged}">
    <TextBox.InputBindings>
        <KeyBinding Gesture="Enter" Command="{Binding RelativeSource={RelativeSource AncestorType={x:Type UserControl }}, Path=DataContext.FilterCommand}"></KeyBinding>
    </TextBox.InputBindings>
</TextBox>

Es scheint Funktionstasten (F1 etc) und ALT+[key] arbeite. Ich nehme das an CTRL und SHIFT Modifikatoren "blockieren" irgendwie das Hochsprudeln des Ereignisses zum UserControl.

20
Ralf de Kleine

Der Grund, warum einige Eingabebindungen funktionieren und andere nicht, ist, dass das TextBox-Steuerelement einige Schlüsselbindungen abfängt und verarbeitet. Zum Beispiel behandelt es CTRL+V für paste, CTRL+Home um an den Anfang des Textes zu springen usw. Andere Tastenkombinationen wie CTRL+F3 Auf der anderen Seite werden sie nicht von der TextBox verarbeitet und sprudeln daher in die Luft.

Wenn Sie nur die Eingabebindung der TextBox deaktivieren möchten, wäre das ganz einfach - Sie könnten den Befehl ApplicationCommands.NotACommand verwenden, der das Standardverhalten deaktiviert. Zum Beispiel im folgenden Fall, Einfügen mit CTRL+V wird deaktiviert:

<TextBox>
    <TextBox.InputBindings>
        <KeyBinding Key="V" Modifiers="Control" Command="ApplicationCommands.NotACommand" />
    </TextBox.InputBindings>
</TextBox>

Es ist jedoch etwas schwieriger, die Benutzersteuerung in die Luft zu sprudeln. Mein Vorschlag ist, ein angefügtes Verhalten zu erstellen, das auf das UserControl angewendet wird, sich für sein Ereignis PreviewKeyDown zu registrieren und seine Eingabebindungen nach Bedarf auszuführen, bevor sie das Textfeld erreichen. Dies gibt dem UserControl Vorrang, wenn Eingabebindungen ausgeführt werden.

Ich habe ein grundlegendes Verhalten geschrieben, das diese Funktionalität bietet, um Ihnen den Einstieg zu erleichtern:

public class InputBindingsBehavior
{
    public static readonly DependencyProperty TakesInputBindingPrecedenceProperty =
        DependencyProperty.RegisterAttached("TakesInputBindingPrecedence", typeof(bool), typeof(InputBindingsBehavior), new UIPropertyMetadata(false, OnTakesInputBindingPrecedenceChanged));

    public static bool GetTakesInputBindingPrecedence(UIElement obj)
    {
        return (bool)obj.GetValue(TakesInputBindingPrecedenceProperty);
    }

    public static void SetTakesInputBindingPrecedence(UIElement obj, bool value)
    {
        obj.SetValue(TakesInputBindingPrecedenceProperty, value);
    }

    private static void OnTakesInputBindingPrecedenceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        ((UIElement)d).PreviewKeyDown += new KeyEventHandler(InputBindingsBehavior_PreviewKeyDown);
    }

    private static void InputBindingsBehavior_PreviewKeyDown(object sender, KeyEventArgs e)
    {
        var uielement = (UIElement)sender;

        var foundBinding = uielement.InputBindings
            .OfType<KeyBinding>()
            .FirstOrDefault(kb => kb.Key == e.Key && kb.Modifiers == e.KeyboardDevice.Modifiers);

        if (foundBinding != null)
        {
            e.Handled = true;
            if (foundBinding.Command.CanExecute(foundBinding.CommandParameter))
            {
                foundBinding.Command.Execute(foundBinding.CommandParameter);
            }
        }
    }
}

Verwendungszweck:

<UserControl local:InputBindingsBehavior.TakesInputBindingPrecedence="True">
    <UserControl.InputBindings>
        <KeyBinding Key="Home" Modifiers="Control" Command="{Binding MyCommand}" />
    </UserControl.InputBindings>
    <TextBox ... />
</UserControl>

Hoffe das hilft.

44
Adi Lester

Die Lösung von Adi Lester funktioniert gut. Hier eine ähnliche Lösung mit Behavior: Der C # -Code:

public class AcceptKeyBinding : Behavior<UIElement>
{


    private TextBox _textBox;



    /// <summary>
    ///  Subscribes to the PreviewKeyDown event of the <see cref="TextBox"/>.
    /// </summary>
    protected override void OnAttached()
    {
        base.OnAttached();

        _textBox = AssociatedObject as TextBox;

        if (_textBox == null)
        {
            return;
        }

        _textBox.PreviewKeyDown += TextBoxOnPreviewKeyDown;
    }

    private void TextBoxOnPreviewKeyDown(object sender, KeyEventArgs keyEventArgs)
    {
        var uielement = (UIElement)sender;

        var foundBinding = uielement.InputBindings
            .OfType<KeyBinding>()
            .FirstOrDefault(kb => kb.Key == keyEventArgs.Key && kb.Modifiers ==           keyEventArgs.KeyboardDevice.Modifiers);

        if (foundBinding != null)
        {
            keyEventArgs.Handled = true;
            if (foundBinding.Command.CanExecute(foundBinding.CommandParameter))
            {
                foundBinding.Command.Execute(foundBinding.CommandParameter);
            }
        }
    }

    /// <summary>
    ///     Unsubscribes to the PreviewKeyDown event of the <see cref="TextBox"/>.
    /// </summary>
    protected override void OnDetaching()
    {
        if (_textBox == null)
        {
            return;
        }

        _textBox.PreviewKeyDown -= TextBoxOnPreviewKeyDown;

        base.OnDetaching();
    }

}

Und das XAML:

<TextBox>
  <TextBox.InputBindings>
      <KeyBinding Key="Enter" Modifiers="Shift" Command="{Binding CommandManager[ExecuteCommand]}"
          CommandParameter="{Binding ExecuteText}" />
  </TextBox.InputBindings>
      <i:Interaction.Behaviors>
         <behaviours:AcceptKeyBinding />
      </i:Interaction.Behaviors>
</TextBox>
4
JF Moreau

Neben Adi Lester, seiner (sehr hilfreichen) Antwort, möchte ich einige Verbesserungen/Erweiterungen vorschlagen, die mir bei meiner Implementierung geholfen haben.

Gesture.Matches

FoundBinding kann auch durch Aufrufen von Gesture.Matches ausgeführt werden. Ändern Sie die Abfrage foundBinding Linq wie folgt:

KeyBinding foundBinding = ((UIElement)this).InputBindings
            .OfType<KeyBinding>()
            .FirstOrDefault(inputBinding => inputBinding.Gesture.Matches(sender, eventArgs));

Mausbindung

Außerdem können Sie MouseBindings definieren.

<MouseBinding Command="{Binding DataContext.AddInputValueCommand, ElementName=root}" CommandParameter="{Binding}" Gesture="Shift+MiddleClick" />

Sie müssen dann PreviewMouseEvents abonnieren, z. PreviewMouseUp und PreviewMouseDoubleClick. Die Implementierung ist dann fast dieselbe wie für KeyBindings.

private void OnTextBoxPreviewMouseUp(object sender, MouseButtonEventArgs eventArgs)
{
    MouseBinding foundBinding = ((UIElement)this).InputBindings
        .OfType<MouseBinding>()
        .FirstOrDefault(inputBinding => inputBinding.Gesture.Matches(sender, eventArgs));

    if (foundBinding != null)
    {
        eventArgs.Handled = true;
        if (foundBinding.Command.CanExecute(foundBinding.CommandParameter))
        {
            foundBinding.Command.Execute(foundBinding.CommandParameter);
        }
    }
}
1
J. Voermans
<UserControl.Style>
    <Style TargetType="UserControl">
        <Style.Triggers>
            <Trigger Property="IsKeyboardFocusWithin" Value="True">
                <Setter Property="FocusManager.FocusedElement" Value="   {Binding ElementName=keyPressPlaceHoler}" />
                </Trigger>
        </Style.Triggers>
    </Style>
</UserControl.Style>

keyPressPlaceHoler ist der Name des Containers Ihres Ziels uielement

denken Sie daran, den Focusable="True" in usercontrol einzustellen

0
mao