/// <summary> /// Get an App element from uia /// it is trace back to the top most ancestor with same process Id. /// </summary> /// <param name="e">A11yElement</param> /// <returns></returns> public static A11yElement GetAppElement(A11yElement e) { var walker = GetTreeWalker(TreeViewMode.Control); var tree = new DesktopElementAncestry(TreeViewMode.Control, e); Marshal.ReleaseComObject(walker); A11yElement app = tree.First; // make sure that we have an app first if (app != null) { // get first item under Desktop. if app is not Desktop, then it is ok to take it as is. if (app.IsRootElement()) { app = app.Children.First(); } tree.Items.Remove(app); tree.Items.ForEach(el => el.Dispose()); // make sure that Unique ID is set to 0 since this element will be POI. app.UniqueId = 0; } return(app); }
/// <summary> /// Select the specified element if it meets all eligibilty requirements /// </summary> /// <param name="element">The potential element to select</param> /// <returns>true if the element was selected</returns> protected bool SelectElementIfItIsEligible(A11yElement element) { if (element != null && !element.IsRootElement() && !element.IsSameUIElement(SelectedElementRuntimeId, SelectedBoundingRectangle, SelectedControlTypeId, SelectedName)) { SelectedElementRuntimeId = element.RuntimeId; SelectedBoundingRectangle = element.BoundingRectangle; SelectedControlTypeId = element.ControlTypeId; SelectedName = element.Name; SetElement?.Invoke(element); return(true); } return(false); }
/// <summary> /// Set element for Next selection call /// it is for internal use. /// </summary> /// <param name="el"></param> public void SetCandidateElement(A11yElement el) { lock (this) { if (el != null) { if (el.IsRootElement() == false) { this.CandidateEC?.Dispose(); this.CandidateEC = new ElementContext(el); } else { el.Dispose(); } } } }
/// <summary> /// Set Parent to build ancestry tree /// </summary> /// <param name="e"></param> /// <param name="uniqueId"></param> private void SetParent(A11yElement e, int uniqueId) { if (e == null || e.PlatformObject == null || e.IsRootElement()) { return; } try { var puia = this.TreeWalker.GetParentElement((IUIAutomationElement)e.PlatformObject); if (puia == null) { return; } #pragma warning disable CA2000 // Call IDisposable.Dispose() var parent = new DesktopElement(puia, true, SetMembers); parent.PopulateMinimumPropertiesForSelection(); // we need to avoid infinite loop of self reference as parent. // it is a probably a bug in UIA or the target app. if (e.IsSameUIElement(parent) == false) { parent.IsAncestorOfSelected = true; parent.Children.Add(e); e.Parent = parent; this.Items.Add(parent); parent.UniqueId = uniqueId; SetParent(parent, uniqueId - 1); } #pragma warning restore CA2000 } #pragma warning disable CA1031 // Do not catch general exception types catch (Exception ex) { ex.ReportException(); // ignore to show the best efforts. System.Diagnostics.Trace.WriteLine(ex); } #pragma warning restore CA1031 // Do not catch general exception types }
/// <summary> /// Gets window that is eventual parent of element. If there is no window parent, get /// highest element that isn't desktop and has a bounding rectangle /// </summary> /// <param name="el"></param> /// <returns></returns> public static A11yElement GetParentWindow(this A11yElement el) { A11yElement prev = null; A11yElement curr = el; while (curr != null && curr.Parent != null && curr.ControlTypeId != ControlType.UIA_WindowControlTypeId && !curr.IsRootElement()) { prev = curr; curr = curr.Parent; } if (curr.ControlTypeId != ControlType.UIA_WindowControlTypeId && prev != null) { curr = prev; while (curr.UniqueId < -1 && curr.Children?[0].BoundingRectangle == null) { if (curr.Children.Count > 0) { curr = curr.Children[0]; } else { // if no elements have bounding rectangles, give up break; } } } return(curr); }