@@ -453,27 +453,194 @@ public static class DependencyObjectExtensions
453
453
/// <para>
454
454
/// This method is meant to provide extra flexibility in specific scenarios and it should not
455
455
/// 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.
458
458
/// </para>
459
459
/// </summary>
460
460
/// <param name="element">The root element.</param>
461
461
/// <returns>All the descendant <see cref="DependencyObject"/> instance from <paramref name="element"/>.</returns>
462
462
public static IEnumerable < DependencyObject > FindDescendants ( this DependencyObject element )
463
463
{
464
- int childrenCount = VisualTreeHelper . GetChildrenCount ( element ) ;
464
+ return FindDescendants ( element , SearchType . DepthFirst ) ;
465
+ }
465
466
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 )
467
484
{
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 ) ;
469
510
470
- yield return child ;
511
+ yield return child ;
471
512
472
- foreach ( DependencyObject childOfChild in FindDescendants ( child ) )
513
+ bufferWriter . Add ( child ) ;
514
+ }
515
+
516
+ for ( int i = 0 ; i < bufferWriter . Count ; i ++ )
473
517
{
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
+ }
475
530
}
476
531
}
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
+ } ;
477
644
}
478
645
479
646
/// <summary>
0 commit comments