簡體   English   中英

如何使用 WPF 的 UI 自動化向 AutomationElement 發送右鍵單擊事件?

[英]How can I send a right-click event to an AutomationElement using WPF's UI automation?

使用 WPF 的內置 UI 自動化,很容易(雖然非常冗長)向 AutomationElement(例如按鈕)發送左鍵單擊事件:

InvokePattern invokePattern = (InvokePattern) element.GetCurrentPattern(InvokePattern.Pattern);
invokePattern.Invoke();

但是,似乎沒有內置方法可以將右鍵單擊發送到同一元素。 我已經辭職使用 P/Invoke 來調用SendInput ,但我無法讓它工作。 使用下面的代碼,當我調用 RightClick() 時,會在 cursor 所在的位置彈出一個上下文菜單,而不是在我希望右鍵單擊的元素處。 所以它似乎忽略了我提交的坐標,只使用當前的 cursor 位置。

public static void RightClick(this AutomationElement element)
{
    Point p = element.GetClickablePoint();

    NativeStructs.Input input = new NativeStructs.Input
    {
        type = NativeEnums.SendInputEventType.Mouse,
        mouseInput = new NativeStructs.MouseInput
        {
            dx = (int) p.X,
            dy = (int) p.Y,
            mouseData = 0,
            dwFlags = NativeEnums.MouseEventFlags.Absolute | NativeEnums.MouseEventFlags.RightDown,
            time = 0,
            dwExtraInfo = IntPtr.Zero,
        },
    };

    NativeMethods.SendInput(1, ref input, Marshal.SizeOf(input));

    input.mouseInput.dwFlags = NativeEnums.MouseEventFlags.Absolute | NativeEnums.MouseEventFlags.RightUp;

    NativeMethods.SendInput(1, ref input, Marshal.SizeOf(input));
}

internal static class NativeMethods
{
    [DllImport("user32.dll", SetLastError = true)]
    internal static extern uint SendInput(uint nInputs, ref NativeStructs.Input pInputs, int cbSize);
}

internal static class NativeStructs
{
    [StructLayout(LayoutKind.Sequential)]
    internal struct Input
    {
        public NativeEnums.SendInputEventType type;
        public MouseInput mouseInput;
    }

    [StructLayout(LayoutKind.Sequential)]
    internal struct MouseInput
    {
        public int dx;
        public int dy;
        public uint mouseData;
        public NativeEnums.MouseEventFlags dwFlags;
        public uint time;
        public IntPtr dwExtraInfo;
    }
}

internal static class NativeEnums
{
    internal enum SendInputEventType : int
    {
        Mouse = 0,
        Keyboard = 1,
        Hardware = 2,
    }

    [Flags]
    internal enum MouseEventFlags : uint
    {
        Move = 0x0001,
        LeftDown = 0x0002,
        LeftUp = 0x0004,
        RightDown = 0x0008,
        RightUp = 0x0010,
        MiddleDown = 0x0020,
        MiddleUp = 0x0040,
        XDown = 0x0080,
        XUp = 0x0100,
        Wheel = 0x0800,
        Absolute = 0x8000,
    }
}

MSDN說:

如果指定了 MOUSEEVENTF_ABSOLUTE 值,則 dx 和 dy 包含0 到 65,535 之間的歸一化絕對坐標 事件過程將這些坐標映射到顯示表面上。 坐標(0,0)映射到顯示表面的左上角; 坐標 (65535,65535) 映射到右下角。 在多顯示器系統中,坐標 map 到主顯示器。

這意味着您不能只使用 pX 和 pY 您需要像這樣對其進行規范化,例如:

var virtualScreen = System.Windows.Forms.SystemInformation.VirtualScreen;
Int32 x = Convert.ToInt32((p.X - virtualScreen.Left) * 65536 / virtualScreen.Width);
Int32 y = Convert.ToInt32((p.Y - virtualScreen.Top) * 65536 / virtualScreen.Height);

並且您也需要使用MOUSEEVENTF_VIRTUALDESK標志對其進行 OR 操作。

據我了解,您認為 SendInput 必須模擬 UIA 元素的右鍵單擊是正確的。

至於如何在右鍵單擊之前強制 cursor 移動到您的元素,您可以嘗試將 MOUSEEVENTF_MOVE 標志添加到您的 dwFlags。

如果這仍然不起作用,也許嘗試調用 SendInput 兩次 - 一次移動鼠標(使用“dwFlags = MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE”),第二次按照您現在的方式執行右鍵單擊。

另外,你看過這個項目嗎?

http://inputsimulator.codeplex.com/

目前不確定它的鼠標輸入支持有多完善,但它可能很有用。

此外,這個問題也可能有用:

在 C 中使用帶有 sendInput 的鼠標

我檢查了所有答案並編譯了最終的工作解決方案。

public static void RightClick(this AutomationElement element)
{
    Point p = element.GetClickablePoint();

    NativeStructs.Input input = new NativeStructs.Input
    {
        type = NativeEnums.SendInputEventType.Mouse,
        mouseInput = new NativeStructs.MouseInput
        {
            dx = 0,
            dy = 0,
            mouseData = 0,
            dwFlags = NativeEnums.MouseEventFlags.Absolute | NativeEnums.MouseEventFlags.RightDown | NativeEnums.MouseEventFlags.Move,
            time = 0,
            dwExtraInfo = IntPtr.Zero,
        },
    };

        var primaryScreen = Screen.PrimaryScreen;
        input.mouseInput.dx = Convert.ToInt32((p.X - primaryScreen.Bounds.Left) * 65536 / primaryScreen.Bounds.Width);
        input.mouseInput.dy = Convert.ToInt32((p.Y - primaryScreen.Bounds.Top) * 65536 / primaryScreen.Bounds.Height);
    NativeMethods.SendInput(1, ref input, Marshal.SizeOf(input));
    input.mouseInput.dwFlags = NativeEnums.MouseEventFlags.Absolute | NativeEnums.MouseEventFlags.RightUp | NativeEnums.MouseEventFlags.Move;
    NativeMethods.SendInput(1, ref input, Marshal.SizeOf(input));
}

internal static class NativeMethods
{
    [DllImport("user32.dll", SetLastError = true)]
    internal static extern uint SendInput(uint nInputs, ref NativeStructs.Input pInputs, int cbSize);
}

internal static class NativeStructs
{
    [StructLayout(LayoutKind.Sequential)]
    internal struct Input
    {
        public NativeEnums.SendInputEventType type;
        public MouseInput mouseInput;
    }

    [StructLayout(LayoutKind.Sequential)]
    internal struct MouseInput
    {
        public int dx;
        public int dy;
        public uint mouseData;
        public NativeEnums.MouseEventFlags dwFlags;
        public uint time;
        public IntPtr dwExtraInfo;
    }
}

internal static class NativeEnums
{
    internal enum SendInputEventType : int
    {
        Mouse = 0,
        Keyboard = 1,
        Hardware = 2,
    }

    [Flags]
    internal enum MouseEventFlags : uint
    {
        Move = 0x0001,
        LeftDown = 0x0002,
        LeftUp = 0x0004,
        RightDown = 0x0008,
        RightUp = 0x0010,
        MiddleDown = 0x0020,
        MiddleUp = 0x0040,
        XDown = 0x0080,
        XUp = 0x0100,
        Wheel = 0x0800,
        Absolute = 0x8000,
    }
}

如果您從控件派生,則可以使用以下內容:

var e = new MouseButtonEventArgs(Mouse.PrimaryDevice, 0, MouseButton.Right);
e.RoutedEvent = Mouse.MouseDownEvent;
this.OnMouseDown(e);

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM