On peut parfois avoir besoin de détecter l’ouverture d’une nouvelle fenêtre afin d’effectuer un traitement : fermer une popup, saisir automatiquement certaines informations…
Afin d’éviter d’utiliser l’interop et la méthode RegisterShellHookWindow, on peut utiliser UI Automation. UI Automation permet d’accéder à l’UI de manière programmatique initialement pour créer des logiciels d’assistance pour les personnes handicapées mais on s’en sert aussi pour créer des tests automatisés d’interface graphique. Et le meilleur pour finir (au moins pour ce besoin) UI Automation fournit des évènements pour indiquer des changements de l’UI.
Pour utiliser UI Automation, il faut ajouter les références suivantes :
- 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)
{
}
}
Le code est très simple : on s’abonne à l’évènement WindowOpened pour tous les éléments de l’UI (RootElement) et on récupère le nom de l’élément qui correspond au titre de la fenêtre. UI Automation permet de restreindre les évènements à une partie de l’arbre de l’interface graphique, ce qui n’est pas voulu dans notre cas. A la fin il ne faut pas oublier de se désabonner des évènements.
Attention : Avec UI Automation il faut être très préventif au niveau du code d’où le try catch. En effet l’élément peut ne plus exister au moment où on essaye d’y accéder (par exemple si la fenêtre est fermée).
Ce code, bien que pratique, n’est pas vraiment passionnant donc l’étape d’après est de détecter le changement de fenêtre active. UI Automation fournit un évènement pour détecter le changement de focus :
System.Windows.Automation.Automation.AddAutomationFocusChangedEventHandler(OnFocusChanged);
Cet évènement est appelé à chaque fois un nouveau contrôle acquière focus et ne contient pas de filtre pour les fenêtres. L’idée est donc de remonter l’arbre jusqu’à trouver une fenêtre (window) et de vérifier s’il s’agit d’une nouvelle fenêtre :
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;
}