2017-10-06 14 views
0

한 핀에서 다른 핀으로 연결되는 라인 커넥터 컨트롤에서 작업하고 있습니다. 일반적인 WPF 솔루션은 사용자가 연결선을 드래그하기 시작할 때 마우스 캡처를 사용하는 것입니다. 불행히도 사용자가 유효한 핀을 넘었 으면 표시기 위에 ​​마우스가 있어야합니다. 그러나 이전에 마우스를 캡처했을 때 대상 핀이 마우스 이벤트를 가져 오지 않기 때문에 표시기가 표시되지 않습니다.다른 컨트롤이 이미 마우스를 캡처 한 경우에도 컨트롤에 대해 MouseEvents를 가져 오는 방법

<Window x:Class="WpfApp1.MainWindow" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
    xmlns:local="clr-namespace:WpfApp1" 
    mc:Ignorable="d" 
    WindowState="Maximized" 
    Title="MainWindow" Height="350" Width="525"> 
    <Canvas> 
     <CheckBox x:Name="EnableMouseCapture" IsChecked="True" Content="Enable Mouse Capture" /> 
     <Rectangle x:Name="Test" Fill="Blue" Width="40" Height="40" Canvas.Left="200" Canvas.Top="200" /> 
     <Line x:Name="Line" Stroke="Black" StrokeThickness="1" IsHitTestVisible="False" /> 
    </Canvas> 
</Window> 

그리고 파일 뒤에 코드 :

using System; 
using System.Windows.Input; 
using System.Windows.Media; 

namespace WpfApp1 
{ 
    /// <summary> 
    /// Interaction logic for MainWindow.xaml 
    /// </summary> 
    public partial class MainWindow 
    { 
     public MainWindow() 
     { 
      InitializeComponent(); 

      Test.MouseEnter += TestOnMouseEnter; 
      Test.MouseLeave += TestOnMouseLeave; 

      MouseDown += OnMouseDown; 
     } 

     private void TestOnMouseEnter(object sender, MouseEventArgs mouseEventArgs) 
     { 
      Console.WriteLine("(Test) MouseEnter"); 

      Test.Fill = Brushes.Coral; 
     } 

     private void TestOnMouseLeave(object sender, MouseEventArgs mouseEventArgs) 
     { 
      Console.WriteLine("(Test) MouseLeave"); 

      Test.Fill = Brushes.Blue; 
     } 

     private void OnMouseMove(object sender, MouseEventArgs mouseEventArgs) 
     { 
      Console.WriteLine("(Window) MouseMove"); 

      var pos = mouseEventArgs.GetPosition(this); 
      Line.X2 = pos.X; 
      Line.Y2 = pos.Y; 
     } 

     private void OnMouseDown(object sender, MouseButtonEventArgs mouseButtonEventArgs) 
     { 
      Console.WriteLine("(Window) MouseDown"); 

      MouseUp += OnMouseUp; 
      MouseMove += OnMouseMove; 

      var pos = mouseButtonEventArgs.GetPosition(this); 
      Line.X1 = pos.X; 
      Line.Y1 = pos.Y; 

      if (EnableMouseCapture.IsChecked == true) 
      { 
       CaptureMouse(); 
      } 
     } 

     private void OnMouseUp(object sender, MouseButtonEventArgs mouseButtonEventArgs) 
     { 
      Console.WriteLine("(Window) MouseUp"); 

      ReleaseMouseCapture(); 

      MouseUp -= OnMouseUp; 
      MouseMove -= OnMouseMove; 
     } 
    } 
} 

캔버스에 마우스 캡처가 활성화 된 경우, TestOnMouseEnterTestOnMouseLeave이 아닌 기능

나는 내 문제를 보여주기 위해 lighweight 샘플을 썼다 라는. 마우스 캡쳐가 비활성화되면이 두 함수가 호출됩니다. 이것이 WPF의 일반적인 동작이지만, 다른 컨트롤에 캡처 방법에 대한 정보를 얻는 방법을 알고있는 사람이 있습니까?

+0

당신이 "OnPreview ..."이벤트를 사용하여 시도? –

+0

MouseCapturing의 개념 때문에 이것이 작동하지 않을 것이라고 생각합니다. – Rod

답변

1

글쎄, 나는 그것이 MouseCapture가 코드를 깔끔하게 만들 수 있다는 것을 이해합니다. 그렇다면 마우스 캡처를 사용하지 않는 것이 어떻습니까? 객체가 마우스를 캡처 할 때 마우스 캡처 객체가 마우스 포인터가 다른 개체 위에있는 경우에도 이벤트를 수행하는 것처럼, 모든 마우스 관련 이벤트가 처리됩니다

MSDN에서 마우스 캡처

https://msdn.microsoft.com/en-us/library/ms771301.aspx

.

What does it mean to "Capture the mouse" in WPF?

마우스를 캡처하여 발표 할 때까지 만 캡처 컨트롤이 마우스 이벤트를 받기 때문에 드래그 유용합니다. 모든 드래그 코드는 여러 컨트롤에 분산되어 있지 않고 한 컨트롤에 존재할 수 있습니다.

앱이 원하는 방식으로 동작하도록 만드는 경우 사용하지 않는 것이 좋을까요? 마우스 캡처의 목적과 같은 소리는 당신이 성취하려는 것을 무너 뜨립니다. 당신이 모양을 가지고 있고 싶으면

내가 너무 이와 비슷한 질문을 발견 :

How to fire MouseEnter for one object if another object has mousecapture?

편집 : 여기에 예를 들어 있었나요 있지만 제대로 작동하지 않았다, 기본적으로 다음을 수행해야 수동으로 테스트를 누르고 마우스 입력을 트리거하고 마우스를 수동으로 종료하십시오.

+0

빠른 답변 주셔서 감사합니다. 나는 너의 제안을 점검 할 것이다. – Rod

1

일부 대체 솔루션을 평가 한 후에 다른 방법으로 문제를 해결할 수 있습니다. 그것은 Win32 API를 사용하고 있습니다. 이런 종류의 문제를 해결할 수있는 방법은 두 가지가 있습니다. 콜린스 방식은 WPF가 더 좋아하지만 수동으로 마우스 이벤트를 에뮬레이션해야하는 번거 로움이 있습니다. 두 번째 솔루션은 안타깝게도 관리되지 않는 Win32 후크를 사용하지만 WPF 마우스 이벤트 시스템은 제한없이 작동합니다.

여기 내 예제 코드입니다 :

using System; 
using System.Collections.Generic; 
using System.Diagnostics; 
using System.Linq; 
using System.Runtime.InteropServices; 
using System.Windows; 
using System.Windows.Input; 

namespace WpfApp1 
{ 
    public static class NativeMouseHook 
    { 
     private static readonly Dictionary<MouseMessages, List<MouseEventHandler>> MouseHandlers = new Dictionary<MouseMessages, List<MouseEventHandler>>(); 
     private static readonly Dictionary<MouseMessages, List<MouseButtonEventHandler>> MouseButtonHandlers = new Dictionary<MouseMessages, List<MouseButtonEventHandler>>(); 
     private static readonly Dictionary<MouseMessages, List<MouseWheelEventHandler>> MouseWheelHandlers = new Dictionary<MouseMessages, List<MouseWheelEventHandler>>(); 

     public static void RegisterMouseHandler(MouseMessages mouseMessage, MouseEventHandler handler) 
     { 
      AddHandler(mouseMessage, MouseHandlers, handler); 
      Start(); 
     } 

     public static void UnregisterMouseHandler(MouseMessages mouseMessage, MouseEventHandler handler) 
     { 
      RemoveHandler(mouseMessage, MouseHandlers, handler); 
      CheckAndStop(); 
     } 

     public static void RegisterMouseHandler(MouseMessages mouseMessage, MouseButtonEventHandler handler) 
     { 
      AddHandler(mouseMessage, MouseButtonHandlers, handler); 
      Start(); 
     } 

     public static void UnregisterMouseHandler(MouseMessages mouseMessage, MouseButtonEventHandler handler) 
     { 
      RemoveHandler(mouseMessage, MouseButtonHandlers, handler); 
      CheckAndStop(); 
     } 

     public static void RegisterMouseHandler(MouseMessages mouseMessage, MouseWheelEventHandler handler) 
     { 
      AddHandler(mouseMessage, MouseWheelHandlers, handler); 
      Start(); 
     } 

     public static void UnregisterMouseHandler(MouseMessages mouseMessage, MouseWheelEventHandler handler) 
     { 
      RemoveHandler(mouseMessage, MouseWheelHandlers, handler); 
      CheckAndStop(); 
     } 

     private static void AddHandler<T>(MouseMessages mouseMessage, Dictionary<MouseMessages, List<T>> targetHandlerDictionary, T handler) 
     { 
      if (!targetHandlerDictionary.ContainsKey(mouseMessage)) 
      { 
       targetHandlerDictionary.Add(mouseMessage, new List<T>()); 
      } 

      targetHandlerDictionary[mouseMessage].Add(handler); 
     } 

     private static void RemoveHandler<T>(MouseMessages mouseMessage, Dictionary<MouseMessages, List<T>> targetHandlerDictionary, T handler) 
     { 
      if (targetHandlerDictionary.ContainsKey(mouseMessage)) 
      { 
       var handlerList = targetHandlerDictionary[mouseMessage]; 
       handlerList.Remove(handler); 

       if (handlerList.Count == 0) 
       { 
        targetHandlerDictionary.Remove(mouseMessage); 
       } 
      } 
     } 

     private static void CheckAndStop() 
     { 
      if (MouseHandlers.Count == 0 && MouseButtonHandlers.Count == 0 && MouseWheelHandlers.Count == 0) 
      { 
       Stop(); 
      } 
     } 

     private static void Start() 
     { 
      if (_hookId == IntPtr.Zero) 
      { 
       _hookId = SetHook(Proc); 
      } 
     } 

     private static void Stop() 
     { 
      if (_hookId != IntPtr.Zero) 
      { 
       UnhookWindowsHookEx(_hookId); 
       _hookId = IntPtr.Zero; 
      } 
     } 

     private static readonly LowLevelMouseProc Proc = HookCallback; 
     private static IntPtr _hookId = IntPtr.Zero; 

     private static IntPtr SetHook(LowLevelMouseProc proc) 
     { 
      using (var curProcess = Process.GetCurrentProcess()) 
      { 
       using (var curModule = curProcess.MainModule) 
       { 
        return SetWindowsHookEx(WH_MOUSE_LL, proc, GetModuleHandle(curModule.ModuleName), 0); 
       } 
      } 
     } 

     private delegate IntPtr LowLevelMouseProc(int nCode, IntPtr wParam, IntPtr lParam); 

     private static IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam) 
     { 
      if (nCode >= 0) 
      { 
       var hookStruct = (MSLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(MSLLHOOKSTRUCT)); 

       switch ((MouseMessages)wParam) 
       { 
        case MouseMessages.WM_LBUTTONDOWN: 
         CallHandler(MouseMessages.WM_LBUTTONDOWN, MouseButtonHandlers, new MouseButtonEventArgs(Mouse.PrimaryDevice, (int)hookStruct.time, MouseButton.Left)); 
         break; 
        case MouseMessages.WM_LBUTTONUP: 
         CallHandler(MouseMessages.WM_LBUTTONUP, MouseButtonHandlers, new MouseButtonEventArgs(Mouse.PrimaryDevice, (int)hookStruct.time, MouseButton.Left)); 
         break; 
        case MouseMessages.WM_MOUSEMOVE: 
         CallHandler(MouseMessages.WM_MOUSEMOVE, MouseHandlers, new MouseEventArgs(Mouse.PrimaryDevice, (int)hookStruct.time)); 
         break; 
        case MouseMessages.WM_MOUSEWHEEL: 
         CallHandler(MouseMessages.WM_MOUSEWHEEL, MouseWheelHandlers, new MouseWheelEventArgs(Mouse.PrimaryDevice, (int)hookStruct.time, 0)); 
         break; 
        case MouseMessages.WM_RBUTTONDOWN: 
         CallHandler(MouseMessages.WM_LBUTTONDOWN, MouseButtonHandlers, new MouseButtonEventArgs(Mouse.PrimaryDevice, (int)hookStruct.time, MouseButton.Right)); 
         break; 
        case MouseMessages.WM_RBUTTONUP: 
         CallHandler(MouseMessages.WM_LBUTTONUP, MouseButtonHandlers, new MouseButtonEventArgs(Mouse.PrimaryDevice, (int)hookStruct.time, MouseButton.Right)); 
         break; 
       } 
      } 

      return CallNextHookEx(_hookId, nCode, wParam, lParam); 
     } 

     private static void CallHandler<T>(MouseMessages mouseMessage, Dictionary<MouseMessages, List<T>> targetHandlerDictionary, EventArgs args) 
     { 
      if (targetHandlerDictionary.ContainsKey(mouseMessage)) 
      { 
       var handlerList = targetHandlerDictionary[mouseMessage]; 
       foreach (var handler in handlerList.Cast<Delegate>()) 
       { 
        handler.DynamicInvoke(null, args); 
       } 
      } 
     } 

     private const int WH_MOUSE_LL = 14; 

     public enum MouseMessages 
     { 
      WM_LBUTTONDOWN = 0x0201, 
      WM_LBUTTONUP = 0x0202, 
      WM_MOUSEMOVE = 0x0200, 
      WM_MOUSEWHEEL = 0x020A, 
      WM_RBUTTONDOWN = 0x0204, 
      WM_RBUTTONUP = 0x0205 
     } 

     [StructLayout(LayoutKind.Sequential)] 
     private struct POINT 
     { 
      public int x; 
      public int y; 
     } 

     [StructLayout(LayoutKind.Sequential)] 
     private struct MSLLHOOKSTRUCT 
     { 
      public POINT pt; 
      public uint mouseData; 
      public uint flags; 
      public uint time; 
      public IntPtr dwExtraInfo; 
     } 

     [DllImport("user32.dll")] 
     [return: MarshalAs(UnmanagedType.Bool)] 
     internal static extern bool GetCursorPos(ref Win32Point pt); 

     [StructLayout(LayoutKind.Sequential)] 
     internal struct Win32Point 
     { 
      public Int32 X; 
      public Int32 Y; 
     }; 
     public static Point GetMousePosition() 
     { 
      Win32Point w32Mouse = new Win32Point(); 
      GetCursorPos(ref w32Mouse); 
      return new Point(w32Mouse.X, w32Mouse.Y); 
     } 

     [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] 
     private static extern IntPtr SetWindowsHookEx(int idHook, LowLevelMouseProc lpfn, IntPtr hMod, uint dwThreadId); 

     [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] 
     [return: MarshalAs(UnmanagedType.Bool)] 
     private static extern bool UnhookWindowsHookEx(IntPtr hhk); 

     [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] 
     private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam); 

     [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] 
     private static extern IntPtr GetModuleHandle(string lpModuleName); 
    } 
} 

XAML : 뒤에

<Window x:Class="WpfApp1.MainWindow" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
     xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
     xmlns:local="clr-namespace:WpfApp1" 
     mc:Ignorable="d" 
     WindowState="Maximized" 
     Title="MainWindow" Height="350" Width="525"> 
    <Canvas> 
     <Rectangle x:Name="Test" Fill="Blue" Width="40" Height="40" Canvas.Left="200" Canvas.Top="200" /> 
     <Line x:Name="Line" Stroke="Black" StrokeThickness="1" IsHitTestVisible="False" /> 
    </Canvas> 
</Window> 

코드 :

using System; 
using System.Windows.Input; 
using System.Windows.Media; 

namespace WpfApp1 
{ 
    /// <summary> 
    /// Interaction logic for MainWindow.xaml 
    /// </summary> 
    public partial class MainWindow 
    { 
     public MainWindow() 
     { 
      InitializeComponent(); 

      Test.MouseEnter += TestOnMouseEnter; 
      Test.MouseLeave += TestOnMouseLeave; 

      MouseDown += OnMouseDown; 
     } 

     private void TestOnMouseEnter(object sender, MouseEventArgs mouseEventArgs) 
     { 
      Console.WriteLine("(Test) MouseEnter"); 

      Test.Fill = Brushes.Coral; 
     } 

     private void TestOnMouseLeave(object sender, MouseEventArgs mouseEventArgs) 
     { 
      Console.WriteLine("(Test) MouseLeave"); 

      Test.Fill = Brushes.Blue; 
     } 

     private void OnMouseMove(object sender, MouseEventArgs mouseEventArgs) 
     { 
      Console.WriteLine("(Window) MouseMove"); 

      var pos = NativeMouseHook.GetMousePosition(); 
      Line.X2 = pos.X; 
      Line.Y2 = pos.Y; 
     } 

     private void OnMouseDown(object sender, MouseButtonEventArgs mouseButtonEventArgs) 
     { 
      Console.WriteLine("(Window) MouseDown"); 

      NativeMouseHook.RegisterMouseHandler(NativeMouseHook.MouseMessages.WM_MOUSEMOVE, (MouseEventHandler)OnMouseMove); 
      NativeMouseHook.RegisterMouseHandler(NativeMouseHook.MouseMessages.WM_LBUTTONUP, OnMouseUp); 

      var pos = mouseButtonEventArgs.GetPosition(this); 
      Line.X1 = pos.X; 
      Line.Y1 = pos.Y; 
     } 

     private void OnMouseUp(object sender, MouseButtonEventArgs mouseButtonEventArgs) 
     { 
      Console.WriteLine("(Window) MouseUp"); 

      NativeMouseHook.UnregisterMouseHandler(NativeMouseHook.MouseMessages.WM_MOUSEMOVE, (MouseEventHandler)OnMouseMove); 
      NativeMouseHook.UnregisterMouseHandler(NativeMouseHook.MouseMessages.WM_LBUTTONUP, OnMouseUp); 
     } 
    } 
}