从菜单启动时,InputManager忽略剪切/复制/粘贴

时间:2010-06-10 21:41:09

标签: wpf user-input

我正在使用InputManager来检查控件的更改是由用户还是代码完成的。这种方法很好,除非用户使用上下文菜单进行剪切/复制/粘贴。如果用户在文本框中执行ctrl + v,则InputManager会正确注意它。但是,如果粘贴是从文本框的上下文菜单中完成的,则InputManager永远不会触发PreNotifyInput或PostNotifyInput事件。谁知道为什么?或者如何检测这些用户操作?以下是一份工作样本。当用户在上面的文本框中使用剪切/复制/粘贴菜单时,下面的文本块永远不会更新,因为PreNotifyInput永远不会触发。

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;
    }
}

1 个答案:

答案 0 :(得分:2)

实际上,PreNotifyInput事件会触发MouseLeftButtonUp事件,但PostNotifyInput会在实际粘贴发生之前触发。

以下是操作顺序:

  • 用户在菜单项
  • 上释放鼠标按钮
  • 您的PreNotifyInput事件处理程序称为
  • 引发MouseLeftButtonUp事件,该事件冒泡到MenuItem
  • MenuItem处理MouseButtonUp并将其转换为OnClick
  • OnClick引发PreviewClickEvent,然后安排调度程序回调以引发Click事件并执行命令
  • 您的PostNotifyInput事件处理程序被称为,因为处理了MouseLeftButtonUp事件

  • Dispatcher安排的任何渲染都会完成

  • Dispatcher在MenuItem中调用回调

  • MenuItem触发Click事件,该事件不执行任何操作
  • MenuItem执行粘贴命令
  • TextBox处理粘贴命令并粘贴数据
  • TextBox触发TextChanged事件

在WPF中,“用户输入”的影响可以通过调度程序回调等任意延迟,因此您无法知道更改是否是由用户输入引起的。

事实上,从理论上讲,这一般都是正确的。请考虑以下情况:

  1. 用户单击应用程序中其他位置的按钮,这会导致加载新数据,从而更新您的值。
  2. 用户单击另一个应用程序中的按钮,该按钮会写入文件,从而导致应用程序刷新并显示新数据。
  3. 用户走到另一台计算机并在某处更新网站上的某些数据。您的应用程序正在监控此网站并检测到更改。
  4. 显然,在每种情况下,变化都是由用户输入引起的;-)你看到我要去哪里吗?从哲学角度来说,没有根本的方法来决定您的用户或其他人是否进行了更改。

    如果您真正想要的是“在用户单击鼠标或在此应用程序中使用键盘与应用程序空闲时间之间发生的任何更改”,您可以实现此目的:

    InputManager.Current.PreNotifyInput += (sender, e) =>
    {
      _userInput = true;
      Dispatcher.BeginInvoke(DispatcherPriority.ApplicationIdle, new Action(() =>
      {
        _userInput = false;
      }));
    }; 
    

    但是在这种情况下,如果您有来自外部Feed的动态数据,则可能会错误地将其视为用户输入。

    另一种方法是将您的内容颠倒过来:每当您从外部数据源刷新数据时,请设置一个标记,说明您正在这样做。然后,只要您看到未设置该标志的更改,您就会认为这是用户交互。如果您可以确保所有外部数据更新都发生在DispatcherPriority.Render之上,那么这可能更容易实现。