XAML WPF KeyBinding吞入密钥,防止使用TextBox

6tqwzwtp  于 2022-12-07  发布在  其他
关注(0)|答案(4)|浏览(145)

问题概述:

在高于TextBox的级别上定义的任何KeyBinding(未分配修饰键),将阻止用户在TextBox中键入这些键。

最小XAML层次结构:

<Window>
  <UserControl>
    <Border>
      <UserControl>
        <TextBox>

最小命令/键绑定:

<UserControl.Resources>
    <RoutedUICommand x:Key="Commands.SomeCommand" />
</UserControl.Resources>
<UserControl.InputBindings>
    <KeyBinding Key="A" Command="{StaticResource Commands.SomeCommand}" />
</UserControl.InputBindings>
<UserControl.CommandBindings>
    <CommandBinding Command="{StaticResource Commands.SomeCommand}" Executed="..." />
</UserControl.CommandBindings>

CommandKeyBinding是在firstUserControl级别定义的,所以在这个例子中,在文本框中,用户可以自由输入,直到他们按下A键,然后它就不把字母插入文本框了。我可以清楚地看到当你按下A键时TextBox.KeyDownTextBox.PreviewKeyDown正在发射(和Handled = false),但字母不会添加到文本框的文本中,并且TextBox.PreviewTextInput不会触发。
我正在寻找任何建议,这些建议可能会指出是什么吞噬了按键并阻止TextBox处理它,或者与如何调试此问题相关的任何建议。

编辑:

多亏了Snoop,我才能清楚地看到问题所在。

  1. TextBox.PreviewKeyDown向下通过隧道并通过可视树触发,从Window开始,到TextBox结束
  2. TextBox.KeyDown从TextBox开始向窗口方向返回
  3. TextBox.KeyDown会由第一个设定KeyBinding的UserControl将Handled设定为true。
  4. TextBox.PreviewTextInput永远不会引发,文字方块也不会行程输入,因为KeyDown事件已设定为已行程。
    这仍然留下了一个问题,如果文本框具有焦点,如何防止UserControl处理输入?在命令执行中,我可以检查文本框是否具有键盘焦点,但此时已经太晚了。
jum4pzuy

jum4pzuy1#

TextInputPreviewTextInput仅在文本实际更改/可能更改时触发。
当您更新问题以反映时,Command会拦截事件,且永远不会引发(Preview)TextInput事件。
最好的解决方案是在KeyBinding中添加一个修饰键,但我怀疑这不是您的首选方法。
另一种方法是对TextBox上的PreviewKeyDown事件执行e.Handle操作,然后自己引发TextComposition事件,方法如下:

target.RaiseEvent(new TextCompositionEventArgs(InputManager.Current.PrimaryKeyboardDevice, 
new TextComposition(InputManager.Current, target, "A")) 
{ 
    RoutedEvent = TextCompositionManager.TextInputEvent 
});

(或者,在正确的CaretIndex处插入textBox.Text
说实话,这仍然是一个黑客。

pbgvytdp

pbgvytdp2#

我也有同样的问题。我看了一下key bindind的文档,上面描述了,绑定的键不应该只是key,而是key gesture,所以它应该是

  • 辅助键+普通键
  • 数字键盘按键
  • 功能键。

当然,它只适用于A,但总体来说这是一种糟糕的做法。您应该考虑实现后面提到的一些可能性。更多信息请访问https://msdn.microsoft.com/cs-cz/library/system.windows.input.keybinding(v=vs.110).aspx

lkaoscv7

lkaoscv73#

我已经使用TextComposition RaiseEvent方法很多年了,但是这似乎破坏了非拉丁键盘布局(如西里尔字母)的输入。
执行此操作的正确方法是从InputBinding派生,如果事件源自文本框,则在Matches?检查中返回false。

/// <summary>
/// This gesture doesn't handle keys originating in a text control. This allows key bindings without modifier keys
/// that don't break normal typing. A standard KeyGesture doesn't have such logic; this allows the parent of a
/// text box to handle such bare keypresses before the textbox gets to see it as normal text input, thus breaking
/// normal typing.
/// </summary>
public class BareKeyGesture : InputGesture
{
    public Key Key { get; set; }

    public override bool Matches(object targetElement, InputEventArgs inputEventArgs)
    {
        var keyEventArgs = inputEventArgs as KeyEventArgs;
        if (keyEventArgs == null)
            return false;

        if (inputEventArgs.OriginalSource is TextBoxBase)
            return false;

        return (int)Key == (int)keyEventArgs.Key && Keyboard.Modifiers == ModifierKeys.None;
    }
}

/// <summary>
/// This only exists because the InputBinding constructor is protected, but since we have to have it anyway
/// we also use this opportunity to simplify adding a BareKeyGesture to it.
/// </summary>
public class BareKeyBinding : InputBinding
{
    private BareKeyGesture _gesture = new();

    public BareKeyBinding()
    {
        Gesture = _gesture;
    }

    public Key Key
    {
        get => _gesture.Key;
        set { _gesture.Key = value; }
    }
}

现在您有了一个InputGesture,它将忽略来自文本框的事件,您可以像平常一样在XAML中使用它:

<UserControl.InputBindings>
    <nsp:BareKeyBinding
        Key="D"
        Command="{StaticResource Commands.YourCommand}"
        CommandParameter="None" />
</UserControl.InputBindings>
pgccezyw

pgccezyw4#

只要你使用KeyBinding,这将不会工作,没有主要的黑客。我实现的解决方案是:
1.使用KeyDown事件来捕获那些被按下的键(而不是KeyBindings)。这将在代码隐藏中,从那里你需要打开被按下的键来调用所需的命令(在你的例子中是SomeCommand)。
1.现在你有了一个不同的问题。TextBox正在获取输入,但是你的键绑定命令也在触发。在后面的代码中,检查keyEventArgs.InputSource的类型,如果是TextBox,则忽略击键。
它应该如下所示:

private void OnKeyDown(object sender, KeyEventArgs e)
{
    ICommand command = null;

    switch (e.Key)
    {
        case Key.A:
            command = Commands.SomeCommand;
            break;
        case Key.B:
            command = Commands.SomeOtherCommand;
            break;
    }

    bool isSourceATextBox = e.InputSource.GetType() == typeof(TextBox);
    if (command != null && !isSourceATextBox)
    {
        command.Execute(parameter:null);
    }
}

相关问题