Skip to content

Commit 488c705

Browse files
committed
Added new FindDescendants[OrSelf] overloads
1 parent 4a74a5f commit 488c705

File tree

1 file changed

+175
-8
lines changed

1 file changed

+175
-8
lines changed

Microsoft.Toolkit.Uwp.UI/Extensions/DependencyObjectExtensions.cs

Lines changed: 175 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -453,27 +453,194 @@ public static class DependencyObjectExtensions
453453
/// <para>
454454
/// This method is meant to provide extra flexibility in specific scenarios and it should not
455455
/// be used when only the first item is being looked for. In those cases, use one of the
456-
/// available <see cref="FindDescendant{T}(DependencyObject)"/> overloads instead, which will
457-
/// offer a more compact syntax as well as better performance in those cases.
456+
/// available <see cref="FindDescendant{T}(DependencyObject)"/> overloads instead,
457+
/// which will offer a more compact syntax as well as better performance in those cases.
458458
/// </para>
459459
/// </summary>
460460
/// <param name="element">The root element.</param>
461461
/// <returns>All the descendant <see cref="DependencyObject"/> instance from <paramref name="element"/>.</returns>
462462
public static IEnumerable<DependencyObject> FindDescendants(this DependencyObject element)
463463
{
464-
int childrenCount = VisualTreeHelper.GetChildrenCount(element);
464+
return FindDescendants(element, SearchType.DepthFirst);
465+
}
465466

466-
for (var i = 0; i < childrenCount; i++)
467+
/// <summary>
468+
/// Find all descendant elements of the specified element. This method can be chained with
469+
/// LINQ calls to add additional filters or projections on top of the returned results.
470+
/// <para>
471+
/// This method is meant to provide extra flexibility in specific scenarios and it should not
472+
/// be used when only the first item is being looked for. In those cases, use one of the
473+
/// available <see cref="FindDescendant{T}(DependencyObject)"/> overloads instead,
474+
/// which will offer a more compact syntax as well as better performance in those cases.
475+
/// </para>
476+
/// </summary>
477+
/// <param name="element">The root element.</param>
478+
/// <param name="searchType">The search type to use to explore the visual tree.</param>
479+
/// <returns>All the descendant <see cref="DependencyObject"/> instance from <paramref name="element"/>.</returns>
480+
public static IEnumerable<DependencyObject> FindDescendants(this DependencyObject element, SearchType searchType)
481+
{
482+
// Depth-first traversal, with recursion
483+
static IEnumerable<DependencyObject> FindDescendantsWithDepthFirstSearch(DependencyObject element)
467484
{
468-
DependencyObject child = VisualTreeHelper.GetChild(element, i);
485+
int childrenCount = VisualTreeHelper.GetChildrenCount(element);
486+
487+
for (var i = 0; i < childrenCount; i++)
488+
{
489+
DependencyObject child = VisualTreeHelper.GetChild(element, i);
490+
491+
yield return child;
492+
493+
foreach (DependencyObject childOfChild in FindDescendants(child))
494+
{
495+
yield return childOfChild;
496+
}
497+
}
498+
}
499+
500+
// Breadth-first traversal, with pooled local stack
501+
static IEnumerable<DependencyObject> FindDescendantsWithBreadthFirstSearch(DependencyObject element)
502+
{
503+
using ArrayPoolBufferWriter<object> bufferWriter = ArrayPoolBufferWriter<object>.Create();
504+
505+
int childrenCount = VisualTreeHelper.GetChildrenCount(element);
506+
507+
for (int i = 0; i < childrenCount; i++)
508+
{
509+
DependencyObject child = VisualTreeHelper.GetChild(element, i);
469510

470-
yield return child;
511+
yield return child;
471512

472-
foreach (DependencyObject childOfChild in FindDescendants(child))
513+
bufferWriter.Add(child);
514+
}
515+
516+
for (int i = 0; i < bufferWriter.Count; i++)
473517
{
474-
yield return childOfChild;
518+
DependencyObject parent = (DependencyObject)bufferWriter[i];
519+
520+
childrenCount = VisualTreeHelper.GetChildrenCount(parent);
521+
522+
for (int j = 0; j < childrenCount; j++)
523+
{
524+
DependencyObject child = VisualTreeHelper.GetChild(parent, j);
525+
526+
yield return child;
527+
528+
bufferWriter.Add(child);
529+
}
475530
}
476531
}
532+
533+
static IEnumerable<DependencyObject> ThrowArgumentOutOfRangeExceptionForInvalidSearchType()
534+
{
535+
throw new ArgumentOutOfRangeException(nameof(searchType), "The input search type is not valid");
536+
}
537+
538+
return searchType switch
539+
{
540+
SearchType.DepthFirst => FindDescendantsWithDepthFirstSearch(element),
541+
SearchType.BreadthFirst => FindDescendantsWithBreadthFirstSearch(element),
542+
_ => ThrowArgumentOutOfRangeExceptionForInvalidSearchType()
543+
};
544+
}
545+
546+
/// <summary>
547+
/// Find all descendant elements of the specified element (or self). This method can be chained
548+
/// with LINQ calls to add additional filters or projections on top of the returned results.
549+
/// <para>
550+
/// This method is meant to provide extra flexibility in specific scenarios and it should not
551+
/// be used when only the first item is being looked for. In those cases, use one of the
552+
/// available <see cref="FindDescendantOrSelf{T}(DependencyObject)"/> overloads instead,
553+
/// which will offer a more compact syntax as well as better performance in those cases.
554+
/// </para>
555+
/// </summary>
556+
/// <param name="element">The root element.</param>
557+
/// <returns>All the descendant <see cref="DependencyObject"/> instance from <paramref name="element"/>.</returns>
558+
public static IEnumerable<DependencyObject> FindDescendantsOrSelf(this DependencyObject element)
559+
{
560+
return FindDescendantsOrSelf(element, SearchType.DepthFirst);
561+
}
562+
563+
/// <summary>
564+
/// Find all descendant elements of the specified element (or self). This method can be chained
565+
/// with LINQ calls to add additional filters or projections on top of the returned results.
566+
/// <para>
567+
/// This method is meant to provide extra flexibility in specific scenarios and it should not
568+
/// be used when only the first item is being looked for. In those cases, use one of the
569+
/// available <see cref="FindDescendantOrSelf{T}(DependencyObject)"/> overloads instead,
570+
/// which will offer a more compact syntax as well as better performance in those cases.
571+
/// </para>
572+
/// </summary>
573+
/// <param name="element">The root element.</param>
574+
/// <param name="searchType">The search type to use to explore the visual tree.</param>
575+
/// <returns>All the descendant <see cref="DependencyObject"/> instance from <paramref name="element"/>.</returns>
576+
public static IEnumerable<DependencyObject> FindDescendantsOrSelf(this DependencyObject element, SearchType searchType)
577+
{
578+
// Depth-first traversal, with recursion
579+
static IEnumerable<DependencyObject> FindDescendantsWithDepthFirstSearch(DependencyObject element)
580+
{
581+
yield return element;
582+
583+
int childrenCount = VisualTreeHelper.GetChildrenCount(element);
584+
585+
for (var i = 0; i < childrenCount; i++)
586+
{
587+
DependencyObject child = VisualTreeHelper.GetChild(element, i);
588+
589+
yield return child;
590+
591+
foreach (DependencyObject childOfChild in FindDescendants(child))
592+
{
593+
yield return childOfChild;
594+
}
595+
}
596+
}
597+
598+
// Breadth-first traversal, with pooled local stack
599+
static IEnumerable<DependencyObject> FindDescendantsWithBreadthFirstSearch(DependencyObject element)
600+
{
601+
yield return element;
602+
603+
using ArrayPoolBufferWriter<object> bufferWriter = ArrayPoolBufferWriter<object>.Create();
604+
605+
int childrenCount = VisualTreeHelper.GetChildrenCount(element);
606+
607+
for (int i = 0; i < childrenCount; i++)
608+
{
609+
DependencyObject child = VisualTreeHelper.GetChild(element, i);
610+
611+
yield return child;
612+
613+
bufferWriter.Add(child);
614+
}
615+
616+
for (int i = 0; i < bufferWriter.Count; i++)
617+
{
618+
DependencyObject parent = (DependencyObject)bufferWriter[i];
619+
620+
childrenCount = VisualTreeHelper.GetChildrenCount(parent);
621+
622+
for (int j = 0; j < childrenCount; j++)
623+
{
624+
DependencyObject child = VisualTreeHelper.GetChild(parent, j);
625+
626+
yield return child;
627+
628+
bufferWriter.Add(child);
629+
}
630+
}
631+
}
632+
633+
static IEnumerable<DependencyObject> ThrowArgumentOutOfRangeExceptionForInvalidSearchType()
634+
{
635+
throw new ArgumentOutOfRangeException(nameof(searchType), "The input search type is not valid");
636+
}
637+
638+
return searchType switch
639+
{
640+
SearchType.DepthFirst => FindDescendantsWithDepthFirstSearch(element),
641+
SearchType.BreadthFirst => FindDescendantsWithBreadthFirstSearch(element),
642+
_ => ThrowArgumentOutOfRangeExceptionForInvalidSearchType()
643+
};
477644
}
478645

479646
/// <summary>

0 commit comments

Comments
 (0)