InputManager忽略从菜单启动时的剪切/复制/粘贴
我正在使用InputManager检查控件的更改是由用户还是代码完成。这工作正常,除非用户使用上下文菜单进行剪切/复制/粘贴。如果用户在文本框中执行ctrl + v,InputManager会正确地通知它。但是,如果粘贴是从文本框的上下文菜单中完成的,则InputManager不会触发PreNotifyInput或PostNotifyInput事件。有人知道为什么或者如何检测这些用户操作?以下是一个工作示例。由于PreNotifyInput从不触发,所以用户在上面的文本框中使用剪切/复制/粘贴菜单时,下面的文本块永远不会更新。InputManager忽略从菜单启动时的剪切/复制/粘贴
XAML:
<Window x:Class="InputMgrDemo.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="300" Width="300">
<StackPanel>
<TextBox TextChanged="TextBox_TextChanged" />
<TextBlock Name="_text" />
</StackPanel>
</Window>
后面的代码:
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
namespace InputMgrDemo
{
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
InputManager.Current.PreNotifyInput += ((sender, e) => _userInput = true);
InputManager.Current.PostNotifyInput += ((sender, args) => _userInput = false);
}
private void TextBox_TextChanged(object sender, TextChangedEventArgs e)
{
if (_userInput)
{
_text.Text = (sender as TextBox).Text;
}
}
private bool _userInput;
}
}
其实PreNotifyInput事件确实对的MouseLeftButtonUp事件火灾,但随后PostNotifyInput火灾实际粘贴发生之前。
下面是操作顺序:
- 用户释放该菜单项
- 你PreNotifyInput事件处理函数
- 的的MouseLeftButtonUp事件引发的鼠标按钮,向上冒泡MenuItem
- MenuItem处理MouseButtonUp并将其转换为OnClick
- OnClick引发一个Prev iewClickEvent,然后调度调度员回调,以提高点击事件并执行命令,因为的MouseLeftButtonUp事件被处理
你PostNotifyInput事件处理函数
由调遣安排任何渲染完成
调度程序调用MenuItem中的回调
- MenuItem触发Click事件,该操作不会执行
- 菜单项执行粘贴命令
- 文本框处理粘贴命令和粘贴的数据
- 文本框触发TextChanged事件
在WPF“用户输入”的效果可以通过调度程序回调随意延迟,等等,所以你不知道一个变化是否是由用户输入引起的。
实际上,从理论上讲,这一般是正确的。考虑以下情况:
- 用户单击应用程序中其他位置的按钮,这会导致要加载的新数据更新您的值。
- 用户单击中的另一个应用程序中的按钮,该应用程序写入文件,导致应用程序刷新并显示新数据。
- 用户走到另一台计算机并更新某个网站上的某些数据。您的应用程序正在监控此网站并检测到此更改。
显然,在每种情况下,更改都是由用户输入引起的;-)您是否看到我要去哪里?在哲学上,没有根本的方法来决定是否由用户或其他人做出更改。
如果你真正想要的是“用户点击鼠标或使用这个应用程序和应用程序进入空闲状态时键盘的时间之间发生的任何变化,”你可以实现这一点:
InputManager.Current.PreNotifyInput += (sender, e) =>
{
_userInput = true;
Dispatcher.BeginInvoke(DispatcherPriority.ApplicationIdle, new Action(() =>
{
_userInput = false;
}));
};
但是在这种情况下,如果您有来自外部Feed的动态数据,它可能会被错误地视为用户输入。
另一种方法是翻转你的上下颠倒:任何时候你从外部数据源刷新数据,设置一个标志说你这样做。然后,每当您看到未设置该标志的更改时,就认为它是用户交互。如果您可以确保所有外部数据更新发生在DispatcherPriority.Render之上,这可能会更容易实现。
感谢您的(一如既往)详细的答案。它帮助我理解了这个问题。我很好奇,你怎么知道命令是通过调度器回调完成的? – 2010-06-14 16:51:01
在TextChanged事件中设置断点,然后查看调用堆栈。 TextChanged由TextBoxBase调用,由数据绑定代码调用,该代码由MenuItem单击处理代码调用,该代码由Dispatcher调用。由于MenuItem点击处理代码由Dispatcher调用,因此您知道MenuItem使用了调度程序回调。 – 2010-06-14 17:29:11
您需要禁用“Just My Code”并显示所有堆栈帧才能看到此内容。我建议你总是这样做 - 通过WPF的堆栈跟踪可以提供有关事情如何工作的有价值的线索。 – 2010-06-14 17:30:04