天天看点

[WPF] PerformClick ?

原文:

[WPF] PerformClick ?

                                      [WPF] PerformClick ? 

                                                 周银辉

WPF没有提供这个方法,还真是让人觉得有些讨厌啊。而关于这个嘛,Google中搜一下,一大堆,但一般是利用XXXAutomationPeer。

这个原本用于支持自动化测试的,被拿来干了这事,代码如下:

        public static void PerformClick(this Button button)

        {

            var peer =new ButtonAutomationPeer(button);

            var invokeProv = peer.GetPattern(PatternInterface.Invoke) as IInvokeProvider;

            if (invokeProv != null)

            {

                invokeProv.Invoke();

            }

        }

但很快地,你会发现去其依赖于具体的类:ButtonAutomationPeer, 所以WPF实现了一堆XXXAutomationPeer, 这多少让人觉得有些.... 比如“我想在任意UI元素上模拟下鼠标点击”,这种方法便不可行了,虽然有一堆Peer,再多也不是“任意”,那么用SendMessage吧,这才是王道,代码如下:

        public static void PerformClick(this UIElement element, Point point)

            var window = Window.GetWindow(element);

            if (window != null)

                var windowHwnd = GetHwnd(window);

                var locOfElement = element.TranslatePoint(new Point(0, 0), window);

                var pointToWindow = new Point(point.X + locOfElement.X, point.Y + locOfElement.Y);

                Int32 lparam = MakeLong((int)pointToWindow.X, (int)pointToWindow.Y);

                // WM_LBUTTONDOWN = 0x0201

                SendMessage(windowHwnd, WM_LBUTTONDOWN, 0, lparam);

                // WM_LBUTTONUP = 0x0202;

                SendMessage(windowHwnd, WM_LBUTTONUP, 0, lparam);

        internal static int MakeLong(int lowWord, int highWord)

            return (highWord << 16) | (lowWord & 0xffff);

        internal static IntPtr GetHwnd(this Window window)

            var winHelper = new WindowInteropHelper(window);

            return winHelper.Handle;

 这个方法可以拓展到任意UI元素上,但很奇怪的是:居然不会引发Button的Click事件!从效果上看,的确点击了,因为焦点都转移上去了。那好吧,再用用下面的方法吧:反射,我比较喜欢这个方式:

        public static void PerformClick(this ButtonBase button)

            var method = button.GetType().GetMethod("OnClick", 

                BindingFlags.NonPublic | BindingFlags.Instance);

            if (method != null)

                method.Invoke(button, null);

            //button.Focus();

OK,总结一下:

第一种方法,依赖于具体的XXXPeer, 能力有限,不够灵活

第二种方法,较灵活,但由于SendMessage第一个参数要求传入hwnd,而WPF普通控件没有句柄,所以其依赖于窗口句柄,也就是该方法依赖窗口

第三种方法,我喜欢。有什么缺点吗?如果没有,为啥Google上的朋友们都用第一种方法?如果有,是啥?

-----------------------

[update]

最近看到一个开源项目, 专门模拟键盘和鼠标,非常棒: 

http://inputsimulator.codeplex.com/

源代码打包下载 :

http://files.cnblogs.com/zhouyinhui/WindowsInput.zip  

使用方法嘛,比如:

 var sim = new InputSimulator();

 sim.Mouse.LeftButtonDown();