wpf 文本框不会抛出 textInput 事件

wpf textbox won't throw textInput event

我有一个 window,其中的文本框在键入时不会抛出 textInput 事件。

我一直在用 Snooper 看它。仅引发 KeyDown 和 KeyUp 事件。

响应几个键:Space、退格、插入、主页、删除、结束

它响应复制和粘贴命令,以及Select全部

它不响应任何字符、符号或数字

关键在于: 这个 window 是通过从代码中的两个不同位置调用的共享方法打开的。当从一个位置调用时,文本框工作完美,当从其他位置调用时,它没有。

我已经排除了绑定、数据转换器、样式、控件位置。 我将 window 剥离为只有一个没有绑定的纯文本框,但问题仍然存在。

我已经尽我所能去追踪这个神秘的错误。在 previewTextInput 甚至被抛出之前,我看不到什么可以处理我的事件,或者为什么它可能只发生一半的时间。

任何关于此错误的原因的想法,或我可以尝试跟踪事件的其他工具,将不胜感激!

编辑: 添加一些代码来演示。这已被简化为所需的最简单代码,但问题仍然存在。

<Window x:Class="EventViewEmail"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Width="880" Height="600">

    <TextBox VerticalAlignment="Top"/>
</Window>

注意缺少绑定、样式或任何其他可能干扰控件的东西

Public Class EventViewEmail
    'all code removed from the view-behind'
End Class   

这是构建 window 的静态 class。对 class 的两次单独调用以不同方式构建参数。我删除了使用参数的代码,以表明它们不会影响手头的问题。

Public Class EventManager
    Public Shared Sub Show(e As EventEdit, p As WorkplanPageViewModel)
        Dim w = New EventViewEmail
        If w Is Nothing Then Return

        'removed datacontext for testing'
        'w.DataContext = e '
        'w.Tag = p'
        w.Show()
    End Sub
End Class

我唯一可以补充的是,调用 Show() sub 的代码来自两个独立的解决方案。不确定在删除所有参数后可能会产生什么影响

编辑 2: 我一直在尝试跟踪事件序列以缩小事件处理的范围。到目前为止,我可以看到在 keyDown 和 keyUp 事件之间,有一系列事件 应该 发生但不是:

没有处理 keydown 事件,所以我不确定为什么没有触发 PreviewInputReport

问题是当 TextInput 被触发时(没有抛出...抛出异常)您没有看到它,因为文本框本身正在使用它。要捕获此事件,您需要使用 PreviewTextInput。例如

    <TextBox TextInput="UIElement_OnTextInput" PreviewTextInput="UIElement_OnPreviewTextInput"></TextBox>

使用事件处理程序...

    private void UIElement_OnTextInput(object sender, TextCompositionEventArgs e)
    {
        Console.WriteLine($"In text input event, {e.Text}");
    }

    private void UIElement_OnPreviewTextInput(object sender, TextCompositionEventArgs e)
    {
        Console.WriteLine($"In preview text input event, {e.Text}");
    }

从不调用第一个处理程序,始终调用第二个处理程序。

免责声明

我会 post C# 代码,因为我的 VB 不够流利,无法编写代码(只能在有限的范围内阅读)。如果我有时间,我会尝试翻译它,或者你们中的任何人都可以随意翻译。


我通过处理 window 上的可视化树上的 PreviewTextInput 事件成功地重现了您描述的行为(记住这是一个隧道路由事件):

<Window (...)>
    <StackPanel>
        <TextBox x:Name="myTextBox"></TextBox>
    </StackPanel>
</Window>
public partial class MainWindow
{
    public MainWindow()
    {
        InitializeComponent();
        PreviewTextInput += (s, e) => e.Handled = true;
    }
}

因此,在我看来,您的问题的起因很可能是它。它还解释了 您的 PreviewTextInput 处理程序未被调用。您可以通过不同的订阅来确认情况是否如此(即使事件被标记为已处理,处理程序也会被调用):

public partial class MainWindow
{
    public MainWindow()
    {
        InitializeComponent();
        PreviewTextInput += (s, e) => e.Handled = true;
        myTextBox.AddHandler(
            routedEvent: PreviewTextInputEvent,
            handler: new TextCompositionEventHandler((s, e) =>
            {
                if (e.Handled)
                    throw new Exception("Gotcha!");
            }),
            handledEventsToo: true);
    }
}

如果事实证明这是正确的诊断,那么有一件事有点神秘 - 谁在何处处理 PreviewTextInput 事件?我想那是给你调查的...

可能会派上用场:

Look for global event handlers like EventManager.RegisterClassHandler(typeof(TextBox), UIElement.PreviewTextInputEvent, ...) and/or behaviors. Are you using any third-party UI library? Look in their code as well.


更新

因为 PreviewTextInput 似乎不是罪魁祸首,所以接下来我会这样做。

如果我没记错的话,在 TextInputEvent 之前会触发一整套事件,我相信处理其中任何一个都可能会破坏该链条。它看起来也像 InputManager 负责管理这个事件周期(你可以检查它的 C# 源代码 here)。

话虽如此,我建议执行以下操作:

  1. 使用 InputManager.Current 订阅其 PreProcessInput and/or PostProcessInput 事件(也可选择 PreNotifyInputPostNotifyInput
  2. 记录工作场景中的事件链(特别是检查 args' StagingItem.Input.RoutedEvent,其中包含当前正在处理的路由事件)
  3. 对不工作的场景重复该过程
  4. 指出第一个区别 - 第一个路由事件在工作场景中被处理,但在非工作场景中没有被处理
  5. 调查最后一个公共路由事件和第一个不同的路由事件 - 也许其中一个是在您的代码中处理的?

在这种情况下,事实证明在不工作的情况下启动 window 的表单是一个 winform。 winform 正在阻止击键。这是通过将 window 作为模式打开来解决的。

您的处理程序未被调用,因为其他处理程序已将事件标记为已处理。您可以使用 AddHandler() 添加处理程序并为第三个参数传递 true

public MainWindow()
{
    var handler = new RoutedEventHandler(OnTextInput);
    myTextBox.AddHandler(TextInputEvent, handler, handledEventsToo: true);
}

private static void OnTextInput(object sender, RoutedEventArgs e)
{
}

我在这里遇到了同样的问题。两次调用之间的区别是:一次 WPF-Windows 被称为模态(有效),一次是非模态(无效),每次都来自 WindowsForms Window.

正如Shaboboo在他的回答中所写,这就是区别,但是,如果您需要调用非模态。 给出的答案是这个。 (愚蠢的我,我在 2 年前遇到了同样的问题,询问并得到了答案)。我们必须使用

ElementHost.EnableModelessKeyboardInterop(wpfWindow);

再次感谢V.Leon