2010-06-10 70 views
0

我正在使用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; 
    } 
} 

回答

2

其實PreNotifyInput事件確實對的MouseLeftButtonUp事件火災,但隨後PostNotifyInput火災實際粘貼發生之前。

下面是操作順序:

  • 用戶釋放該菜單項
  • 你PreNotifyInput事件處理函數
  • 的的MouseLeftButtonUp事件引發的鼠標按鈕,向上冒泡MenuItem
  • MenuItem處理MouseButtonUp並將其轉換爲OnClick
  • OnClick引發一個Prev iewClickEvent,然後調度調度員回調,以提高點擊事件並執行命令,因爲的MouseLeftButtonUp事件被處理

  • 你PostNotifyInput事件處理函數

  • 由調遣安排任何渲染完成

  • 調度程序調用MenuItem中的回調

  • MenuItem觸發Click事件,該操作不會執行
  • 菜單項執行粘貼命令
  • 文本框處理粘貼命令和粘貼的數據
  • 文本框觸發TextChanged事件

在WPF「用戶輸入」的效果可以通過調度程序回調隨意延遲,等等,所以你不知道一個變化是否是由用戶輸入引起的。

實際上,從理論上講,這一般是正確的。考慮以下情況:

  1. 用戶單擊應用程序中其他位置的按鈕,這會導致要加載的新數據更新您的值。
  2. 用戶單擊中的另一個應用程序中的按鈕,該應用程序寫入文件,導致應用程序刷新並顯示新數據。
  3. 用戶走到另一臺計算機並更新某個網站上的某些數據。您的應用程序正在監控此網站並檢測到此更改。

顯然,在每種情況下,更改都是由用戶輸入引起的;-)您是否看到我要去哪裏?在哲學上,沒有根本的方法來決定是否由用戶或其他人做出更改。

如果你真正想要的是「用戶點擊鼠標或使用這個應用程序和應用程序進入空閒狀態時鍵盤的時間之間發生的任何變化,」你可以實現這一點:

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

但是在這種情況下,如果您有來自外部Feed的動態數據,它可能會被錯誤地視爲用戶輸入。

另一種方法是翻轉你的上下顛倒:任何時候你從外部數據源刷新數據,設置一個標誌說你這樣做。然後,每當您看到未設置該標誌的更改時,就認爲它是用戶交互。如果您可以確保所有外部數據更新發生在DispatcherPriority.Render之上,這可能會更容易實現。

+0

感謝您的(一如既往)詳細的答案。它幫助我理解了這個問題。我很好奇,你怎麼知道命令是通過調度器回調完成的? – 2010-06-14 16:51:01

+0

在TextChanged事件中設置斷點,然後查看調用堆棧。 TextChanged由TextBoxBase調用,由數據綁定代碼調用,該代碼由MenuItem單擊處理代碼調用,該代碼由Dispatcher調用。由於MenuItem點擊處理代碼由Dispatcher調用,因此您知道MenuItem使用了調度程序回調。 – 2010-06-14 17:29:11

+0

您需要禁用「Just My Code」並顯示所有堆棧幀才能看到此內容。我建議你總是這樣做 - 通過WPF的堆棧跟蹤可以提供有關事情如何工作的有價值的線索。 – 2010-06-14 17:30:04

相關問題