GPAL - Generally Positive Automation Library v1.0
GPAL The Fluent Automation LIbrary
Loading...
Searching...
No Matches
AutomationElementExtension.cs
1// =============================================================================
2// GPAL - Generally Positive Automation Library
3// Copyright © 2026 Software Decisions, Inc. All rights reserved.
4//
5// This file is part of GPAL.
6// Licensed under the Business Source License 1.1
7//
8// Primary development, architecture, and vision by Michael B. Vederman,
9// CEO of Software Decisions, Inc., Texas.
10//
11// Internal development maintained privately.
12// Public releases appear on GitHub: https://github.com/SoftwareDecisionsInc/GPAL.
13//
14// See LICENSE for full terms, including Additional Use Grant.
15// =============================================================================
16
17using System;
18using System.Collections.Generic;
19using System.Collections.ObjectModel;
20using System.Diagnostics;
21using System.Linq;
22using System.Text;
23using System.Threading.Tasks;
24using System.Windows.Automation;
25using System.Windows.Automation.Peers;
26using static GenerallyPositive.Enums;
27
29{
30 internal static class AutomationElementExtension
31 {
32 public static ReadOnlyCollection<GPALAutomationElement> FindElements(
33 this AutomationElement element,
34 string findByString,
35 ConditionPropertyType conditionPropertyType,
36 int idx = 0,
37 bool isRoot = false)
38 {
39 if (element == null)
40 throw new ArgumentNullException(nameof(element));
41
42 PropertyCondition condition = null;
43 ControlType controlType = null;
44
45 switch (conditionPropertyType)
46 {
47 case ConditionPropertyType.AutomationID:
48 condition = new PropertyCondition(AutomationElement.AutomationIdProperty, findByString);
49 break;
50 case ConditionPropertyType.ClassName:
51 condition = new PropertyCondition(AutomationElement.ClassNameProperty, findByString);
52 break;
53 case ConditionPropertyType.ControlType:
54 controlType = GetControlType(findByString);
55 condition = new PropertyCondition(AutomationElement.ControlTypeProperty, controlType);
56 break;
57 case ConditionPropertyType.Name:
58 condition = new PropertyCondition(AutomationElement.NameProperty, findByString);
59 break;
60 case ConditionPropertyType.Value:
61 condition = new PropertyCondition(ValuePattern.ValueProperty, findByString);
62 break;
63 default:
64 throw new ArgumentException("Unsupported ConditionPropertyType");
65 }
66
67 TreeScope treeScope = isRoot ? TreeScope.Subtree : TreeScope.Children;
68
69 List<GPALAutomationElement> tmpElems = new List<GPALAutomationElement>();
70
71 if (isRoot)
72 {
73 AutomationElement found = element.FindFirst(treeScope, condition);
74 if (found != null)
75 tmpElems.Add(new GPALAutomationElement(found));
76 }
77 else
78 {
79 // Your existing custom logic – kept unchanged
80 List<AutomationElement> automationElements = GetAllChildren(element, idx, controlType);
81 foreach (AutomationElement elem in automationElements)
82 tmpElems.Add(new GPALAutomationElement(elem));
83 }
84
85 return new ReadOnlyCollection<GPALAutomationElement>(tmpElems);
86 }
87 /*
88 public static ReadOnlyCollection<GPALAutomationElement> FindElements(this AutomationElement element, string findByString, ConditionPropertyType conditionPropertyType, int idx = 0, bool isRoot = false)
89 {
90 ReadOnlyCollection<GPALAutomationElement> elems = null;
91 List<GPALAutomationElement> tmpElems = new List<GPALAutomationElement>();
92 PropertyCondition condition = null;
93 ControlType controlType = null;
94
95 switch (conditionPropertyType)
96 {
97 case ConditionPropertyType.AutomationID:
98 condition = new PropertyCondition(AutomationElement.AutomationIdProperty, findByString);
99 break;
100 case ConditionPropertyType.ClassName:
101 condition = new PropertyCondition(AutomationElement.ClassNameProperty, findByString);
102 break;
103 case ConditionPropertyType.ControlType:
104 controlType = GetControlType(findByString);
105 condition = new PropertyCondition(AutomationElement.ControlTypeProperty, controlType);
106 break;
107 case ConditionPropertyType.Name:
108 condition = new PropertyCondition(AutomationElement.NameProperty, findByString);
109 break;
110 case ConditionPropertyType.Value:
111 condition = new PropertyCondition(System.Windows.Automation.ValuePattern.ValueProperty, findByString);
112 break;
113 }
114 TreeScope treeScope = System.Windows.Automation.TreeScope.Children;
115 if (true == isRoot)
116 treeScope = System.Windows.Automation.TreeScope.Subtree;
117 List<AutomationElement> automationElements = new List<AutomationElement>();
118 AutomationElement automationElement;
119 if (true == isRoot)
120 {
121 automationElement = element.FindFirst(treeScope, condition);
122 tmpElems.Add(new GPALAutomationElement(automationElement));
123 }
124 else
125 {
126 //AutomationElementCollection automationElems;
127 // automationElems = element.FindAll(treeScope, condition);
128 // foreach (AutomationElement elem in automationElems)
129 // tmpElems.Add(new GPALAutomationElement(elem));
130 //automationElement = element.FindFirst(treeScope, condition);
131
132 automationElements = GetAllChildren(element, idx, controlType);
133 foreach (AutomationElement elem in automationElements)
134 tmpElems.Add(new GPALAutomationElement(elem));
135
136
137 //if (0 == automationElems.Count)
138 //{
139 // treeScope = System.Windows.Automation.TreeScope.Subtree;
140 // automationElems = element.FindAll(treeScope, condition);
141 // if (0 == automationElems.Count)
142 // {
143 // treeScope = System.Windows.Automation.TreeScope.Descendants;
144 // automationElems = element.FindAll(treeScope, condition);
145 // }
146 //}
147 //foreach (AutomationElement elem in automationElems)
148 // tmpElems.Add(new GPALAutomationElement(elem));
149 }
150
151 elems = new ReadOnlyCollection<GPALAutomationElement>(tmpElems);
152 return elems;
153 }
154 */
155 private static List<AutomationElement> GetAllChildren(AutomationElement rootElement, int idx, ControlType controlType)
156 {
157 Condition condition = new PropertyCondition(AutomationElement.IsControlElementProperty, true);
158 TreeWalker walker = new TreeWalker(condition);
159 AutomationElement elementNode = walker.GetFirstChild(rootElement);
160 int cnt = 0;
161
162 if (null == elementNode) return null;
163
164 List<AutomationElement> automationElements = new List<AutomationElement>();
165
166 while (elementNode != null && cnt <= idx) // only get as many children as we require
167 {
168 if (controlType == elementNode.Current.ControlType)
169 {
170 automationElements.Add(elementNode);
171 cnt++;
172 }
173
174 elementNode = walker.GetNextSibling(elementNode);
175 }
176
177 return automationElements;
178 }
179 public static string GetText(this AutomationElement element)
180 {
181 object patternObj;
182 // LegacyIAccessiblePattern only accessible in UIACommWrapperX
183 //if (element.TryGetCurrentPattern(LegacyIAccessiblePattern.Pattern, out patternObj))
184 //{
185 // var legacyPattern = (LegacyIAccessiblePattern)patternObj;
186 // return legacyPattern.Current.Value;
187 //}
188 //else
189 if (element.TryGetCurrentPattern(ValuePattern.Pattern, out patternObj))
190 {
191 var valuePattern = (ValuePattern)patternObj;
192 return valuePattern.Current.Value;
193 }
194 else if (element.TryGetCurrentPattern(TextPattern.Pattern, out patternObj))
195 {
196 var textPattern = (TextPattern)patternObj;
197 return textPattern.DocumentRange.GetText(-1).TrimEnd('\r'); // often there is an extra '\r' hanging off the end.
198 }
199 else
200 {
201 return element.Current.Name;
202 }
203 }
204
205 internal static ControlType GetControlType(string controlTypeString)
206 {
207 if (null == controlTypeString) return null;
208
209 AutomationControlType typeEnum;
210 bool result = Enum.TryParse(controlTypeString, true, out typeEnum);
211 if (result) typeEnum = (AutomationControlType)Enum.Parse(typeof(AutomationControlType), controlTypeString);
212 int enumId = (int)typeEnum + 50000;
213 ControlType controlType = ControlType.LookupById(enumId);
214
215 return controlType;
216 }
217 }
218}
219
220