/src/FlaUI.Core/AutomationElements/Window.cs

https://github.com/Roemer/FlaUI · C# · 167 lines · 107 code · 13 blank · 47 comment · 15 complexity · 8c66ffa279563ac05b4310982b41b1ea MD5 · raw file

  1. using System;
  2. using System.ComponentModel;
  3. using System.Linq;
  4. using System.Runtime.InteropServices;
  5. using FlaUI.Core.Conditions;
  6. using FlaUI.Core.Definitions;
  7. using FlaUI.Core.Exceptions;
  8. using FlaUI.Core.WindowsAPI;
  9. namespace FlaUI.Core.AutomationElements
  10. {
  11. /// <summary>
  12. /// Class to interact with a window element.
  13. /// </summary>
  14. public class Window : AutomationElement
  15. {
  16. /// <summary>
  17. /// Creates a <see cref="Window"/> element.
  18. /// </summary>
  19. public Window(FrameworkAutomationElementBase frameworkAutomationElement) : base(frameworkAutomationElement)
  20. {
  21. }
  22. /// <summary>
  23. /// Gets the title of the window.
  24. /// </summary>
  25. public string Title => Properties.Name.Value;
  26. /// <summary>
  27. /// Gets if the window is modal.
  28. /// </summary>
  29. public bool IsModal => Patterns.Window.Pattern.IsModal.Value;
  30. /// <summary>
  31. /// Gets the <see cref="TitleBar"/> of the window.
  32. /// </summary>
  33. public TitleBar TitleBar => FindFirstChild(cf => cf.ByControlType(ControlType.TitleBar))?.AsTitleBar();
  34. /// <summary>
  35. /// Flag to indicate, if the window is the application's main window.
  36. /// Is used so that it does not need to be looked up again in some cases (e.g. Context Menu).
  37. /// </summary>
  38. internal bool IsMainWindow { get; set; }
  39. /// <summary>
  40. /// Gets a list of all modal child windows.
  41. /// </summary>
  42. public Window[] ModalWindows
  43. {
  44. get
  45. {
  46. return FindAllChildren(cf =>
  47. cf.ByControlType(ControlType.Window).
  48. And(new PropertyCondition(Automation.PropertyLibrary.Window.IsModal, true))
  49. ).Select(e => e.AsWindow()).ToArray();
  50. }
  51. }
  52. /// <summary>
  53. /// Gets the current WPF popup window.
  54. /// </summary>
  55. public Window Popup
  56. {
  57. get
  58. {
  59. var mainWindow = GetMainWindow();
  60. var popup = mainWindow.FindFirstChild(cf => cf.ByControlType(ControlType.Window).And(cf.ByText(String.Empty).And(cf.ByClassName("Popup"))));
  61. return popup?.AsWindow();
  62. }
  63. }
  64. /// <summary>
  65. /// Gets the context menu for the window.
  66. /// Note: It uses the FrameworkType of the window as lookup logic. Use <see cref="GetContextMenuByFrameworkType" /> if you want to control this.
  67. /// </summary>
  68. public Menu ContextMenu => GetContextMenuByFrameworkType(FrameworkType);
  69. /// <summary>
  70. /// Gets the context menu by a given <see cref="FrameworkType"/>.
  71. /// </summary>
  72. public Menu GetContextMenuByFrameworkType(FrameworkType frameworkType)
  73. {
  74. if (frameworkType == FrameworkType.Win32)
  75. {
  76. // The main menu is directly under the desktop with the name "Context" or in a few cases "System"
  77. var desktop = FrameworkAutomationElement.Automation.GetDesktop();
  78. var nameCondition = ConditionFactory.ByName("Context").Or(ConditionFactory.ByName("System"));
  79. var ctxMenu = desktop.FindFirstChild(cf => cf.ByControlType(ControlType.Menu).And(nameCondition)).AsMenu();
  80. if (ctxMenu != null)
  81. {
  82. ctxMenu.IsWin32Menu = true;
  83. return ctxMenu;
  84. }
  85. }
  86. var mainWindow = GetMainWindow();
  87. if (frameworkType == FrameworkType.WinForms)
  88. {
  89. var ctxMenu = mainWindow.FindFirstChild(cf => cf.ByControlType(ControlType.Menu).And(cf.ByName("DropDown")));
  90. return ctxMenu.AsMenu();
  91. }
  92. if (frameworkType == FrameworkType.Wpf)
  93. {
  94. // In WPF, there is a window (Popup) where the menu is inside
  95. var popup = Popup;
  96. var ctxMenu = popup.FindFirstChild(cf => cf.ByControlType(ControlType.Menu));
  97. return ctxMenu.AsMenu();
  98. }
  99. // No menu found
  100. return null;
  101. }
  102. /// <summary>
  103. /// Closes the window.
  104. /// </summary>
  105. public void Close()
  106. {
  107. var titleBar = TitleBar;
  108. if (titleBar?.CloseButton != null)
  109. {
  110. titleBar.CloseButton.Invoke();
  111. return;
  112. }
  113. if (Patterns.Window.TryGetPattern(out var windowPattern))
  114. {
  115. windowPattern.Close();
  116. return;
  117. }
  118. throw new MethodNotSupportedException("Close is not supported");
  119. }
  120. /// <summary>
  121. /// Moves the window to the given coordinates.
  122. /// </summary>
  123. public void Move(int x, int y)
  124. {
  125. Patterns.Transform.PatternOrDefault?.Move(x, y);
  126. }
  127. /// <summary>
  128. /// Brings the element to the foreground.
  129. /// </summary>
  130. public void SetTransparency(byte alpha)
  131. {
  132. if (User32.SetWindowLong(Properties.NativeWindowHandle, WindowLongParam.GWL_EXSTYLE, WindowStyles.WS_EX_LAYERED) == 0)
  133. {
  134. throw new Win32Exception(Marshal.GetLastWin32Error());
  135. }
  136. if (!User32.SetLayeredWindowAttributes(Properties.NativeWindowHandle, 0, alpha, LayeredWindowAttributes.LWA_ALPHA))
  137. {
  138. throw new Win32Exception(Marshal.GetLastWin32Error());
  139. }
  140. }
  141. /// <summary>
  142. /// Gets the main window (first window on desktop with the same process as this window).
  143. /// </summary>
  144. private Window GetMainWindow()
  145. {
  146. if (IsMainWindow)
  147. {
  148. return this;
  149. }
  150. var mainWindow = Automation.GetDesktop().FindFirstChild(cf => cf.ByProcessId(Properties.ProcessId.Value)).AsWindow();
  151. return mainWindow ?? this;
  152. }
  153. }
  154. }