wpf 文本框不会抛出 textInput 事件

Posted

技术标签:

【中文标题】wpf 文本框不会抛出 textInput 事件【英文标题】:wpf textbox won't throw textInput event 【发布时间】:2019-12-11 11:34:24 【问题描述】:

我有一个带有文本框的窗口,该窗口在键入时不会引发 textInput 事件。

我一直在用 Snooper 研究它。只有 KeyDown 和 KeyUp 事件被抛出。

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

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

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

这里是关键:这个窗口是通过一个从代码中两个不同位置调用的共享方法打开的。当从一个位置调用时,文本框可以正常工作,而从其他位置调用时则不能。

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

我已经尽我所能来追查这个神秘的错误。在 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   

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

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() 子的代码来自两个不同的解决方案。不确定在我删除所有参数后可能会产生什么影响

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

PreviewInputReport / InputReport(无来源) 预览TextInputStart / TextInputStart(文本框) 预览文本输入/文本输入(文本框) PreviewInputReport / InputReport (textboxView)

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

【问题讨论】:

请发布相关代码和/或一些示例,以便我们为您提供帮助,以便我们尝试重现问题;现在成为会员 10 多年了,您应该知道这一点 :) “当以一种方式打开时,文本框可以完美地工作,当以另一种方式打开时,它不会。” 啊,一种方式。但反过来呢?很好读。 @Shaboboo 我建议从 .net 框架下载调试符号并从最底层开始。 如果您想要一些想法,这是我的一个想法 - 也许您在处理此事件的解决方案之一中具有全局 Window 样式?尝试在您的 Show 方法中设置 w.Style = New Style 寻找像EventManager.RegisterClassHandler(typeof(TextBox), UIElement.PreviewTextInputEvent, ...)这样的全局事件处理程序和/或行为。您是否使用任何第三方 UI 库?看看他们的代码。 【参考方案1】:

免责声明

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


我已经设法通过在窗口上的可视树上处理PreviewTextInput 事件来重现您描述的行为(记住它是一个隧道路由事件):

<Window (...)>
    <StackPanel>
        <TextBox x:Name="myTextBox"></TextBox>
    </StackPanel>
</Window>
public partial class MainWindow

    public MainWindow()
    
        InitializeComponent();
        PreviewTextInput += (s, e) => e.Handled = true;
    

所以在我看来,这是您问题原因的强烈怀疑。它还解释了 the comment to @AQuirky's answer 你的 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 事件?我想这是让你调查的......

This comment from @l33t 可能会派上用场:

查找全局事件处理程序,例如 EventManager.RegisterClassHandler(typeof(TextBox), UIElement.PreviewTextInputEvent, ...) 和/或行为。您是否使用任何第三方 UI 库?看看他们的代码。


更新

既然PreviewTextInput 似乎毕竟不是罪魁祸首,这就是我接下来要做的事情。

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

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

    使用InputManager.Current 订阅其PreProcessInput 和/或PostProcessInput 事件(也可选择PreNotifyInputPostNotifyInput) 记录工作场景中的事件链(特别是检查args的StagingItem.Input.RoutedEvent,它保存了当前正在处理的路由事件) 对不工作的场景重复该过程 找出第一个区别 - 第一个路由事件在工作场景中被处理,但在非工作场景中未被处理 调查最后一个常见的路由事件和第一个不同的事件 - 也许其中一个已在您的代码中处理?

【讨论】:

所以,我将 PreviewTextInput 处理程序放在可视化树的每个级别。当我从有效的调用中打开窗口时,我可以看到事件从窗口向下传输到文本框。当我从不工作的地方打开窗口时,PreviewTextInput 事件不会在任何级别触发。有趣的旁注;在这两种情况下,空格按钮都不会触发 PreviewTextInput 事件,但可以在两种情况下都插入空格。 您能否明确说明哪些情况会导致哪些结果?如果您使用 this AddHandler overload 订阅 PreviewTextInput 并且它没有被调用,那么就会发生一些非常奇怪的事情 - 您的代码中有一个讨厌的 hack,或者 .NET 框架中确实存在一个错误。 只是一个快速检查列表:1)您将处理程序添加到子窗口内的 TextBox 2)您在 TextBox 上调用 AddHandler method 而不是使用VB.NETAddHandler statement。对吗? 抱歉,请原谅之前的评论,我还没有完成对您的处理程序的正确测试,它正在按预期工作。那个handledEventsToo 太棒了,谢谢。还在调试中! PreviewTextInputStart 活动怎么样?我的直觉是这个事件首先被触发,如果处理,会阻止触发PreviewTextInput,但我目前无法确认这一点,因为我无法访问安装了 VS 的计算机。【参考方案2】:

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

【讨论】:

【参考方案3】:

问题是,当 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");
    

第一个处理程序永远不会被调用,第二个总是。

【讨论】:

问题是预览 TextInput 事件也没有被抛出。我们只看到 KeyDown 和 KeyUp 事件。在引发预览事件之前,有东西正在中断事件链 *被解雇,没有被抛出,对不起 我从未见过 PreviewTextInput 不起作用的情况。放入事件处理程序。它会起作用的。不要依赖你的工具。他们在告诉你错误的故事。如果您只是重新开始...使用您发布的代码...它将起作用。现在,如果它仍然无法在您的目标应用程序中运行,则意味着问题出在您尚未发布的代码中,因此我或其他任何人都可以帮助您。,【参考方案4】:

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

public MainWindow()

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


private static void OnTextInput(object sender, RoutedEventArgs e)


【讨论】:

【参考方案5】:

我在这里遇到了同样的问题。这两个调用之间的区别是:一次 WPF-Windows 被称为模态(工作)和一次非模态(不起作用),每次都来自 WindowsForms 窗口。

正如Shaboboo 在他的回答中所写,这就是区别,但是,如果您需要调用非模态,那又如何呢? 答案是这个thread。 (愚蠢的我,两年前我也遇到过同样的问题,问并得到了答案)。我们必须使用

ElementHost.EnableModelessKeyboardInterop(wpfWindow);

再次感谢V.Leon

【讨论】:

以上是关于wpf 文本框不会抛出 textInput 事件的主要内容,如果未能解决你的问题,请参考以下文章

在 WPF 文本框中删除文件

textInput事件

WPF 文本框没有单击事件吗?如果想用单击事件怎么处理

文本框文本在WPF中更改了事件

WPF MVVM 文本框文本绑定与 changedText 事件

WPF PropertyChanged 事件未触发/更新文本框