44
55using System ;
66using System . Collections . Generic ;
7+ using Microsoft . Toolkit . Uwp . UI . Helpers . Internals ;
78using Microsoft . Toolkit . Uwp . UI . Predicates ;
89using Windows . UI . Xaml ;
910using Windows . UI . Xaml . Media ;
@@ -28,7 +29,22 @@ public static class DependencyObjectExtensions
2829 {
2930 PredicateByName predicateByName = new ( name , comparisonType ) ;
3031
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 ) ;
3248 }
3349
3450 /// <summary>
@@ -42,7 +58,22 @@ public static class DependencyObjectExtensions
4258 {
4359 PredicateByAny < T > predicateByAny = default ;
4460
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 ) ;
4677 }
4778
4879 /// <summary>
@@ -55,7 +86,21 @@ public static class DependencyObjectExtensions
5586 {
5687 PredicateByType predicateByType = new ( type ) ;
5788
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 ) ;
59104 }
60105
61106 /// <summary>
@@ -70,7 +115,23 @@ public static class DependencyObjectExtensions
70115 {
71116 PredicateByFunc < T > predicateByFunc = new ( predicate ) ;
72117
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 ) ;
74135 }
75136
76137 /// <summary>
@@ -87,41 +148,122 @@ public static class DependencyObjectExtensions
87148 {
88149 PredicateByFunc < T , TState > predicateByFunc = new ( state , predicate ) ;
89150
90- return FindDescendant < T , PredicateByFunc < T , TState > > ( element , ref predicateByFunc ) ;
151+ return FindDescendant < T , PredicateByFunc < T , TState > > ( element , ref predicateByFunc , SearchType . DepthFirst ) ;
91152 }
92153
93154 /// <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.
95156 /// </summary>
96157 /// <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>
98159 /// <param name="element">The root element.</param>
160+ /// <param name="state">The state to give as input to <paramref name="predicate"/>.</param>
99161 /// <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>
100163 /// <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 )
102165 where T : notnull , DependencyObject
103- where TPredicate : struct , IPredicate < T >
104166 {
105- int childrenCount = VisualTreeHelper . GetChildrenCount ( element ) ;
167+ PredicateByFunc < T , TState > predicateByFunc = new ( state , predicate ) ;
106168
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 )
108187 {
109- DependencyObject child = VisualTreeHelper . GetChild ( element , i ) ;
188+ int childrenCount = VisualTreeHelper . GetChildrenCount ( element ) ;
110189
111- if ( child is T result && predicate . Match ( result ) )
190+ for ( int i = 0 ; i < childrenCount ; i ++ )
112191 {
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+ }
114205 }
115206
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 ( ) ;
117217
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 ++ )
119222 {
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 ) ;
121231 }
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 ;
122254 }
123255
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+ } ;
125267 }
126268
127269 /// <summary>
0 commit comments