2011-04-22 121 views
10

我有一個類似於Popup或Menu的控件。我想顯示它,當用戶在框的邊界外單擊時,讓它自己隱藏。我已經使用Mouse.Capture(這個,CaptureMode.SubTree)以及以OnLostMouseCapture的Menu/Popup方式重新獲取捕獲。釋放鼠標捕獲並讓鼠標點擊通過

當用戶單擊控件的邊界外時,我釋放鼠標捕獲在OnPreviewMouseDown。我沒有把電話處理爲真實。鼠標單擊將使其在主UI上的其他控件上,但不會顯示在窗口的關閉按鈕(紅色X)上。它需要2次點擊關閉應用程序。

有沒有辦法告訴WPF重新啓動鼠標點擊或發送重複的鼠標點擊事件?

這是我的代碼。注意我將它重命名爲MainMenuControl - 我沒有構建菜單,所以Menu/MenuItem和Popup不是選項。

public class MainMenuControl : Control 
    { 
     static MainMenuControl() 
     { 
      DefaultStyleKeyProperty.OverrideMetadata(typeof(MainMenuControl), new FrameworkPropertyMetadata(typeof(MainMenuControl))); 
     } 

     public MainMenuControl() 
     { 
      this.Loaded += new RoutedEventHandler(MainMenuControl_Loaded); 

      Mouse.AddPreviewMouseDownOutsideCapturedElementHandler(this, OnPreviewMouseDownOutsideCapturedElementHandler); 
     } 

     void MainMenuControl_Loaded(object sender, RoutedEventArgs e) 
     { 
      this.IsVisibleChanged += new DependencyPropertyChangedEventHandler(MainMenuControl_IsVisibleChanged); 
     } 

     void MainMenuControl_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e) 
     { 
      if (this.IsVisible) 
      { 
       Mouse.Capture(this, CaptureMode.SubTree); 
       Debug.WriteLine("Mouse.Capture"); 
      } 
     } 

     // I was doing this in OnPreviewMouseDown, but changing to this didn't have any effect 
     private void OnPreviewMouseDownOutsideCapturedElementHandler(object sender, MouseButtonEventArgs e) 
     { 
      Debug.WriteLine("OnPreviewMouseDownOutsideCapturedElementHandler"); 

      if (!this.IsMouseInBounds()) 
      { 
       if (Mouse.Captured == this) 
       { 
        Mouse.Capture(this, CaptureMode.None); 
        Debug.WriteLine("Mouse.Capture released"); 
       } 
       Debug.WriteLine("Close Menu"); 
      } 
     } 

     protected override void OnLostMouseCapture(MouseEventArgs e) 
     { 
      base.OnLostMouseCapture(e); 

      Debug.WriteLine("OnLostMouseCapture"); 

      MainMenuControl reference = e.Source as MainMenuControl; 
      if (Mouse.Captured != reference) 
      { 
       if (e.OriginalSource == reference) 
       { 
        if ((Mouse.Captured == null) || (!reference.IsAncestorOf(Mouse.Captured as DependencyObject))) 
        { 
         //TODO: Close 
         Debug.WriteLine("Close Menu"); 
        } 
       } 
       // if a child caused use to lose the capture, then recapture. 
       else if (reference.IsAncestorOf(e.OriginalSource as DependencyObject)) 
       { 
        if (Mouse.Captured == null) 
        { 
         Mouse.Capture(reference, CaptureMode.SubTree); 
         Debug.WriteLine("Mouse.Capture"); 
         e.Handled = true; 
        } 
       } 
       else 
       { 
        //TODO: Close 
        Debug.WriteLine("Close Menu"); 
       } 
      } 

     } 

     private bool IsMouseInBounds() 
     { 
      Point point = Mouse.GetPosition(this); 
      Rect bounds = new Rect(0, 0, this.Width, this.Height); 

      return bounds.Contains(point); 
     } 

    } 

回答

5

的問題是,鼠標的操作,你所談論的是操作系統,以便我們真正談論的是互動不夠好大部分的大約兩個相當不同的鼠標消息隊列的WPF事件系統和部件外但是在這些邊緣情況下,我們發現互操作性並不完美。

您可以嘗試生成Win32鼠標消息或發送自己的窗口關閉消息,但所有這些方法都是黑客。由於彈出窗口和菜單展示了與您描述的完全相同的症狀,因此您似乎沒有像描述過的那樣容易地實現您想要的內容。

相反,我建議您考慮在鼠標離開窗口的北方客戶區域或其他啓發方法(如距控件指定距離)時放棄鼠標捕獲。我知道這可能並不理想,但如果您希望關閉按鈕的功能足夠好,它可能是一個令人滿意的折中方案。

+0

您的建議效果很好。我把它全部放在一個行爲中 - 當在主窗口之外移動時釋放捕獲,並訂閱App.Current.MainWindow.MouseEnter以重新捕獲鼠標。你也是對的,WPF和Windows不共享相同的消息泵 - 我比較了WPF和非WPF菜單行爲,並看到點擊的不同。您的解決方案提供了我想要的正確(非WPF)行爲。謝謝! – 2011-04-24 17:25:52