Skip to content

Commit b1dfbdd

Browse files
committed
Refactored enumerable parent/child visual tree extensions
1 parent 81a5ca4 commit b1dfbdd

File tree

2 files changed

+96
-66
lines changed

2 files changed

+96
-66
lines changed

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

Lines changed: 59 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,6 @@ public static class LogicalTree
135135
public static T? FindChild<T, TState>(this FrameworkElement element, TState state, Func<T, TState, bool> predicate)
136136
where T : notnull, FrameworkElement
137137
{
138-
139138
if (element is Panel panel)
140139
{
141140
foreach (UIElement child in panel.Children)
@@ -282,71 +281,60 @@ public static class LogicalTree
282281
}
283282

284283
/// <summary>
285-
/// Find all logical child controls of the specified type.
284+
/// Find all logical child elements of the specified element. This method can be chained with
285+
/// LINQ calls to add additional filters or projections on top of the returned results.
286+
/// <para>
287+
/// This method is meant to provide extra flexibility in specific scenarios and it should not
288+
/// be used when only the first item is being looked for. In those cases, use one of the
289+
/// available <see cref="FindChild{T}(FrameworkElement)"/> overloads instead, which will
290+
/// offer a more compact syntax as well as better performance in those cases.
291+
/// </para>
286292
/// </summary>
287-
/// <typeparam name="T">Type to search for.</typeparam>
288-
/// <param name="element">Parent element.</param>
289-
/// <returns>Child controls or empty if not found.</returns>
290-
public static IEnumerable<T> FindChildren<T>(this FrameworkElement element)
291-
where T : FrameworkElement
293+
/// <param name="element">The root element.</param>
294+
/// <returns>All the child <see cref="FrameworkElement"/> instance from <paramref name="element"/>.</returns>
295+
public static IEnumerable<FrameworkElement> FindChildren(this FrameworkElement element)
292296
{
293-
if (element == null)
294-
{
295-
yield break;
296-
}
297-
298-
if (element is Panel)
297+
if (element is Panel panel)
299298
{
300-
foreach (var child in (element as Panel).Children)
299+
foreach (UIElement child in panel.Children)
301300
{
302-
if (child is T)
301+
if (child is not FrameworkElement current)
303302
{
304-
yield return child as T;
303+
continue;
305304
}
306305

307-
var childFrameworkElement = child as FrameworkElement;
306+
yield return current;
308307

309-
if (childFrameworkElement != null)
308+
foreach (FrameworkElement childOfChild in FindChildren(current))
310309
{
311-
foreach (T childOfChild in childFrameworkElement.FindChildren<T>())
312-
{
313-
yield return childOfChild;
314-
}
310+
yield return childOfChild;
315311
}
316312
}
317313
}
318-
else if (element is ItemsControl)
314+
else if (element is ItemsControl itemsControl)
319315
{
320-
foreach (var item in (element as ItemsControl).Items)
316+
foreach (object item in itemsControl.Items)
321317
{
322-
var childFrameworkElement = item as FrameworkElement;
318+
if (item is not FrameworkElement current)
319+
{
320+
continue;
321+
}
323322

324-
if (childFrameworkElement != null)
323+
yield return current;
324+
325+
foreach (FrameworkElement childOfChild in FindChildren(current))
325326
{
326-
foreach (T childOfChild in childFrameworkElement.FindChildren<T>())
327-
{
328-
yield return childOfChild;
329-
}
327+
yield return childOfChild;
330328
}
331329
}
332330
}
333-
else
331+
else if (element.TryGetContentControl() is FrameworkElement contentControl)
334332
{
335-
var content = element.TryGetContentControl();
333+
yield return contentControl;
336334

337-
if (content is T)
335+
foreach (FrameworkElement childOfChild in FindChildren(contentControl))
338336
{
339-
yield return content as T;
340-
}
341-
342-
var childFrameworkElement = content as FrameworkElement;
343-
344-
if (childFrameworkElement != null)
345-
{
346-
foreach (T childOfChild in childFrameworkElement.FindChildren<T>())
347-
{
348-
yield return childOfChild;
349-
}
337+
yield return childOfChild;
350338
}
351339
}
352340
}
@@ -544,6 +532,33 @@ public static IEnumerable<T> FindChildren<T>(this FrameworkElement element)
544532
return FindParent(element, state, predicate);
545533
}
546534

535+
/// <summary>
536+
/// Find all parent elements of the specified element. This method can be chained with
537+
/// LINQ calls to add additional filters or projections on top of the returned results.
538+
/// <para>
539+
/// This method is meant to provide extra flexibility in specific scenarios and it should not
540+
/// be used when only the first item is being looked for. In those cases, use one of the
541+
/// available <see cref="FindParent{T}(FrameworkElement)"/> overloads instead, which will
542+
/// offer a more compact syntax as well as better performance in those cases.
543+
/// </para>
544+
/// </summary>
545+
/// <param name="element">The root element.</param>
546+
/// <returns>All the parent <see cref="FrameworkElement"/> instance from <paramref name="element"/>.</returns>
547+
public static IEnumerable<FrameworkElement> FindParents(this FrameworkElement element)
548+
{
549+
while (true)
550+
{
551+
if (element.Parent is not FrameworkElement parent)
552+
{
553+
yield break;
554+
}
555+
556+
yield return parent;
557+
558+
element = parent;
559+
}
560+
}
561+
547562
/// <summary>
548563
/// Tries to retrieve the content property of this element as defined by <see cref="ContentPropertyAttribute"/>.
549564
/// </summary>

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

Lines changed: 37 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -228,28 +228,30 @@ public static class VisualTree
228228
}
229229

230230
/// <summary>
231-
/// Find all descendant controls of the specified type.
231+
/// Find all descendant elements of the specified element. This method can be chained with
232+
/// LINQ calls to add additional filters or projections on top of the returned results.
233+
/// <para>
234+
/// This method is meant to provide extra flexibility in specific scenarios and it should not
235+
/// be used when only the first item is being looked for. In those cases, use one of the
236+
/// available <see cref="FindDescendant{T}(DependencyObject)"/> overloads instead, which will
237+
/// offer a more compact syntax as well as better performance in those cases.
238+
/// </para>
232239
/// </summary>
233-
/// <typeparam name="T">Type to search for.</typeparam>
234-
/// <param name="element">Parent element.</param>
235-
/// <returns>Descendant controls or empty if not found.</returns>
236-
public static IEnumerable<T> FindDescendants<T>(this DependencyObject element)
237-
where T : DependencyObject
240+
/// <param name="element">The root element.</param>
241+
/// <returns>All the descendant <see cref="DependencyObject"/> instance from <paramref name="element"/>.</returns>
242+
public static IEnumerable<DependencyObject> FindDescendants(this DependencyObject element)
238243
{
239-
var childrenCount = VisualTreeHelper.GetChildrenCount(element);
244+
int childrenCount = VisualTreeHelper.GetChildrenCount(element);
240245

241246
for (var i = 0; i < childrenCount; i++)
242247
{
243-
var child = VisualTreeHelper.GetChild(element, i);
244-
var type = child as T;
245-
if (type != null)
246-
{
247-
yield return type;
248-
}
248+
DependencyObject child = VisualTreeHelper.GetChild(element, i);
249249

250-
foreach (T childofChild in child.FindDescendants<T>())
250+
yield return child;
251+
252+
foreach (DependencyObject childOfChild in FindDescendants(child))
251253
{
252-
yield return childofChild;
254+
yield return childOfChild;
253255
}
254256
}
255257
}
@@ -454,18 +456,31 @@ public static IEnumerable<T> FindDescendants<T>(this DependencyObject element)
454456
}
455457

456458
/// <summary>
457-
/// Find all visual ascendants for the element.
459+
/// Find all ascendant elements of the specified element. This method can be chained with
460+
/// LINQ calls to add additional filters or projections on top of the returned results.
461+
/// <para>
462+
/// This method is meant to provide extra flexibility in specific scenarios and it should not
463+
/// be used when only the first item is being looked for. In those cases, use one of the
464+
/// available <see cref="FindAscendant{T}(DependencyObject)"/> overloads instead, which will
465+
/// offer a more compact syntax as well as better performance in those cases.
466+
/// </para>
458467
/// </summary>
459-
/// <param name="element">Child element.</param>
460-
/// <returns>A collection of parent elements or null if none found.</returns>
468+
/// <param name="element">The root element.</param>
469+
/// <returns>All the descendant <see cref="DependencyObject"/> instance from <paramref name="element"/>.</returns>
461470
public static IEnumerable<DependencyObject> FindAscendants(this DependencyObject element)
462471
{
463-
var parent = VisualTreeHelper.GetParent(element);
464-
465-
while (parent != null)
472+
while (true)
466473
{
474+
DependencyObject? parent = VisualTreeHelper.GetParent(element);
475+
476+
if (parent is null)
477+
{
478+
yield break;
479+
}
480+
467481
yield return parent;
468-
parent = VisualTreeHelper.GetParent(parent);
482+
483+
element = parent;
469484
}
470485
}
471486
}

0 commit comments

Comments
 (0)