You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

WPF测试中生成指定坐标的MouseDown、MouseMove、MouseUp事件

我之前做拖拽选框功能时也踩过类似的坑,TestStack.White的封装确实太死,没法拆分成单个Mouse事件,WPF原生触发坐标事件的方法如果没做对也容易失效。给你分享两个亲测有效的实现思路:

方案一:WPF原生控件自定义拖拽选框(应用内场景)

如果你的需求是在自己的WPF应用里实现拖拽选框,直接用WPF的原生鼠标事件是最稳定的方式,不需要依赖第三方库。

实现步骤:

  1. 在容器控件(比如Grid)上绑定MouseDown、MouseMove、MouseUp事件
  2. 用一个Rectangle作为选框的可视化元素,默认隐藏
  3. 鼠标按下时记录起始坐标,显示选框并初始化位置
  4. 鼠标移动时实时计算选框的位置和尺寸(要处理从右下往左上拖拽的反向情况)
  5. 鼠标松开时隐藏选框,根据选框尺寸执行计算逻辑

代码示例:

XAML部分:

<Grid x:Name="MainGrid" Background="Transparent"
      MouseDown="MainGrid_MouseDown"
      MouseMove="MainGrid_MouseMove"
      MouseUp="MainGrid_MouseUp">
    <!-- 选框元素,默认隐藏 -->
    <Rectangle x:Name="SelectionBox" 
               Stroke="Blue" 
               StrokeThickness="2" 
               Fill="#200000FF" 
               Visibility="Collapsed"/>
    <!-- 你的其他业务控件 -->
</Grid>

注意:一定要把Grid的Background设为Transparent,否则空白区域不会响应鼠标事件

C#后台代码:

using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;

namespace WpfDragSelection
{
    public partial class MainWindow : Window
    {
        private Point _dragStartPoint;
        private bool _isDragging;

        public MainWindow()
        {
            InitializeComponent();
        }

        private void MainGrid_MouseDown(object sender, MouseButtonEventArgs e)
        {
            // 仅处理左键按下事件
            if (e.LeftButton != MouseButtonState.Pressed) return;

            _dragStartPoint = e.GetPosition(MainGrid);
            _isDragging = true;

            // 显示选框并初始化位置
            SelectionBox.Visibility = Visibility.Visible;
            Canvas.SetLeft(SelectionBox, _dragStartPoint.X);
            Canvas.SetTop(SelectionBox, _dragStartPoint.Y);
            SelectionBox.Width = 0;
            SelectionBox.Height = 0;
        }

        private void MainGrid_MouseMove(object sender, MouseEventArgs e)
        {
            if (!_isDragging) return;

            Point currentPoint = e.GetPosition(MainGrid);

            // 计算选框的正确位置和尺寸,兼容反向拖拽
            double left = Math.Min(_dragStartPoint.X, currentPoint.X);
            double top = Math.Min(_dragStartPoint.Y, currentPoint.Y);
            double width = Math.Abs(currentPoint.X - _dragStartPoint.X);
            double height = Math.Abs(currentPoint.Y - _dragStartPoint.Y);

            // 更新选框UI
            Canvas.SetLeft(SelectionBox, left);
            Canvas.SetTop(SelectionBox, top);
            SelectionBox.Width = width;
            SelectionBox.Height = height;
        }

        private void MainGrid_MouseUp(object sender, MouseButtonEventArgs e)
        {
            if (!_isDragging) return;

            _isDragging = false;
            SelectionBox.Visibility = Visibility.Collapsed;

            // 这里执行选框尺寸的计算逻辑
            double boxWidth = SelectionBox.Width;
            double boxHeight = SelectionBox.Height;

            // 示例:弹出尺寸信息
            MessageBox.Show($"选框尺寸:宽{boxWidth:F2},高{boxHeight:F2}\n面积:{boxWidth * boxHeight:F2}");
        }
    }
}
方案二:Windows API模拟全局鼠标事件(跨进程/全局场景)

如果你的需求是给其他应用模拟拖拽选框(比如TestStack.White的使用场景),可以直接调用Windows的SendInput API,它能模拟最底层的鼠标输入事件,完全拆分MouseDown、MouseMove、MouseUp。

实现步骤:

  1. 导入user32.dll的SendInput函数和相关结构体
  2. 实现坐标转换方法(将屏幕坐标转为API需要的绝对坐标范围)
  3. 按顺序发送“移动到起始点→左键按下→移动到结束点→左键松开”的输入序列
  4. 根据起始和结束坐标计算选框尺寸,执行你的计算逻辑

代码示例:

using System;
using System.Runtime.InteropServices;
using System.Windows;

namespace GlobalDragSimulation
{
    public static class MouseSimulator
    {
        [StructLayout(LayoutKind.Sequential)]
        private struct INPUT
        {
            public int type;
            public MOUSEINPUT mi;
        }

        [StructLayout(LayoutKind.Sequential)]
        private struct MOUSEINPUT
        {
            public int dx;
            public int dy;
            public int mouseData;
            public int dwFlags;
            public int time;
            public IntPtr dwExtraInfo;
        }

        private const int INPUT_MOUSE = 0;
        private const int MOUSEEVENTF_LEFTDOWN = 0x0002;
        private const int MOUSEEVENTF_LEFTUP = 0x0004;
        private const int MOUSEEVENTF_MOVE = 0x0001;
        private const int MOUSEEVENTF_ABSOLUTE = 0x8000;

        [DllImport("user32.dll", SetLastError = true)]
        private static extern uint SendInput(uint nInputs, INPUT[] pInputs, int cbSize);

        // 将屏幕坐标转换为SendInput需要的绝对坐标(范围0-65535)
        private static int ConvertToAbsoluteX(int x)
        {
            return (x * 65535) / System.Windows.Forms.Screen.PrimaryScreen.Bounds.Width;
        }

        private static int ConvertToAbsoluteY(int y)
        {
            return (y * 65535) / System.Windows.Forms.Screen.PrimaryScreen.Bounds.Height;
        }

        public static void SimulateDragSelection(Point startPoint, Point endPoint)
        {
            var inputSequence = new INPUT[4];

            // 1. 移动鼠标到起始坐标
            inputSequence[0].type = INPUT_MOUSE;
            inputSequence[0].mi.dx = ConvertToAbsoluteX((int)startPoint.X);
            inputSequence[0].mi.dy = ConvertToAbsoluteY((int)startPoint.Y);
            inputSequence[0].mi.dwFlags = MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE;

            // 2. 按下左键
            inputSequence[1].type = INPUT_MOUSE;
            inputSequence[1].mi.dwFlags = MOUSEEVENTF_LEFTDOWN;

            // 3. 移动鼠标到结束坐标
            inputSequence[2].type = INPUT_MOUSE;
            inputSequence[2].mi.dx = ConvertToAbsoluteX((int)endPoint.X);
            inputSequence[2].mi.dy = ConvertToAbsoluteY((int)endPoint.Y);
            inputSequence[2].mi.dwFlags = MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE;

            // 4. 松开左键
            inputSequence[3].type = INPUT_MOUSE;
            inputSequence[3].mi.dwFlags = MOUSEEVENTF_LEFTUP;

            // 发送输入序列
            SendInput(4, inputSequence, Marshal.SizeOf(typeof(INPUT)));

            // 计算选框尺寸并执行逻辑
            double width = Math.Abs(endPoint.X - startPoint.X);
            double height = Math.Abs(endPoint.Y - startPoint.Y);
            Console.WriteLine($"模拟选框尺寸:宽{width:F2},高{height:F2}");
        }
    }
}

注意:需要引用System.Windows.Forms程序集,并且如果是WPF项目,要在项目里添加对它的引用

两种方案对比:

  • 方案一适合WPF应用内部的拖拽选框,完全原生,无外部依赖,响应速度快
  • 方案二适合跨进程或全局场景,能绕过TestStack.White的封装限制,但需要处理API调用的细节

内容的提问来源于stack exchange,提问作者TheMachinist

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 04:32:09