WPF 鼠标与触摸屏拖动窗体 窗体拖动功能失灵问题处理

ops/2024/10/17 23:22:21/

实现思路:

 通过 WPF的behavior 的 AssociatedObject 对象绑定事件 TouchDown、TouchUp、MouseLeftButtonDown处理。

 WindowDragBehavior  ----代码:

  public class WindowDragBehavior : Behavior<Window>{ private uint _currentTouchCount;protected override void OnAttached(){AssociatedObject.TouchDown += AssociatedObject_TouchDown; AssociatedObject.TouchUp += AssociatedObject_TouchUp;AssociatedObject.MouseLeftButtonDown += AssociatedObject_MouseLeftButtonDown;} private void AssociatedObject_MouseLeftButtonDown(object sender, MouseButtonEventArgs e){DragMoveWindowHelper.DragMove((Window)sender); }private void AssociatedObject_TouchUp(object sender, TouchEventArgs e){_currentTouchCount--;}private void AssociatedObject_TouchDown(object sender, TouchEventArgs e){ ((Window)sender).CaptureTouch(e.TouchDevice);if (_currentTouchCount == 0){DragMoveWindowHelper.DragMove((Window)sender);}}}

DragMoveWindowHelper:

public static class DragMoveWindowHelper
{public static void DragMove(Window window){var dragMoveMode = new DragMoveMode(window);dragMoveMode.Start();}class DragMoveMode{public DragMoveMode(Window window){_window = window;}public void Start(){var window = _window;window.PreviewMouseMove += Window_PreviewMouseMove;window.PreviewMouseUp += Window_PreviewMouseUp;window.LostMouseCapture += Window_LostMouseCapture;}public void Stop(){Window window = _window;window.PreviewMouseMove -= Window_PreviewMouseMove;window.PreviewMouseUp -= Window_PreviewMouseUp;window.LostMouseCapture -= Window_LostMouseCapture;window.ReleaseMouseCapture();}private readonly Window _window;private Win32.User32.Point? _lastPoint;private void Window_LostMouseCapture(object sender, MouseEventArgs e){Stop();}private void Window_PreviewMouseUp(object sender, MouseButtonEventArgs e){Stop();}private void Window_PreviewMouseMove(object sender, MouseEventArgs e){Win32.User32.GetCursorPos(out var lpPoint);if (_lastPoint == null){_lastPoint = lpPoint;_window.CaptureMouse();}var dx = lpPoint.X - _lastPoint.Value.X;var dy = lpPoint.Y - _lastPoint.Value.Y;// Debug.WriteLine($"dx={dx} dy={dy}");// 以下的 60 是表示最大移动速度if (Math.Abs(dx) < 60 && Math.Abs(dy) < 60){var handle = new WindowInteropHelper(_window).Handle;Win32.User32.GetWindowRect(handle, out var lpRect);Win32.User32.SetWindowPos(handle, IntPtr.Zero, lpRect.Left + dx, lpRect.Top + dy, 0, 0,(int)(Win32.User32.WindowPositionFlags.SWP_NOSIZE |Win32.User32.WindowPositionFlags.SWP_NOZORDER));}_lastPoint = lpPoint;}}static class Win32{public static class User32{/// <summary>/// 改变一个子窗口、弹出式窗口和顶层窗口的尺寸、位置和 Z 序。/// </summary>/// <param name="hWnd">窗口句柄。</param>/// <param name="hWndInsertAfter">/// 在z序中的位于被置位的窗口前的窗口句柄。该参数必须为一个窗口句柄,或下列值之一:/// <para>HWND_BOTTOM:将窗口置于 Z 序的底部。如果参数hWnd标识了一个顶层窗口,则窗口失去顶级位置,并且被置在其他窗口的底部。</para>/// <para>HWND_NOTOPMOST:将窗口置于所有非顶层窗口之上(即在所有顶层窗口之后)。如果窗口已经是非顶层窗口则该标志不起作用。</para>/// <para>HWND_TOP:将窗口置于Z序的顶部。</para>/// <para>HWND_TOPMOST:将窗口置于所有非顶层窗口之上。即使窗口未被激活窗口也将保持顶级位置。</para>/// </param>/// <param name="x">以客户坐标指定窗口新位置的左边界。</param>/// <param name="y">以客户坐标指定窗口新位置的顶边界。</param>/// <param name="cx">以像素指定窗口的新的宽度。</param>/// <param name="cy">以像素指定窗口的新的高度。</param>/// <param name="wFlagslong">/// 窗口尺寸和定位的标志。该参数可以是下列值的组合:/// <para>SWP_ASYNCWINDOWPOS:如果调用进程不拥有窗口,系统会向拥有窗口的线程发出需求。这就防止调用线程在其他线程处理需求的时候发生死锁。</para>/// <para>SWP_DEFERERASE:防止产生 WM_SYNCPAINT 消息。</para>/// <para>SWP_DRAWFRAME:在窗口周围画一个边框(定义在窗口类描述中)。</para>/// <para>SWP_FRAMECHANGED:给窗口发送 WM_NCCALCSIZE 消息,即使窗口尺寸没有改变也会发送该消息。如果未指定这个标志,只有在改变了窗口尺寸时才发送 WM_NCCALCSIZE。</para>/// <para>SWP_HIDEWINDOW:隐藏窗口。</para>/// <para>SWP_NOACTIVATE:不激活窗口。如果未设置标志,则窗口被激活,并被设置到其他最高级窗口或非最高级组的顶部(根据参数hWndlnsertAfter设置)。</para>/// <para>SWP_NOCOPYBITS:清除客户区的所有内容。如果未设置该标志,客户区的有效内容被保存并且在窗口尺寸更新和重定位后拷贝回客户区。</para>/// <para>SWP_NOMOVE:维持当前位置(忽略X和Y参数)。</para>/// <para>SWP_NOOWNERZORDER:不改变 Z 序中的所有者窗口的位置。</para>/// <para>SWP_NOREDRAW:不重画改变的内容。如果设置了这个标志,则不发生任何重画动作。适用于客户区和非客户区(包括标题栏和滚动条)和任何由于窗回移动而露出的父窗口的所有部分。如果设置了这个标志,应用程序必须明确地使窗口无效并区重画窗口的任何部分和父窗口需要重画的部分。</para>/// <para>SWP_NOREPOSITION:与 SWP_NOOWNERZORDER 标志相同。</para>/// <para>SWP_NOSENDCHANGING:防止窗口接收 WM_WINDOWPOSCHANGING 消息。</para>/// <para>SWP_NOSIZE:维持当前尺寸(忽略 cx 和 cy 参数)。</para>/// <para>SWP_NOZORDER:维持当前 Z 序(忽略 hWndlnsertAfter 参数)。</para>/// <para>SWP_SHOWWINDOW:显示窗口。</para>/// </param>/// <returns>如果函数成功,返回值为非零;如果函数失败,返回值为零。若想获得更多错误消息,请调用 GetLastError 函数。</returns>[DllImport(LibraryName, ExactSpelling = true, SetLastError = true)]public static extern Int32 SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, Int32 x, Int32 y, Int32 cx,Int32 cy, Int32 wFlagslong);[Flags]public enum WindowPositionFlags{/// <summary>///     If the calling thread and the thread that owns the window are attached to different input queues, the system posts///     the request to the thread that owns the window. This prevents the calling thread from blocking its execution while///     other threads process the request./// </summary>SWP_ASYNCWINDOWPOS = 0x4000,/// <summary>///     Prevents generation of the WM_SYNCPAINT message./// </summary>SWP_DEFERERASE = 0x2000,/// <summary>///     Draws a frame (defined in the window's class description) around the window./// </summary>SWP_DRAWFRAME = 0x0020,/// <summary>///     Applies new frame styles set using the SetWindowLong function. Sends a WM_NCCALCSIZE message to the window, even if///     the window's size is not being changed. If this flag is not specified, WM_NCCALCSIZE is sent only when the window's///     size is being changed./// </summary>SWP_FRAMECHANGED = 0x0020,/// <summary>///     Hides the window./// </summary>SWP_HIDEWINDOW = 0x0080,/// <summary>///     Does not activate the window. If this flag is not set, the window is activated and moved to the top of either the///     topmost or non-topmost group (depending on the setting of the hWndInsertAfter parameter)./// </summary>SWP_NOACTIVATE = 0x0010,/// <summary>///     Discards the entire contents of the client area. If this flag is not specified, the valid contents of the client///     area are saved and copied back into the client area after the window is sized or repositioned./// </summary>SWP_NOCOPYBITS = 0x0100,/// <summary>///     Retains the current position (ignores X and Y parameters)./// </summary>SWP_NOMOVE = 0x0002,/// <summary>///     Does not change the owner window's position in the Z order./// </summary>SWP_NOOWNERZORDER = 0x0200,/// <summary>///     Does not redraw changes. If this flag is set, no repainting of any kind occurs. This applies to the client area,///     the nonclient area (including the title bar and scroll bars), and any part of the parent window uncovered as a///     result of the window being moved. When this flag is set, the application must explicitly invalidate or redraw any///     parts of the window and parent window that need redrawing./// </summary>SWP_NOREDRAW = 0x0008,/// <summary>///     Same as the SWP_NOOWNERZORDER flag./// </summary>SWP_NOREPOSITION = 0x0200,/// <summary>///     Prevents the window from receiving the WM_WINDOWPOSCHANGING message./// </summary>SWP_NOSENDCHANGING = 0x0400,/// <summary>///     Retains the current size (ignores the cx and cy parameters)./// </summary>SWP_NOSIZE = 0x0001,/// <summary>///     Retains the current Z order (ignores the hWndInsertAfter parameter)./// </summary>SWP_NOZORDER = 0x0004,/// <summary>///     Displays the window./// </summary>SWP_SHOWWINDOW = 0x0040}public const string LibraryName = "user32";/// <summary>/// 获取的是以屏幕为坐标轴窗口坐标/// </summary>/// <param name="hWnd"></param>/// <param name="lpRect"></param>/// <returns></returns>[return: MarshalAs(UnmanagedType.Bool)][DllImport(LibraryName, ExactSpelling = true)]public static extern bool GetWindowRect(IntPtr hWnd, out Rectangle lpRect);/// <summary>/// 在 Win32 函数使用的矩形/// </summary>[StructLayout(LayoutKind.Sequential)]public partial struct Rectangle : IEquatable<Rectangle>{/// <summary>///  创建在 Win32 函数使用的矩形/// </summary>/// <param name="left"></param>/// <param name="top"></param>/// <param name="right"></param>/// <param name="bottom"></param>public Rectangle(int left = 0, int top = 0, int right = 0, int bottom = 0){Left = left;Top = top;Right = right;Bottom = bottom;}/// <summary>/// 创建在 Win32 函数使用的矩形/// </summary>/// <param name="width">矩形的宽度</param>/// <param name="height">矩形的高度</param>public Rectangle(int width = 0, int height = 0) : this(0, 0, width, height){}public int Left;public int Top;public int Right;public int Bottom;public bool Equals(Rectangle other){return (Left == other.Left) && (Right == other.Right) && (Top == other.Top) &&(Bottom == other.Bottom);}public override bool Equals(object obj){return obj is Rectangle && Equals((Rectangle)obj);}public static bool operator ==(Rectangle left, Rectangle right){return left.Equals(right);}public static bool operator !=(Rectangle left, Rectangle right){return !(left == right);}public override int GetHashCode(){unchecked{var hashCode = (int)Left;hashCode = (hashCode * 397) ^ (int)Top;hashCode = (hashCode * 397) ^ (int)Right;hashCode = (hashCode * 397) ^ (int)Bottom;return hashCode;}}/// <summary>/// 获取当前矩形是否空矩形/// </summary>public bool IsEmpty => this.Left == 0 && this.Top == 0 && this.Right == 0 && this.Bottom == 0;/// <summary>/// 矩形的宽度/// </summary>public int Width{get { return unchecked((int)(Right - Left)); }set { Right = unchecked((int)(Left + value)); }}/// <summary>/// 矩形的高度/// </summary>public int Height{get { return unchecked((int)(Bottom - Top)); }set { Bottom = unchecked((int)(Top + value)); }}/// <summary>/// 通过 x、y 坐标和宽度高度创建矩形/// </summary>/// <param name="x"></param>/// <param name="y"></param>/// <param name="width"></param>/// <param name="height"></param>/// <returns></returns>public static Rectangle Create(int x, int y, int width, int height){unchecked{return new Rectangle(x, y, (int)(width + x), (int)(height + y));}}public override string ToString(){var culture = CultureInfo.CurrentCulture;return$"{{ Left = {Left.ToString(culture)}, Top = {Top.ToString(culture)} , Right = {Right.ToString(culture)}, Bottom = {Bottom.ToString(culture)} }}, {{ Width: {Width.ToString(culture)}, Height: {Height.ToString(culture)} }}";}public static Rectangle From(ref Rectangle lvalue, ref Rectangle rvalue,Func<int, int, int> leftTopOperation,Func<int, int, int> rightBottomOperation = null){if (rightBottomOperation == null)rightBottomOperation = leftTopOperation;return new Rectangle(leftTopOperation(lvalue.Left, rvalue.Left),leftTopOperation(lvalue.Top, rvalue.Top),rightBottomOperation(lvalue.Right, rvalue.Right),rightBottomOperation(lvalue.Bottom, rvalue.Bottom));}public void Add(Rectangle value){Add(ref this, ref value);}public void Subtract(Rectangle value){Subtract(ref this, ref value);}public void Multiply(Rectangle value){Multiply(ref this, ref value);}public void Divide(Rectangle value){Divide(ref this, ref value);}public void Deflate(Rectangle value){Deflate(ref this, ref value);}public void Inflate(Rectangle value){Inflate(ref this, ref value);}public void Offset(int x, int y){Offset(ref this, x, y);}public void OffsetTo(int x, int y){OffsetTo(ref this, x, y);}public void Scale(int x, int y){Scale(ref this, x, y);}public void ScaleTo(int x, int y){ScaleTo(ref this, x, y);}public static void Add(ref Rectangle lvalue, ref Rectangle rvalue){lvalue.Left += rvalue.Left;lvalue.Top += rvalue.Top;lvalue.Right += rvalue.Right;lvalue.Bottom += rvalue.Bottom;}public static void Subtract(ref Rectangle lvalue, ref Rectangle rvalue){lvalue.Left -= rvalue.Left;lvalue.Top -= rvalue.Top;lvalue.Right -= rvalue.Right;lvalue.Bottom -= rvalue.Bottom;}public static void Multiply(ref Rectangle lvalue, ref Rectangle rvalue){lvalue.Left *= rvalue.Left;lvalue.Top *= rvalue.Top;lvalue.Right *= rvalue.Right;lvalue.Bottom *= rvalue.Bottom;}public static void Divide(ref Rectangle lvalue, ref Rectangle rvalue){lvalue.Left /= rvalue.Left;lvalue.Top /= rvalue.Top;lvalue.Right /= rvalue.Right;lvalue.Bottom /= rvalue.Bottom;}public static void Deflate(ref Rectangle target, ref Rectangle deflation){target.Top += deflation.Top;target.Left += deflation.Left;target.Bottom -= deflation.Bottom;target.Right -= deflation.Right;}public static void Inflate(ref Rectangle target, ref Rectangle inflation){target.Top -= inflation.Top;target.Left -= inflation.Left;target.Bottom += inflation.Bottom;target.Right += inflation.Right;}public static void Offset(ref Rectangle target, int x, int y){target.Top += y;target.Left += x;target.Bottom += y;target.Right += x;}public static void OffsetTo(ref Rectangle target, int x, int y){var width = target.Width;var height = target.Height;target.Left = x;target.Top = y;target.Right = width;target.Bottom = height;}public static void Scale(ref Rectangle target, int x, int y){target.Top *= y;target.Left *= x;target.Bottom *= y;target.Right *= x;}public static void ScaleTo(ref Rectangle target, int x, int y){unchecked{x = (int)(target.Left / x);y = (int)(target.Top / y);}Scale(ref target, x, y);}}[DllImport(LibraryName, SetLastError = true)][return: MarshalAs(UnmanagedType.Bool)]public static extern bool GetCursorPos(out Point lpPoint);[StructLayout(LayoutKind.Sequential)]public struct Point{public int X;public int Y;public Point(int x, int y){this.X = x;this.Y = y;}public static implicit operator System.Drawing.Point(Point p){return new System.Drawing.Point(p.X, p.Y);}public static implicit operator Point(System.Drawing.Point p){return new Point(p.X, p.Y);}}}}
}


http://www.ppmy.cn/ops/124983.html

相关文章

matlab不小心删除怎么撤回

预设项——>删除文件——>移动至临时文件夹 tem临时文件夹下

隐私保护机器学习技术与实践

隐私保护机器学习技术与实践 目录 &#x1f512; 隐私保护机器学习的概念与重要性&#x1f310; 差分隐私&#xff1a;保护数据隐私的关键技术 &#x1f50a; 如何通过噪声注入保护个体数据&#x1f9e9; 差分隐私在机器学习中的应用 &#x1f91d; 联邦学习&#xff1a;分布…

视频号直播自动回复与循环发送话术-自动化插件

我们在做视频号直播的时候&#xff0c;会有这种自动回复咨询问题的功能 唯一客服浏览器插件现在就支持&#xff0c;在视频号直播后台&#xff0c;自动化回复用户问题&#xff0c;以及循环发送我们的介绍话术

IDEA Sping Boot 多配置文件application Maven动态切换

新建application-dev.yml与application-prod.yml pom.xml文件下添加profiles等 让idea识别出配置文件 <profiles><profile><id>dev</id><properties><!-- 环境标识&#xff0c;需要与配置文件的名称相对应 --><profiles.active>dev&…

Django的模板语法

Django的模板语法 1、初步认识2、原理 1、初步认识 本质上&#xff1a;在HTML中写一些占位符&#xff0c;由数据对这些占位符进行替换和处理。 在views.py中用字典&#xff08;键值对&#xff09;的形式传参&#xff0c;在html文件中用两个花括号来显示单独的值 列表、元组等数…

通过修改注册表来提高导出图像的分辨率(PPT尝试)

通过修改注册表来提高 PowerPoint 导出图片的分辨率&#xff0c;可以导致导出的图片尺寸大于您期望的 1920 x 1080 像素。例如&#xff0c;将 ExportBitmapResolution 设置为 300 DPI&#xff0c;可能会导致输出图像的尺寸变得非常大&#xff0c;比如 10240 x 5760 像素。这是因…

SpringBoot定时任务@Scheduled完整功能详解(提供Gitee源码)

目录 一、实现定时任务 1.1、fixedRate 1.2、fixedDelay 1.3、initialDelay 1.4、cron 二、cron表达式 三、读取配置文件 四、实现并行执行定时任务 五、Gitee源码 一、实现定时任务 首先在主应用类或者任何配置类上添加@EnableScheduling注解,以启用定时任务功能。…

Django makemigrations时出现ModuleNotFoundError: No module named ‘MySQLdb‘

使用Python 3.11、Django 5.1.2 写完model进行makemigrations时出现报错 查找资料发现说是mysqldb适用于Python2&#xff0c;不支持Python3&#xff1b;python3可以使用pymysql 安装pymsql pip install pymysql 然后要在项目的__init__.py中加如下代码&#xff1a; import …