2011-04-22 16 views
10

팝업 또는 메뉴와 유사한 컨트롤이 있습니다. 그것을 표시하고 사용자가 상자 경계를 클릭하면 숨길 수 있습니다. 나는 Mouse.Capture (this, CaptureMode.SubTree)를 사용하고 Menu/Popup이 OnLostMouseCapture에서와 같은 방식으로 캡처를 다시 획득했습니다.마우스 캡쳐 해제 및 마우스 클릭 통과

사용자가 컨트롤 경계를 클릭하면 OnPreviewMouseDown에서 마우스 캡처를 해제합니다. 나는 e.Handled를 true로 설정하지 않았습니다. 마우스를 클릭하면 기본 UI의 다른 컨트롤로 이동하지만 윈도우의 닫기 버튼 (빨간색 X)은 클릭하지 않습니다. 앱을 닫으려면 2 번 클릭해야합니다.

WPF에 마우스 클릭을 다시 시작하거나 반복 된 마우스 클릭 이벤트를 보내도록 지시하는 방법이 있습니까?

여기 내 코드가 있습니다. 참고 MainMenuControl으로 이름을 변경했습니다. Menu를 작성하지 않으므로 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가 아닌) 올바른 동작을 제공합니다. 감사! –