Skip to content

Commit 2f7e339

Browse files
committed
Performance improvements, removed code duplication
1 parent f243bb0 commit 2f7e339

File tree

7 files changed

+135
-226
lines changed

7 files changed

+135
-226
lines changed

Microsoft.Toolkit.Uwp.UI/Extensions/Tree/LogicalTree.cs

Lines changed: 69 additions & 140 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using System;
66
using System.Collections.Generic;
77
using System.Reflection;
8+
using Microsoft.Toolkit.Uwp.UI.Extensions.Tree;
89
using Windows.UI.Xaml;
910
using Windows.UI.Xaml.Controls;
1011
using Windows.UI.Xaml.Markup;
@@ -27,10 +28,9 @@ public static class LogicalTree
2728
/// <returns>The child that was found, or <see langword="null"/>.</returns>
2829
public static FrameworkElement? FindChild(this FrameworkElement element, string name, StringComparison comparisonType = StringComparison.Ordinal)
2930
{
30-
return FindChild<FrameworkElement, (string Name, StringComparison ComparisonType)>(
31-
element,
32-
(name, comparisonType),
33-
static (e, s) => s.Name.Equals(e.Name, s.ComparisonType));
31+
PredicateByName predicateByName = new(name, comparisonType);
32+
33+
return FindChild<FrameworkElement, PredicateByName>(element, ref predicateByName);
3434
}
3535

3636
/// <summary>
@@ -42,7 +42,9 @@ public static class LogicalTree
4242
public static T? FindChild<T>(this FrameworkElement element)
4343
where T : notnull, FrameworkElement
4444
{
45-
return FindChild<T>(element, static _ => true);
45+
PredicateByAny<T> predicateByAny = default;
46+
47+
return FindChild<T, PredicateByAny<T>>(element, ref predicateByAny);
4648
}
4749

4850
/// <summary>
@@ -53,7 +55,9 @@ public static class LogicalTree
5355
/// <returns>The child that was found, or <see langword="null"/>.</returns>
5456
public static FrameworkElement? FindChild(this FrameworkElement element, Type type)
5557
{
56-
return FindChild<FrameworkElement, Type>(element, type, static (e, t) => e.GetType() == t);
58+
PredicateByType predicateByType = new(type);
59+
60+
return FindChild<FrameworkElement, PredicateByType>(element, ref predicateByType);
5761
}
5862

5963
/// <summary>
@@ -66,97 +70,9 @@ public static class LogicalTree
6670
public static T? FindChild<T>(this FrameworkElement element, Func<T, bool> predicate)
6771
where T : notnull, FrameworkElement
6872
{
69-
if (element is Panel panel)
70-
{
71-
foreach (UIElement child in panel.Children)
72-
{
73-
if (child is not FrameworkElement current)
74-
{
75-
continue;
76-
}
77-
78-
if (child is T result && predicate(result))
79-
{
80-
return result;
81-
}
82-
83-
T? descendant = FindChild(current, predicate);
84-
85-
if (descendant is not null)
86-
{
87-
return descendant;
88-
}
89-
}
90-
}
91-
else if (element is ItemsControl itemsControl)
92-
{
93-
foreach (object item in itemsControl.Items)
94-
{
95-
if (item is not FrameworkElement current)
96-
{
97-
continue;
98-
}
99-
100-
if (item is T result && predicate(result))
101-
{
102-
return result;
103-
}
104-
105-
T? descendant = FindChild(current, predicate);
73+
PredicateByFunc<T> predicateByFunc = new(predicate);
10674

107-
if (descendant is not null)
108-
{
109-
return descendant;
110-
}
111-
}
112-
}
113-
else if (element is UserControl userControl)
114-
{
115-
if (userControl.Content is T result && predicate(result))
116-
{
117-
return result;
118-
}
119-
120-
if (userControl.Content is FrameworkElement content)
121-
{
122-
return FindChild(content, predicate);
123-
}
124-
}
125-
else if (element is ContentControl contentControl)
126-
{
127-
if (contentControl.Content is T result && predicate(result))
128-
{
129-
return result;
130-
}
131-
132-
if (contentControl.Content is FrameworkElement content)
133-
{
134-
return FindChild(content, predicate);
135-
}
136-
}
137-
else if (element is Border border)
138-
{
139-
if (border.Child is T result && predicate(result))
140-
{
141-
return result;
142-
}
143-
144-
if (border.Child is FrameworkElement child)
145-
{
146-
return FindChild(child, predicate);
147-
}
148-
}
149-
else if (element.GetContentControl() is FrameworkElement containedControl)
150-
{
151-
if (containedControl is T result && predicate(result))
152-
{
153-
return result;
154-
}
155-
156-
return FindChild(containedControl, predicate);
157-
}
158-
159-
return null;
75+
return FindChild<T, PredicateByFunc<T>>(element, ref predicateByFunc);
16076
}
16177

16278
/// <summary>
@@ -170,6 +86,23 @@ public static class LogicalTree
17086
/// <returns>The child that was found, or <see langword="null"/>.</returns>
17187
public static T? FindChild<T, TState>(this FrameworkElement element, TState state, Func<T, TState, bool> predicate)
17288
where T : notnull, FrameworkElement
89+
{
90+
PredicateByFunc<T, TState> predicateByFunc = new(state, predicate);
91+
92+
return FindChild<T, PredicateByFunc<T, TState>>(element, ref predicateByFunc);
93+
}
94+
95+
/// <summary>
96+
/// Find the first child element matching a given predicate, using a depth-first search.
97+
/// </summary>
98+
/// <typeparam name="T">The type of elements to match.</typeparam>
99+
/// <typeparam name="TPredicate">The type of predicate in use.</typeparam>
100+
/// <param name="element">The root element.</param>
101+
/// <param name="predicate">The predicatee to use to match the child nodes.</param>
102+
/// <returns>The child that was found, or <see langword="null"/>.</returns>
103+
private static T? FindChild<T, TPredicate>(this FrameworkElement element, ref TPredicate predicate)
104+
where T : notnull, FrameworkElement
105+
where TPredicate : struct, IPredicate<T>
173106
{
174107
if (element is Panel panel)
175108
{
@@ -180,12 +113,12 @@ public static class LogicalTree
180113
continue;
181114
}
182115

183-
if (child is T result && predicate(result, state))
116+
if (child is T result && predicate.Match(result))
184117
{
185118
return result;
186119
}
187120

188-
T? descendant = FindChild(current, state, predicate);
121+
T? descendant = FindChild<T, TPredicate>(current, ref predicate);
189122

190123
if (descendant is not null)
191124
{
@@ -202,12 +135,12 @@ public static class LogicalTree
202135
continue;
203136
}
204137

205-
if (item is T result && predicate(result, state))
138+
if (item is T result && predicate.Match(result))
206139
{
207140
return result;
208141
}
209142

210-
T? descendant = FindChild(current, state, predicate);
143+
T? descendant = FindChild<T, TPredicate>(current, ref predicate);
211144

212145
if (descendant is not null)
213146
{
@@ -217,48 +150,48 @@ public static class LogicalTree
217150
}
218151
else if (element is UserControl userControl)
219152
{
220-
if (userControl.Content is T result && predicate(result, state))
153+
if (userControl.Content is T result && predicate.Match(result))
221154
{
222155
return result;
223156
}
224157

225158
if (userControl.Content is FrameworkElement content)
226159
{
227-
return FindChild(content, state, predicate);
160+
return FindChild<T, TPredicate>(content, ref predicate);
228161
}
229162
}
230163
else if (element is ContentControl contentControl)
231164
{
232-
if (contentControl.Content is T result && predicate(result, state))
165+
if (contentControl.Content is T result && predicate.Match(result))
233166
{
234167
return result;
235168
}
236169

237170
if (contentControl.Content is FrameworkElement content)
238171
{
239-
return FindChild(content, state, predicate);
172+
return FindChild<T, TPredicate>(content, ref predicate);
240173
}
241174
}
242175
else if (element is Border border)
243176
{
244-
if (border.Child is T result && predicate(result, state))
177+
if (border.Child is T result && predicate.Match(result))
245178
{
246179
return result;
247180
}
248181

249182
if (border.Child is FrameworkElement child)
250183
{
251-
return FindChild(child, state, predicate);
184+
return FindChild<T, TPredicate>(child, ref predicate);
252185
}
253186
}
254187
else if (element.GetContentControl() is FrameworkElement containedControl)
255188
{
256-
if (containedControl is T result && predicate(result, state))
189+
if (containedControl is T result && predicate.Match(result))
257190
{
258191
return result;
259192
}
260193

261-
return FindChild(containedControl, state, predicate);
194+
return FindChild<T, TPredicate>(containedControl, ref predicate);
262195
}
263196

264197
return null;
@@ -456,10 +389,9 @@ public static IEnumerable<FrameworkElement> FindChildren(this FrameworkElement e
456389
/// <returns>The parent that was found, or <see langword="null"/>.</returns>
457390
public static FrameworkElement? FindParent(this FrameworkElement element, string name, StringComparison comparisonType = StringComparison.Ordinal)
458391
{
459-
return FindParent<FrameworkElement, (string Name, StringComparison ComparisonType)>(
460-
element,
461-
(name, comparisonType),
462-
static (e, s) => s.Name.Equals(e.Name, s.ComparisonType));
392+
PredicateByName predicateByName = new(name, comparisonType);
393+
394+
return FindParent<FrameworkElement, PredicateByName>(element, ref predicateByName);
463395
}
464396

465397
/// <summary>
@@ -471,20 +403,9 @@ public static IEnumerable<FrameworkElement> FindChildren(this FrameworkElement e
471403
public static T? FindParent<T>(this FrameworkElement element)
472404
where T : notnull, FrameworkElement
473405
{
474-
while (true)
475-
{
476-
if (element.Parent is not FrameworkElement parent)
477-
{
478-
return null;
479-
}
480-
481-
if (parent is T result)
482-
{
483-
return result;
484-
}
406+
PredicateByAny<T> predicateByAny = default;
485407

486-
element = parent;
487-
}
408+
return FindParent<T, PredicateByAny<T>>(element, ref predicateByAny);
488409
}
489410

490411
/// <summary>
@@ -495,7 +416,9 @@ public static IEnumerable<FrameworkElement> FindChildren(this FrameworkElement e
495416
/// <returns>The parent that was found, or <see langword="null"/>.</returns>
496417
public static FrameworkElement? FindParent(this FrameworkElement element, Type type)
497418
{
498-
return FindParent<FrameworkElement, Type>(element, type, static (e, t) => e.GetType() == t);
419+
PredicateByType predicateByType = new(type);
420+
421+
return FindParent<FrameworkElement, PredicateByType>(element, ref predicateByType);
499422
}
500423

501424
/// <summary>
@@ -508,20 +431,9 @@ public static IEnumerable<FrameworkElement> FindChildren(this FrameworkElement e
508431
public static T? FindParent<T>(this FrameworkElement element, Func<T, bool> predicate)
509432
where T : notnull, FrameworkElement
510433
{
511-
while (true)
512-
{
513-
if (element.Parent is not FrameworkElement parent)
514-
{
515-
return null;
516-
}
517-
518-
if (parent is T result && predicate(result))
519-
{
520-
return result;
521-
}
434+
PredicateByFunc<T> predicateByFunc = new(predicate);
522435

523-
element = parent;
524-
}
436+
return FindParent<T, PredicateByFunc<T>>(element, ref predicateByFunc);
525437
}
526438

527439
/// <summary>
@@ -535,6 +447,23 @@ public static IEnumerable<FrameworkElement> FindChildren(this FrameworkElement e
535447
/// <returns>The parent that was found, or <see langword="null"/>.</returns>
536448
public static T? FindParent<T, TState>(this FrameworkElement element, TState state, Func<T, TState, bool> predicate)
537449
where T : notnull, FrameworkElement
450+
{
451+
PredicateByFunc<T, TState> predicateByFunc = new(state, predicate);
452+
453+
return FindParent<T, PredicateByFunc<T, TState>>(element, ref predicateByFunc);
454+
}
455+
456+
/// <summary>
457+
/// Find the first parent element matching a given predicate.
458+
/// </summary>
459+
/// <typeparam name="T">The type of elements to match.</typeparam>
460+
/// <typeparam name="TPredicate">The type of predicate in use.</typeparam>
461+
/// <param name="element">The starting element.</param>
462+
/// <param name="predicate">The predicatee to use to match the parent nodes.</param>
463+
/// <returns>The parent that was found, or <see langword="null"/>.</returns>
464+
private static T? FindParent<T, TPredicate>(this FrameworkElement element, ref TPredicate predicate)
465+
where T : notnull, FrameworkElement
466+
where TPredicate : struct, IPredicate<T>
538467
{
539468
while (true)
540469
{
@@ -543,7 +472,7 @@ public static IEnumerable<FrameworkElement> FindChildren(this FrameworkElement e
543472
return null;
544473
}
545474

546-
if (parent is T result && predicate(result, state))
475+
if (parent is T result && predicate.Match(result))
547476
{
548477
return result;
549478
}

Microsoft.Toolkit.Uwp.UI/Extensions/Tree/Predicates/Interfaces/IPredicate{T}.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@ namespace Microsoft.Toolkit.Uwp.UI.Extensions.Tree
88
/// An interface representing a predicate for items of a given type.
99
/// </summary>
1010
/// <typeparam name="T">The type of items to match.</typeparam>
11-
internal interface IPredicate<T>
11+
internal interface IPredicate<in T>
12+
where T : class
1213
{
1314
/// <summary>
1415
/// Performs a match with the current predicate over a target <typeparamref name="T"/> instance.

Microsoft.Toolkit.Uwp.UI/Extensions/Tree/Predicates/PredicateByAny{T}.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Extensions.Tree
1111
/// </summary>
1212
/// <typeparam name="T">The type of items to match.</typeparam>
1313
internal readonly struct PredicateByAny<T> : IPredicate<T>
14+
where T : class
1415
{
1516
/// <inheritdoc/>
1617
[MethodImpl(MethodImplOptions.AggressiveInlining)]

Microsoft.Toolkit.Uwp.UI/Extensions/Tree/Predicates/PredicateByFunc{T,TState}.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Extensions.Tree
1313
/// <typeparam name="T">The type of items to match.</typeparam>
1414
/// <typeparam name="TState">The type of state to use when matching items.</typeparam>
1515
internal readonly struct PredicateByFunc<T, TState> : IPredicate<T>
16+
where T : class
1617
{
1718
/// <summary>
1819
/// The state to give as input to <see name="predicate"/>.

Microsoft.Toolkit.Uwp.UI/Extensions/Tree/Predicates/PredicateByFunc{T}.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Extensions.Tree
1212
/// </summary>
1313
/// <typeparam name="T">The type of items to match.</typeparam>
1414
internal readonly struct PredicateByFunc<T> : IPredicate<T>
15+
where T : class
1516
{
1617
/// <summary>
1718
/// The predicatee to use to match items.

0 commit comments

Comments
 (0)