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 事件之间,有一系列事件 应该 发生但不是:
- PreviewInputReport/InputReport(无来源)
- PreviewTextInputStart / TextInputStart(文本框)
- PreviewTextInput/TextInput(文本框)
- 预览输入报告/输入报告(文本框视图)
没有处理 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)。
话虽如此,我建议执行以下操作:
- 使用
InputManager.Current
订阅其 PreProcessInput
and/or PostProcessInput
事件(也可选择 PreNotifyInput
和 PostNotifyInput
)
- 记录工作场景中的事件链(特别是检查 args'
StagingItem.Input.RoutedEvent
,其中包含当前正在处理的路由事件)
- 对不工作的场景重复该过程
- 指出第一个区别 - 第一个路由事件在工作场景中被处理,但在非工作场景中没有被处理
- 调查最后一个公共路由事件和第一个不同的路由事件 - 也许其中一个是在您的代码中处理的?
在这种情况下,事实证明在不工作的情况下启动 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
我有一个 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 事件之间,有一系列事件 应该 发生但不是:
- PreviewInputReport/InputReport(无来源)
- PreviewTextInputStart / TextInputStart(文本框)
- PreviewTextInput/TextInput(文本框)
- 预览输入报告/输入报告(文本框视图)
没有处理 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)。
话虽如此,我建议执行以下操作:
- 使用
InputManager.Current
订阅其PreProcessInput
and/orPostProcessInput
事件(也可选择PreNotifyInput
和PostNotifyInput
) - 记录工作场景中的事件链(特别是检查 args'
StagingItem.Input.RoutedEvent
,其中包含当前正在处理的路由事件) - 对不工作的场景重复该过程
- 指出第一个区别 - 第一个路由事件在工作场景中被处理,但在非工作场景中没有被处理
- 调查最后一个公共路由事件和第一个不同的路由事件 - 也许其中一个是在您的代码中处理的?
在这种情况下,事实证明在不工作的情况下启动 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在他的回答中所写,这就是区别,但是,如果您需要调用非模态。
给出的答案是这个
ElementHost.EnableModelessKeyboardInterop(wpfWindow);
再次感谢V.Leon