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 Kits10inx64inspect.exe
A 32bit version is available (inx86
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();
}
}