Detect the opening of a new window in C#

  • .NET
  • Windows

One can sometimes need to detect the opening of a new window to perform a treatment: close a popup, automatically enter some information…

In order to avoid using interop and the RegisterShellHookWindow method, UI Automation can be used. UI Automation provides programmatic UI access initially to create assistive software for people with disabilities, but it is also used to create automated graphical user interface (GUI) tests. And best of all (at least for this need) UI Automation provides events to indicate changes to the UI.

To use UI Automation, the following references must be added:

  • UIAutomationTypes
  • UIAutomationClient
static void Main(string[] args)
{
    System.Windows.Automation.Automation.AddAutomationEventHandler(
        eventId: WindowPattern.WindowOpenedEvent,
        element: AutomationElement.RootElement,
        scope: TreeScope.Children,
        eventHandler: OnWindowOpened);

    Console.ReadLine();
    Automation.RemoveAllEventHandlers();
}

private static void OnWindowOpened(object sender, AutomationEventArgs automationEventArgs)
{
    try
    {
        var element = sender as AutomationElement;
        if (element != null)
            Console.WriteLine("New Window opened: {0}", element.Current.Name);
    }
    catch (ElementNotAvailableException)
    {
    }
}

The code is very simple: we subscribe to the WindowOpened event for all elements of the UI (RootElement) and we retrieve the name of the element that corresponds to the title of the window. UI Automation makes it possible to restrict the events to a part of the GUI tree, which is not wanted in our case. At the end we must not forget to unsubscribe events.

This code, although convenient, is not really exciting so the next step is to detect the active window change. UI Automation provides an event to detect the change of focus:

System.Windows.Automation.Automation.AddAutomationFocusChangedEventHandler(OnFocusChanged);

This event is called each time a new control acquires focus and does not contain a filter for the windows. The idea is to go up the tree to find a window and to check if it is a new window:

static AutomationElement _lastWindow;

private static void OnFocusChanged(object sender, AutomationFocusChangedEventArgs e)
{
    AutomationElement elementFocused = sender as AutomationElement;
    if (elementFocused == null)
        return;

    try
    {
        AutomationElement topLevelWindow = GetParentWindow(elementFocused);
        if (topLevelWindow == null)
            return;

        if (topLevelWindow != _lastWindow)
        {
            _lastWindow = topLevelWindow;
            Console.WriteLine("Focus moved to window: {0}", topLevelWindow.Current.Name);
        }
        else
        {
            Console.WriteLine("Focused element: Type: '{0}', Name:'{1}'",
            elementFocused.Current.LocalizedControlType, elementFocused.Current.Name);
        }
    }
    catch (ElementNotAvailableException)
    {
    }
}

private static AutomationElement GetParentWindow(AutomationElement element)
{
    AutomationElement node = element;
    try
    {
        while (!Equals(node.Current.ControlType, ControlType.Window))
        {
            node = TreeWalker.ControlViewWalker.GetParent(node);
            if (node == null)
                return null;
        }

        return node;
    }
    catch (ElementNotAvailableException)
    {
    }

    return null;
}

Follow me:
Enjoy this blog?Buy Me A CoffeeDonate with PayPal

Leave a reply