none
如何在WPF中模拟鼠标点击? RRS feed

  • 问题

  • 我正在写一个程序,它是浮在前台的一个半透明的窗口,它需要响应鼠标移动和鼠标点击事件。在某些情况下它响应了鼠标点击事件后,需要在另外一个地方提交一个鼠标点击事件,并且不能被这个窗口捕捉并相应(即要点到这个窗口的后面)。

    所以我的问题是有没有一个可以用来模拟真正鼠标事件的办法,即可以通过这种方法点击到其它的窗口上,并且能够通过一定的方法控制WPF窗体不响应这次提交的鼠标事件?

    我试过User32.dll的mouse_event API,使用它有几个问题。有一个就是WPF的像素无关位置和它的坐标之间的转换;还有一个是直接提交mouse_event时鼠标并不点击到传进去的参数指定的位置,而是鼠标当前的位置。如果说这两个可以通过模拟鼠标移动和实验做出判断的话,那么如何控制窗体不响应这次模拟鼠标点击呢?我试过利用把背景换成透明,模拟鼠标点击,然后再换回来的做法——我觉得这个不是很好的做法,况且的它的效果并不是每次点击都能如预期一样。

    2011年10月5日 3:32

答案

  • 首先,你的需求不是不可能实现,但实现起来不是那么简单。

    有几种方式你可以参考:

    1, 最原始的方式,通过P/Invoke Windows API, 如SendMessage 和 PostMessage去发送鼠标消息。这里我们就需要理解和得到一个WPF Window的句柄了。众所周知,WPF元素由于是通过DX绘制,本身是没有句柄的,不管是Window元素还是Control元素。但是Windows操作系统为了呈现出一个和一般窗体一致的效果,就会在Window元素外面包裹一个GDI的容器,这个容器是有一个句柄的,他的类名是: HwndWrapper

    这个时候我们就需要通过下面的方法去得到这个句柄,然后供我们的SendMessage和PostMessage使用:

    IntPtr windowHandle = new WindowInteropHelper(this).Handle
    

    然后我们还可以注册消息处理函数给WPF窗体:

        public MainWindow()
        {
          InitializeComponent();
          this.Loaded += new RoutedEventHandler(MainWindow_Loaded);
        }
    
        void MainWindow_Loaded(object sender, RoutedEventArgs e)
        {
          HwndSource source = PresentationSource.FromVisual(this) as HwndSource;
          source.AddHook(WndProc);
        }
        private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
        {
          // Handle messages...
    
          return IntPtr.Zero;
        }
    

    2, 通过UIAutomation方式,这个理解起来不难,但是设计内容很多。你可以仔细看他的文档:http://msdn.microsoft.com/en-us/library/ms747327.aspx

    简单来讲,我们可以通过操作系统的一个UI树来找到任何一个程序UI上的标准的控件,然后通过代码来调用这些标准控件上的一些方法,比如我们可以遍历UI树找到某一个程序上的Button控件,然后调用自动触发这个Button的Click事件(因为Button是一个系统标准的控件,而且他有一个标准行为就是被点击,所以UIAutomation种就可以调用他的这个模式)。代码片段:

    ButtonAutomationPeer bap = new ButtonAutomationPeer(myButton);
    IInvokeProvider iip = bap.GetPattern(PatternInterface.Invoke) as IInvokeProvider;
    iip.Invoke();
    

    这里有个很好的工具是可以来看UI树的,就如同我们喜欢用Spy++来看Windows系统里的handle树和进程线程一样:UISpy http://msdn.microsoft.com/zh-cn/library/ms727247.aspx  在Windows SDK 7.0以上中都包含了这个工具。

     

    关于你说的像素无关性,其实大多数情况下你只要简单对坐标取整就行了,因为大多数情况都是96DPI,不会影响结果。而且WPF窗体中的元素坐标也是相对的,并没有绝对与屏幕的坐标。如果你要取到或者转换平魔坐标系,WPF控件本身提供了两个方法 PointToScreen PointFromScreen 这点和Winform一致。

    Sincerely,

     


    Bob Bao [MSFT]
    MSDN Community Support | Feedback to us
    Get or Request Code Sample from Microsoft
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    2011年10月5日 8:43
    版主