简体   繁体   English

UI自动化控制桌面应用程序并单击菜单条

[英]UI Automation Control Desktop Application and Click on Menu Strip

I am using UI Automation to click on a Menu Strip control.我正在使用 UI 自动化单击菜单条控件。
I have already pre filled the textboxes and can now invoke the button as well.我已经预先填充了文本框,现在也可以调用按钮了。

But I would like to traverse to the Menu Bar select an option lets say File which should open its sub menu items and then click on a sub menu button, let's say Exit.但是我想遍历到菜单栏选择一个选项让我们说应该打开它的子​​菜单项的文件,然后单击子菜单按钮,比如说退出。

How can I achieve it, below is my code till now,我怎样才能实现它,下面是我的代码,直到现在,

AutomationElement rootElement = AutomationElement.RootElement;

if (rootElement != null)
{
    System.Windows.Automation.Condition condition = new PropertyCondition
        (AutomationElement.NameProperty, "This Is My Title");
    rootElement.FindAll(TreeScope.Children, condition1);
    AutomationElement appElement = rootElement.FindFirst(TreeScope.Children, condition);
    
    if (appElement != null)
    {
        foreach (var el in eles)
        {
            AutomationElement txtElementA = GetTextElement(appElement, el.textboxid);
            if (txtElementA != null)
            {
                ValuePattern valuePatternA =
                    txtElementA.GetCurrentPattern(ValuePattern.Pattern) as ValuePattern;
                valuePatternA.SetValue(el.value);
                el.found = true;
            }
        }

        System.Threading.Thread.Sleep(5000);
        System.Windows.Automation.Condition condition1 = new PropertyCondition
            (AutomationElement.AutomationIdProperty, "button1");
        AutomationElement btnElement = appElement.FindFirst
            (TreeScope.Descendants, condition1);

        InvokePattern btnPattern =
            btnElement.GetCurrentPattern(InvokePattern.Pattern) as InvokePattern;
        btnPattern.Invoke();

Menu Items support the ExpandCollapsePattern .菜单项支持ExpandCollapsePattern You can invoke a sub MenuItem after you have expanded it.您可以在展开子MenuItem后调用它。 This creates the MenuItem descendant objects.这将创建MenuItem后代对象。 If you don't expand the Menu, it has no descendants so there's nothing to invoke.如果您不展开 Menu,则它没有后代,因此没有任何可调用的内容。
The invocation is performd using an InvokePattern使用InvokePattern执行调用

To get the ExpandCollapsePattern and the InvokePattern , use the TryGetCurrentPattern method:要获取ExpandCollapsePatternInvokePattern ,请使用TryGetCurrentPattern方法:

[MenuItem].TryGetCurrentPattern(ExpandCollapsePattern.Pattern, out object pattern)

[MenuItem].TryGetCurrentPattern(InvokePattern.Pattern, out object pattern)

If the method returns a successful result, you can then call the Expand() and Invoke() methods.如果该方法返回成功的结果,则可以调用Expand()Invoke()方法。

To note that a MenuBar Element has Children , while a MenuItem has Descendants .要注意MenuBar元素有Children ,而MenuItemDescendants If you use the FindAll() method to search for children, you won't find any.如果您使用FindAll()方法搜索子项,您将找不到任何子项。

The Inspect utility is quite useful when coding an UI Automation procedure. Inspect 实用程序在编写 UI 自动化过程时非常有用。 It's usually located in:它通常位于:

 C:\\Program Files (x86)\\Windows Kits\\10\\bin\\x64\\inspect.exe

A 32bit version is available ( \\bin\\x86\\ folder). 32 位版本可用( \\bin\\x86\\文件夹)。

How to proceed:如何进行:

  • Find the handle of main Window of the Application you want to interact with:找到要与之交互的应用程序主窗口的句柄:
  • Get the MenuBar AutomationElement.获取MenuBar自动化MenuBar
    • Note that the SystemMenu is also contained in a MenuBar , it can be determined using the Element Name, it contains: "System" instead of "Application" .请注意, SystemMenu也包含在MenuBar ,可以使用元素名称确定它,它包含: "System"而不是"Application"
  • Enumerate the child elements of the MenuBar or FindFirst() one by Name.按名称枚举MenuBarFindFirst()的子元素。
    • Note, that the Menu items names and accelerators are localized.请注意,菜单项名称和加速键是本地化的。
  • Expand the Menu, using the ExpandCollapsePattern.Expand() method, to create its Descendants elements.使用ExpandCollapsePattern.Expand()方法展开 Menu,以创建其 Descendants 元素。
  • Find one of the Sub-Menu elements by Name or AutomationId or AccessKey .NameAutomationIdAccessKey查找子菜单元素AccessKey
  • Invoke the sub-menu action using the InvokePattern.Invoke() method.使用InvokePattern.Invoke()方法调用子菜单操作。

Of course, the same actions can be repeated to expand and invoke the MenuItems of nested Sub-Menus.当然,可以重复相同的操作来扩展和调用嵌套子菜单的 MenuItem。

Sample code and helper methods to find and expand the File Menu of Notepad.exe and invoke the Exit MenuItem action:用于查找和展开Notepad.exe的文件菜单并调用Exit MenuItem 操作的示例代码和帮助程序方法:

public void CloseNotepad()
{
    IntPtr hWnd = IntPtr.Zero;
    using (Process p = Process.GetProcessesByName("notepad").FirstOrDefault()) {
        hWnd = p.MainWindowHandle;
    }
    if (hWnd == IntPtr.Zero) return;
    var window = GetMainWindowElement(hWnd);
    var menuBar = GetWindowMenuBarElement(window);
    var fileMenu = GetMenuBarMenuByName(menuBar, "File");

    if (fileMenu is null) return;

    // var fileSubMenus = GetMenuSubMenuList(fileMenu);
    bool result = InvokeSubMenuItemByName(fileMenu, "Exit", true);
}

private AutomationElement GetMainWindowElement(IntPtr hWnd) 
    => AutomationElement.FromHandle(hWnd) as AutomationElement;

private AutomationElement GetWindowMenuBarElement(AutomationElement window)
{
    var condMenuBar = new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.MenuBar);
    var menuBar = window.FindAll(TreeScope.Descendants, condMenuBar)
        .OfType<AutomationElement>().FirstOrDefault(ui => !ui.Current.Name.Contains("System"));
    return menuBar;
}

private AutomationElement GetMenuBarMenuByName(AutomationElement menuBar, string menuName)
{
    var condition = new AndCondition(
        new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.MenuItem),
        new PropertyCondition(AutomationElement.NameProperty, menuName)
    );
    if (menuBar.Current.ControlType != ControlType.MenuBar) return null;
    var menuItem = menuBar.FindFirst(TreeScope.Children, condition);
    return menuItem;
}

private List<AutomationElement> GetMenuSubMenuList(AutomationElement menu)
{
    if (menu.Current.ControlType != ControlType.MenuItem) return null;
    ExpandMenu(menu);
    var submenus = new List<AutomationElement>();
    submenus.AddRange(menu.FindAll(TreeScope.Descendants,
        new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.MenuItem))
                                               .OfType<AutomationElement>().ToArray());
    return submenus;
}

private bool InvokeSubMenuItemByName(AutomationElement menuItem, string menuName, bool exactMatch)
{
    var subMenus = GetMenuSubMenuList(menuItem);
    AutomationElement namedMenu = null;
    if (exactMatch) {
        namedMenu = subMenus.FirstOrDefault(elm => elm.Current.Name.Equals(menuName));
    }
    else {
        namedMenu = subMenus.FirstOrDefault(elm => elm.Current.Name.Contains(menuName));
    }
    if (namedMenu is null) return false;
    InvokeMenu(namedMenu);
    return true;
}

private void ExpandMenu(AutomationElement menu)
{
    if (menu.TryGetCurrentPattern(ExpandCollapsePattern.Pattern, out object pattern))
    {
        (pattern as ExpandCollapsePattern).Expand();
    }
}

private void InvokeMenu(AutomationElement menu)
{
    if (menu.TryGetCurrentPattern(InvokePattern.Pattern, out object pattern))
    {
        (pattern as InvokePattern).Invoke();
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM