4
4
5
5
using System ;
6
6
using System . Collections . Generic ;
7
+ using Microsoft . Toolkit . Uwp . UI . Helpers . Internals ;
7
8
using Microsoft . Toolkit . Uwp . UI . Predicates ;
8
9
using Windows . UI . Xaml ;
9
10
using Windows . UI . Xaml . Media ;
@@ -28,7 +29,22 @@ public static class DependencyObjectExtensions
28
29
{
29
30
PredicateByName predicateByName = new ( name , comparisonType ) ;
30
31
31
- return FindDescendant < FrameworkElement , PredicateByName > ( element , ref predicateByName ) ;
32
+ return FindDescendant < FrameworkElement , PredicateByName > ( element , ref predicateByName , SearchType . DepthFirst ) ;
33
+ }
34
+
35
+ /// <summary>
36
+ /// Find the first descendant of type <see cref="FrameworkElement"/> with a given name.
37
+ /// </summary>
38
+ /// <param name="element">The root element.</param>
39
+ /// <param name="name">The name of the element to look for.</param>
40
+ /// <param name="comparisonType">The comparison type to use to match <paramref name="name"/>.</param>
41
+ /// <param name="searchType">The search type to use to explore the visual tree.</param>
42
+ /// <returns>The descendant that was found, or <see langword="null"/>.</returns>
43
+ public static FrameworkElement ? FindDescendant ( this DependencyObject element , string name , StringComparison comparisonType , SearchType searchType )
44
+ {
45
+ PredicateByName predicateByName = new ( name , comparisonType ) ;
46
+
47
+ return FindDescendant < FrameworkElement , PredicateByName > ( element , ref predicateByName , searchType ) ;
32
48
}
33
49
34
50
/// <summary>
@@ -42,7 +58,22 @@ public static class DependencyObjectExtensions
42
58
{
43
59
PredicateByAny < T > predicateByAny = default ;
44
60
45
- return FindDescendant < T , PredicateByAny < T > > ( element , ref predicateByAny ) ;
61
+ return FindDescendant < T , PredicateByAny < T > > ( element , ref predicateByAny , SearchType . DepthFirst ) ;
62
+ }
63
+
64
+ /// <summary>
65
+ /// Find the first descendant element of a given type.
66
+ /// </summary>
67
+ /// <typeparam name="T">The type of elements to match.</typeparam>
68
+ /// <param name="element">The root element.</param>
69
+ /// <param name="searchType">The search type to use to explore the visual tree.</param>
70
+ /// <returns>The descendant that was found, or <see langword="null"/>.</returns>
71
+ public static T ? FindDescendant < T > ( this DependencyObject element , SearchType searchType )
72
+ where T : notnull , DependencyObject
73
+ {
74
+ PredicateByAny < T > predicateByAny = default ;
75
+
76
+ return FindDescendant < T , PredicateByAny < T > > ( element , ref predicateByAny , searchType ) ;
46
77
}
47
78
48
79
/// <summary>
@@ -55,7 +86,21 @@ public static class DependencyObjectExtensions
55
86
{
56
87
PredicateByType predicateByType = new ( type ) ;
57
88
58
- return FindDescendant < DependencyObject , PredicateByType > ( element , ref predicateByType ) ;
89
+ return FindDescendant < DependencyObject , PredicateByType > ( element , ref predicateByType , SearchType . DepthFirst ) ;
90
+ }
91
+
92
+ /// <summary>
93
+ /// Find the first descendant element of a given type.
94
+ /// </summary>
95
+ /// <param name="element">The root element.</param>
96
+ /// <param name="type">The type of element to match.</param>
97
+ /// <param name="searchType">The search type to use to explore the visual tree.</param>
98
+ /// <returns>The descendant that was found, or <see langword="null"/>.</returns>
99
+ public static DependencyObject ? FindDescendant ( this DependencyObject element , Type type , SearchType searchType )
100
+ {
101
+ PredicateByType predicateByType = new ( type ) ;
102
+
103
+ return FindDescendant < DependencyObject , PredicateByType > ( element , ref predicateByType , searchType ) ;
59
104
}
60
105
61
106
/// <summary>
@@ -70,7 +115,23 @@ public static class DependencyObjectExtensions
70
115
{
71
116
PredicateByFunc < T > predicateByFunc = new ( predicate ) ;
72
117
73
- return FindDescendant < T , PredicateByFunc < T > > ( element , ref predicateByFunc ) ;
118
+ return FindDescendant < T , PredicateByFunc < T > > ( element , ref predicateByFunc , SearchType . DepthFirst ) ;
119
+ }
120
+
121
+ /// <summary>
122
+ /// Find the first descendant element matching a given predicate.
123
+ /// </summary>
124
+ /// <typeparam name="T">The type of elements to match.</typeparam>
125
+ /// <param name="element">The root element.</param>
126
+ /// <param name="predicate">The predicatee to use to match the descendant nodes.</param>
127
+ /// <param name="searchType">The search type to use to explore the visual tree.</param>
128
+ /// <returns>The descendant that was found, or <see langword="null"/>.</returns>
129
+ public static T ? FindDescendant < T > ( this DependencyObject element , Func < T , bool > predicate , SearchType searchType )
130
+ where T : notnull , DependencyObject
131
+ {
132
+ PredicateByFunc < T > predicateByFunc = new ( predicate ) ;
133
+
134
+ return FindDescendant < T , PredicateByFunc < T > > ( element , ref predicateByFunc , searchType ) ;
74
135
}
75
136
76
137
/// <summary>
@@ -87,41 +148,122 @@ public static class DependencyObjectExtensions
87
148
{
88
149
PredicateByFunc < T , TState > predicateByFunc = new ( state , predicate ) ;
89
150
90
- return FindDescendant < T , PredicateByFunc < T , TState > > ( element , ref predicateByFunc ) ;
151
+ return FindDescendant < T , PredicateByFunc < T , TState > > ( element , ref predicateByFunc , SearchType . DepthFirst ) ;
91
152
}
92
153
93
154
/// <summary>
94
- /// Find the first descendant element matching a given predicate, using a depth-first search .
155
+ /// Find the first descendant element matching a given predicate.
95
156
/// </summary>
96
157
/// <typeparam name="T">The type of elements to match.</typeparam>
97
- /// <typeparam name="TPredicate ">The type of predicate in use.</typeparam>
158
+ /// <typeparam name="TState ">The type of state to use when matching nodes .</typeparam>
98
159
/// <param name="element">The root element.</param>
160
+ /// <param name="state">The state to give as input to <paramref name="predicate"/>.</param>
99
161
/// <param name="predicate">The predicatee to use to match the descendant nodes.</param>
162
+ /// <param name="searchType">The search type to use to explore the visual tree.</param>
100
163
/// <returns>The descendant that was found, or <see langword="null"/>.</returns>
101
- private static T ? FindDescendant < T , TPredicate > ( this DependencyObject element , ref TPredicate predicate )
164
+ public static T ? FindDescendant < T , TState > ( this DependencyObject element , TState state , Func < T , TState , bool > predicate , SearchType searchType )
102
165
where T : notnull , DependencyObject
103
- where TPredicate : struct , IPredicate < T >
104
166
{
105
- int childrenCount = VisualTreeHelper . GetChildrenCount ( element ) ;
167
+ PredicateByFunc < T , TState > predicateByFunc = new ( state , predicate ) ;
106
168
107
- for ( var i = 0 ; i < childrenCount ; i ++ )
169
+ return FindDescendant < T , PredicateByFunc < T , TState > > ( element , ref predicateByFunc , searchType ) ;
170
+ }
171
+
172
+ /// <summary>
173
+ /// Find the first descendant element matching a given predicate.
174
+ /// </summary>
175
+ /// <typeparam name="T">The type of elements to match.</typeparam>
176
+ /// <typeparam name="TPredicate">The type of predicate in use.</typeparam>
177
+ /// <param name="element">The root element.</param>
178
+ /// <param name="predicate">The predicate to use to match the descendant nodes.</param>
179
+ /// <param name="searchType">The search type to use to explore the visual tree.</param>
180
+ /// <returns>The descendant that was found, or <see langword="null"/>.</returns>
181
+ private static T ? FindDescendant < T , TPredicate > ( this DependencyObject element , ref TPredicate predicate , SearchType searchType )
182
+ where T : notnull , DependencyObject
183
+ where TPredicate : struct , IPredicate < T >
184
+ {
185
+ // Depth-first search, with recursive implementation
186
+ static T ? FindDescendantWithDepthFirstSearch ( DependencyObject element , ref TPredicate predicate )
108
187
{
109
- DependencyObject child = VisualTreeHelper . GetChild ( element , i ) ;
188
+ int childrenCount = VisualTreeHelper . GetChildrenCount ( element ) ;
110
189
111
- if ( child is T result && predicate . Match ( result ) )
190
+ for ( int i = 0 ; i < childrenCount ; i ++ )
112
191
{
113
- return result ;
192
+ DependencyObject child = VisualTreeHelper . GetChild ( element , i ) ;
193
+
194
+ if ( child is T result && predicate . Match ( result ) )
195
+ {
196
+ return result ;
197
+ }
198
+
199
+ T ? descendant = FindDescendantWithDepthFirstSearch ( child , ref predicate ) ;
200
+
201
+ if ( descendant is not null )
202
+ {
203
+ return descendant ;
204
+ }
114
205
}
115
206
116
- T ? descendant = FindDescendant < T , TPredicate > ( child , ref predicate ) ;
207
+ return null ;
208
+ }
209
+
210
+ // Breadth-first search, with iterative implementation and pooled local stack
211
+ static T ? FindDescendantWithBreadthFirstSearch ( DependencyObject element , ref TPredicate predicate )
212
+ {
213
+ // We're using a pooled buffer writer to amortize allocations for the temporary collection of children
214
+ // to visit for each level. The underlying array is deliberately just of type object and not DependencyObject
215
+ // to reduce the number of generic instantiations and allow the rented arrays to be reused more.
216
+ using ArrayPoolBufferWriter < object > bufferWriter = ArrayPoolBufferWriter < object > . Create ( ) ;
117
217
118
- if ( descendant is not null )
218
+ int childrenCount = VisualTreeHelper . GetChildrenCount ( element ) ;
219
+
220
+ // Add the top level children
221
+ for ( int i = 0 ; i < childrenCount ; i ++ )
119
222
{
120
- return descendant ;
223
+ DependencyObject child = VisualTreeHelper . GetChild ( element , i ) ;
224
+
225
+ if ( child is T result && predicate . Match ( result ) )
226
+ {
227
+ return result ;
228
+ }
229
+
230
+ bufferWriter . Add ( child ) ;
121
231
}
232
+
233
+ // Explore each depth level
234
+ for ( int i = 0 ; i < bufferWriter . Count ; i ++ )
235
+ {
236
+ DependencyObject parent = ( DependencyObject ) bufferWriter [ i ] ;
237
+
238
+ childrenCount = VisualTreeHelper . GetChildrenCount ( parent ) ;
239
+
240
+ for ( int j = 0 ; j < childrenCount ; j ++ )
241
+ {
242
+ DependencyObject child = VisualTreeHelper . GetChild ( parent , j ) ;
243
+
244
+ if ( child is T result && predicate . Match ( result ) )
245
+ {
246
+ return result ;
247
+ }
248
+
249
+ bufferWriter . Add ( child ) ;
250
+ }
251
+ }
252
+
253
+ return null ;
122
254
}
123
255
124
- return null ;
256
+ static T ? ThrowArgumentOutOfRangeExceptionForInvalidSearchType ( )
257
+ {
258
+ throw new ArgumentOutOfRangeException ( nameof ( searchType ) , "The input search type is not valid" ) ;
259
+ }
260
+
261
+ return searchType switch
262
+ {
263
+ SearchType . DepthFirst => FindDescendantWithDepthFirstSearch ( element , ref predicate ) ,
264
+ SearchType . BreadthFirst => FindDescendantWithBreadthFirstSearch ( element , ref predicate ) ,
265
+ _ => ThrowArgumentOutOfRangeExceptionForInvalidSearchType ( )
266
+ } ;
125
267
}
126
268
127
269
/// <summary>
0 commit comments