[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();