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
事件(也可选择PreNotifyInput
和PostNotifyInput
)
记录工作场景中的事件链(特别是检查args的StagingItem.Input.RoutedEvent
,它保存了当前正在处理的路由事件)
对不工作的场景重复该过程
找出第一个区别 - 第一个路由事件在工作场景中被处理,但在非工作场景中未被处理
调查最后一个常见的路由事件和第一个不同的事件 - 也许其中一个已在您的代码中处理?
【讨论】:
所以,我将 PreviewTextInput 处理程序放在可视化树的每个级别。当我从有效的调用中打开窗口时,我可以看到事件从窗口向下传输到文本框。当我从不工作的地方打开窗口时,PreviewTextInput 事件不会在任何级别触发。有趣的旁注;在这两种情况下,空格按钮都不会触发 PreviewTextInput 事件,但可以在两种情况下都插入空格。 您能否明确说明哪些情况会导致哪些结果?如果您使用 this AddHandler overload 订阅PreviewTextInput
并且它没有被调用,那么就会发生一些非常奇怪的事情 - 您的代码中有一个讨厌的 hack,或者 .NET 框架中确实存在一个错误。
只是一个快速检查列表:1)您将处理程序添加到子窗口内的 TextBox
2)您在 TextBox
上调用 AddHandler
method 而不是使用VB.NET 的AddHandler
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 事件的主要内容,如果未能解决你的问题,请参考以下文章