forked from ygoe/MultiSelectTreeView
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathMouse.cs
More file actions
339 lines (303 loc) · 13.6 KB
/
Copy pathMouse.cs
File metadata and controls
339 lines (303 loc) · 13.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
namespace MultiSelectTreeView.Test.Model.Helper
{
using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Threading;
using System.Windows;
using System.Windows.Automation;
using Microsoft.VisualStudio.TestTools.UnitTesting;
/// <summary>
/// Class for Keyboard use
/// </summary>
public static class Mouse
{
#region Constants
const int SM_SWAPBUTTON = 23;
const int SM_XVIRTUALSCREEN = 76;
const int SM_YVIRTUALSCREEN = 77;
const int SM_CXVIRTUALSCREEN = 78;
const int SM_CYVIRTUALSCREEN = 79;
const int MOUSEEVENTF_VIRTUALDESK = 0x4000;
#endregion
#region Specific tree methods
internal static void ExpandCollapseClick(Element element)
{
bool oldExpandState = element.IsExpanded;
AutomationElement toggleButton = element.Ae.FindDescendantByAutomationId(ControlType.Button, "Expander");
try
{
Click(toggleButton.GetClickablePoint());
}
catch
{
}
SleepAfter();
if (oldExpandState == element.IsExpanded)
throw new InvalidOperationException("Changing expand state did not work");
}
internal static void Click(Element element)
{
Click(element.Ae);
SleepAfter();
}
internal static void ShiftClick(Element element)
{
Keyboard.HoldShift();
Click(element.Ae);
SleepBetween();
Keyboard.ReleaseShift();
}
internal static void CtrlClick(Element element)
{
Keyboard.HoldCtrl();
Click(element.Ae);
SleepBetween();
Keyboard.ReleaseCtrl();
}
private static void SleepAfter()
{
Thread.Sleep(500);
}
private static void SleepBetween()
{
Thread.Sleep(10);
}
#endregion
#region Private methods
/// <summary>
/// Inject pointer input into the system
/// </summary>
/// <param name="x">x coordinate of pointer, if Move flag specified</param>
/// <param name="y">y coordinate of pointer, if Move flag specified</param>
/// <param name="data">wheel movement, or mouse X button, depending on flags</param>
/// <param name="flags">flags to indicate which type of input occurred - move, button press/release, wheel move, etc.</param>
/// <remarks>x, y are in pixels. If Absolute flag used, are relative to desktop origin.</remarks>
/// <outside_see conditional="false">
/// This API does not work inside the secure execution environment.
/// <exception cref="System.Security.Permissions.SecurityPermission"/>
/// </outside_see>
private static void SendMouseInput(double x, double y, int data, SendMouseInputFlags flags)
{
//CASRemoval:AutomationPermission.Demand( AutomationPermissionFlag.Input );
int intflags = (int)flags;
if ((intflags & (int)SendMouseInputFlags.Absolute) != 0)
{
int vscreenWidth = GetSystemMetrics(SM_CXVIRTUALSCREEN);
int vscreenHeight = GetSystemMetrics(SM_CYVIRTUALSCREEN);
int vscreenLeft = GetSystemMetrics(SM_XVIRTUALSCREEN);
int vscreenTop = GetSystemMetrics(SM_YVIRTUALSCREEN);
// Absolute input requires that input is in 'normalized' coords - with the entire
// desktop being (0,0)...(65535,65536). Need to convert input x,y coords to this
// first.
//
// In this normalized world, any pixel on the screen corresponds to a block of values
// of normalized coords - eg. on a 1024x768 screen,
// y pixel 0 corresponds to range 0 to 85.333,
// y pixel 1 corresponds to range 85.333 to 170.666,
// y pixel 2 correpsonds to range 170.666 to 256 - and so on.
// Doing basic scaling math - (x-top)*65536/Width - gets us the start of the range.
// However, because int math is used, this can end up being rounded into the wrong
// pixel. For example, if we wanted pixel 1, we'd get 85.333, but that comes out as
// 85 as an int, which falls into pixel 0's range - and that's where the pointer goes.
// To avoid this, we add on half-a-"screen pixel"'s worth of normalized coords - to
// push us into the middle of any given pixel's range - that's the 65536/(Width*2)
// part of the formula. So now pixel 1 maps to 85+42 = 127 - which is comfortably
// in the middle of that pixel's block.
// The key ting here is that unlike points in coordinate geometry, pixels take up
// space, so are often better treated like rectangles - and if you want to target
// a particular pixel, target its rectangle's midpoint, not its edge.
x = ((x - vscreenLeft) * 65536) / vscreenWidth + 65536 / (vscreenWidth * 2);
y = ((y - vscreenTop) * 65536) / vscreenHeight + 65536 / (vscreenHeight * 2);
intflags |= MOUSEEVENTF_VIRTUALDESK;
}
INPUT mi = new INPUT();
mi.type = Const.INPUT_MOUSE;
mi.union.mouseInput.dx = (int)x;
mi.union.mouseInput.dy = (int)y;
mi.union.mouseInput.mouseData = data;
mi.union.mouseInput.dwFlags = intflags;
mi.union.mouseInput.time = 0;
mi.union.mouseInput.dwExtraInfo = new IntPtr(0);
//Console.WriteLine("Sending");
if (SendInput(1, ref mi, Marshal.SizeOf(mi)) == 0)
throw new Win32Exception(Marshal.GetLastWin32Error());
}
#endregion
#region Public methods
public static Point Loaction()
{
Win32Point w32Mouse = new Win32Point();
GetCursorPos(ref w32Mouse);
return new Point(w32Mouse.X, w32Mouse.Y);
}
/// <summary>
/// Move the mouse to a point.
/// </summary>
/// <param name="pt">The point that the mouse will move to.</param>
/// <remarks>pt are in pixels that are relative to desktop origin.</remarks>
///
/// <outside_see conditional="false">
/// This API does not work inside the secure execution environment.
/// <exception cref="System.Security.Permissions.SecurityPermission"/>
/// </outside_see>
public static void MoveTo(Point pt)
{
SendMouseInput(pt.X, pt.Y, 0, SendMouseInputFlags.Move | SendMouseInputFlags.Absolute);
}
/// <summary>
/// Move the mouse to an element.
/// </summary>
/// <param name="el">The element that the mouse will move to</param>
/// <exception cref="NoClickablePointException">If there is not clickable point for the element</exception>
///
/// <outside_see conditional="false">
/// This API does not work inside the secure execution environment.
/// <exception cref="System.Security.Permissions.SecurityPermission"/>
/// </outside_see>
public static void MoveTo(AutomationElement el)
{
if (el == null)
{
throw new ArgumentNullException("el");
}
MoveTo(el.GetClickablePoint());
}
/// <summary>
/// Move the mouse to a point and click. The primary mouse button will be used
/// this is usually the left button except if the mouse buttons are swaped.
/// </summary>
/// <param name="pt">The point to click at</param>
/// <remarks>pt are in pixels that are relative to desktop origin.</remarks>
///
/// <outside_see conditional="false">
/// This API does not work inside the secure execution environment.
/// <exception cref="System.Security.Permissions.SecurityPermission"/>
/// </outside_see>
public static void MoveToAndClick(Point pt)
{
SendMouseInput(pt.X, pt.Y, 0, SendMouseInputFlags.Move | SendMouseInputFlags.Absolute);
RawClick(pt, true);
}
/// <summary>
/// Move the mouse to a point and double click. The primary mouse button will be used
/// this is usually the left button except if the mouse buttons are swaped.
/// </summary>
/// <param name="pt">The point to click at</param>
/// <remarks>pt are in pixels that are relative to desktop origin.</remarks>
///
/// <outside_see conditional="false">
/// This API does not work inside the secure execution environment.
/// <exception cref="System.Security.Permissions.SecurityPermission"/>
/// </outside_see>
public static void MoveToAndDoubleClick(Point pt)
{
SendMouseInput(pt.X, pt.Y, 0, SendMouseInputFlags.Move | SendMouseInputFlags.Absolute);
RawClick(pt, true);
Thread.Sleep(300);
RawClick(pt, true);
}
private static void RawClick(Point pt, bool left)
{
// send SendMouseInput works in term of the phisical mouse buttons, therefore we need
// to check to see if the mouse buttons are swapped because this method need to use the primary
// mouse button.
if (ButtonsSwapped() != left)
{
// the mouse buttons are not swaped the primary is the left
SendMouseInput(pt.X, pt.Y, 0, SendMouseInputFlags.LeftDown | SendMouseInputFlags.Absolute);
SendMouseInput(pt.X, pt.Y, 0, SendMouseInputFlags.LeftUp | SendMouseInputFlags.Absolute);
}
else
{
// the mouse buttons are swaped so click the right button which as actually the primary
SendMouseInput(pt.X, pt.Y, 0, SendMouseInputFlags.RightDown | SendMouseInputFlags.Absolute);
SendMouseInput(pt.X, pt.Y, 0, SendMouseInputFlags.RightUp | SendMouseInputFlags.Absolute);
}
}
private static bool ButtonsSwapped()
{
return GetSystemMetrics(SM_SWAPBUTTON) != 0;
}
public static void MoveToAndRightClick(Point pt)
{
SendMouseInput(pt.X, pt.Y, 0, SendMouseInputFlags.Move | SendMouseInputFlags.Absolute);
RawClick(pt, false);
}
public static void Click(Point point)
{
MoveToAndClick(point);
}
private static void Click(AutomationElement element)
{
try
{
MoveToAndClick(element.GetClickablePoint());
}
catch
{
}
}
internal static void DoubleClick(Element element)
{
DoubleClick(element.Ae);
SleepAfter();
}
private static void DoubleClick(AutomationElement element)
{
MoveToAndDoubleClick(element.GetClickablePoint());
}
private static void RightClick(Point point)
{
MoveToAndRightClick(point);
}
#endregion
#region Native types
/// <summary>
/// Flags for Input.SendMouseInput, indicate whether movent took place,
/// or whether buttons were pressed or released.
/// </summary>
[Flags]
public enum SendMouseInputFlags
{
/// <summary>Specifies that the pointer moved.</summary>
Move = 0x0001,
/// <summary>Specifies that the left button was pressed.</summary>
LeftDown = 0x0002,
/// <summary>Specifies that the left button was released.</summary>
LeftUp = 0x0004,
/// <summary>Specifies that the right button was pressed.</summary>
RightDown = 0x0008,
/// <summary>Specifies that the right button was released.</summary>
RightUp = 0x0010,
/// <summary>Specifies that the middle button was pressed.</summary>
MiddleDown = 0x0020,
/// <summary>Specifies that the middle button was released.</summary>
MiddleUp = 0x0040,
/// <summary>Specifies that the x button was pressed.</summary>
XDown = 0x0080,
/// <summary>Specifies that the x button was released. </summary>
XUp = 0x0100,
/// <summary>Specifies that the wheel was moved</summary>
Wheel = 0x0800,
/// <summary>Specifies that x, y are absolute, not relative</summary>
Absolute = 0x8000,
};
[StructLayout(LayoutKind.Sequential)]
internal struct Win32Point
{
public Int32 X;
public Int32 Y;
};
#endregion
#region Native methods
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool GetCursorPos(ref Win32Point pt);
[DllImport("user32.dll")]
public static extern int GetSystemMetrics(int metric);
[DllImport("user32.dll", SetLastError = true)]
public static extern int SendInput(int nInputs, ref INPUT mi, int cbSize);
#endregion
}
}