Detect the opening of a new window in C#
One can sometimes need to detect the opening of a new window to perform a treatment: close a popup, automatically enter some information…
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 an 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
If you are targeting .NET 5+, you need to use a TFM that contains -windows
, such as <TargetFramework>net6.0-windows</TargetFramework>
, and set <UseWPF>true</UseWPF>
.
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. In the end, we must not forget to unsubscribe events.
This code, although convenient, is not 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;
}
Do you have a question or a suggestion about this post? Contact me!