简体   繁体   中英

UI Automation Control Desktop Application and Click on Menu Strip

I am using UI Automation to click on a Menu Strip control.
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 . You can invoke a sub MenuItem after you have expanded it. This creates the MenuItem descendant objects. If you don't expand the Menu, it has no descendants so there's nothing to invoke.
The invocation is performd using an InvokePattern

To get the ExpandCollapsePattern and the InvokePattern , use the TryGetCurrentPattern method:

[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.

To note that a MenuBar Element has Children , while a MenuItem has Descendants . If you use the FindAll() method to search for children, you won't find any.

The Inspect utility is quite useful when coding an UI Automation procedure. It's usually located in:

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

A 32bit version is available ( \\bin\\x86\\ folder).

How to proceed:

  • Find the handle of main Window of the Application you want to interact with:
  • Get the MenuBar AutomationElement.
    • Note that the SystemMenu is also contained in a MenuBar , it can be determined using the Element Name, it contains: "System" instead of "Application" .
  • Enumerate the child elements of the MenuBar or FindFirst() one by Name.
    • Note, that the Menu items names and accelerators are localized.
  • Expand the Menu, using the ExpandCollapsePattern.Expand() method, to create its Descendants elements.
  • Find one of the Sub-Menu elements by Name or AutomationId or AccessKey .
  • Invoke the sub-menu action using the InvokePattern.Invoke() method.

Of course, the same actions can be repeated to expand and invoke the MenuItems of nested Sub-Menus.

Sample code and helper methods to find and expand the File Menu of Notepad.exe and invoke the Exit MenuItem action:

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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