일부 대체 솔루션을 평가 한 후에 다른 방법으로 문제를 해결할 수 있습니다. 그것은 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);
}
}
}
당신이 "OnPreview ..."이벤트를 사용하여 시도? –
MouseCapturing의 개념 때문에 이것이 작동하지 않을 것이라고 생각합니다. – Rod