wpf 当 TextBox 具有焦点时,UserControl 中的 KeyBinding 不起作用

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/12941707/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-08 23:33:22  来源:igfitidea点击:

KeyBinding in UserControl doesn't work when TextBox has the focus

wpfxamlmvvmfocusinputbinding

提问by Ralf de Kleine

The following situation. I've got a UserControl with five keybindings. When the TextBox has the focus the keybindings of the UserControl stop firing..

以下情况。我有一个带有五个键绑定的 UserControl。当 TextBox 具有焦点时,UserControl 的键绑定停止触发..

Is there a way to fix this 'problem'?

有没有办法解决这个“问题”?

<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>

It seems function keys (F1etc) and ALT+[key]do work. I presume the CTRLand SHIFTmodifiers are somehow 'blocking' the event from bubbling up to the UserControl.

似乎功能键(F1等)和ALT+[key]可以工作。我认为CTRLSHIFT修饰符以某种方式“阻止”事件冒泡到 UserControl。

回答by Adi Lester

The reason some input bindings work and some don't is that the TextBox control catches and handles some key bindings. For example, it handles CTRL+Vfor paste, CTRL+Homefor going to the beginning of the text, etc. Other key combinations such as CTRL+F3on the other hand aren't handled by the TextBox, and so they will bubble up.

有些输入绑定有效而有些无效的原因是 TextBox 控件捕获并处理一些键绑定。例如,它处理CTRL+V用于粘贴,CTRL+Home用于转到文本的开头等。另一方面,其他组合键(例如CTRL+)F3不由 TextBox 处理,因此它们会冒泡。

If you'd just wanted to disable the TextBox's input binding, that would be simple - you could use the ApplicationCommands.NotACommandcommand, which would disable the default behavior. For example, in the following case, pasting with CTRL+Vwill be disabled:

如果您只想禁用 TextBox 的输入绑定,那将很简单 - 您可以使用该ApplicationCommands.NotACommand命令,该命令将禁用默认行为。例如,在以下情况下,将禁用CTRL+粘贴V

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

However, making it bubble up to the user control is a bit trickier. My suggestion is to create an attached behavior that will be applied to the UserControl, register to its PreviewKeyDownevent, and execute its input bindings as necessary before they reach the TextBox. This will give precedence to the UserControl when input bindings are executed.

然而,让它冒泡到用户控件有点棘手。我的建议是创建一个附加行为,该行为将应用于 UserControl,注册到其PreviewKeyDown事件,并在它们到达 TextBox 之前根据需要执行其输入绑定。这将在执行输入绑定时优先考虑 UserControl。

I wrote a basic behavior that achieves this functionality to get you started:

我编写了一个实现此功能的基本行为来帮助您入门:

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);
            }
        }
    }
}

Usage:

用法:

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

Hope this helps.

希望这可以帮助。

回答by JF Moreau

Adi Lester's solution works well. Here's a similar solution using Behavior. The C# code:

Adi Lester 的解决方案效果很好。这是使用 Behavior 的类似解决方案。C# 代码:

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();
    }

}

And the XAML:

和 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>

回答by J. Voermans

In addition to Adi Lester his (very helpful) answer I would like to suggest some improvements/extensions that helped me with my implementation.

除了 Adi Lester 他(非常有帮助)的回答之外,我还想提出一些有助于我实施的改进/扩展建议。

Gesture.Matches

手势匹配

The foundBinding can also be done by calling Gesture.Matches. Change the foundBinding Linq query to the following:

foundBinding 也可以通过调用 Gesture.Matches 来完成。将 foundBinding Linq 查询更改为以下内容:

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

MouseBinding

鼠标绑定

Furthermore you can also define MouseBindings.

此外,您还可以定义 MouseBindings。

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

You then also need to subscribe to PreviewMouseEvents e.g. PreviewMouseUp and PreviewMouseDoubleClick. The implementation is then almost the same as for KeyBindings.

然后,您还需要订阅 PreviewMouseEvents,例如 PreviewMouseUp 和 PreviewMouseDoubleClick。实现几乎与 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);
        }
    }
}

回答by Franek Stark

This Thread is old but many have this problem. My research has shown, that Adi Lester's Solution ist the only one which isn't a "dirty" Workaround. For anayone who needs, the VisualBasic.NET Implemantation:

这个线程很旧,但很多人都有这个问题。我的研究表明,Adi Lester 的解决方案是唯一一个不是“肮脏”的解决方法。对于需要的人,VisualBasic.NET 实现:

Public Class InputBindingsBehavior
    Public Shared ReadOnly TakesInputBindingPrecedenceProperty As DependencyProperty = DependencyProperty.RegisterAttached("TakesInputBindingPrecedence", GetType(Boolean), GetType(InputBindingsBehavior), New UIPropertyMetadata(False, AddressOf OnTakesInputBindingPrecedenceChanged))

    Public Shared Function GetTakesInputBindingPrecedence(obj As UIElement) As Boolean
        Return obj.GetValue(TakesInputBindingPrecedenceProperty)
    End Function

    Public Shared Sub SetTakesInputBindingPrecedence(obj As UIElement, value As Boolean)
        obj.SetValue(TakesInputBindingPrecedenceProperty, value)
    End Sub

    Public Shared Sub OnTakesInputBindingPrecedenceChanged(d As DependencyObject, e As DependencyPropertyChangedEventArgs)
        AddHandler DirectCast(d, UIElement).PreviewKeyDown, AddressOf InputBindingsBehavior_PreviewKeyDown
    End Sub

    Public Shared Sub InputBindingsBehavior_PreviewKeyDown(sender As Object, e As KeyEventArgs)
        Dim uielement = DirectCast(sender, UIElement)

        Dim foundBinding = uielement.InputBindings.OfType(Of KeyBinding).FirstOrDefault(Function(kb As KeyBinding) kb.Key = e.Key And kb.Modifiers = e.KeyboardDevice.Modifiers)

        If foundBinding IsNot Nothing Then
            e.Handled = True
            If foundBinding.Command.CanExecute(foundBinding.CommandParameter) Then
                foundBinding.Command.Execute(foundBinding.CommandParameter)
            End If
        End If
    End Sub

End Class

The rest as mentioned.

其余的如前所述。

回答by mao

<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>

keyPressPlaceHoleris the name of container of your target uielement

keyPressPlaceHoler是目标容器的名称 uielement

remember to set the Focusable="True"in usercontrol

记得Focusable="True"在用户控件中设置