Friday, March 19, 2010

Silverlight Visual Tree Walker

Constrained by Silverlight’s FindName method and inspired by a blog note by Jim Baltzell-Gauthier and my own implementation of tree walker for ASP.NET, I came up with a corresponding code snippet for Silverlight.

What you get is two extension methods, Find to search for a single child component and FindAll to search for all children matching specified predicate.

Note that this way you have a complete freedom of what you would like to find. Controls of specific name, specific type, anything – is just a matter of correct predicate.

public static class VisualTreeWalker
{
public static FrameworkElement Find(
this DependencyObject Parent,
Predicate<FrameworkElement> Predicate )
{
if ( Parent is FrameworkElement )
if ( Predicate( (FrameworkElement)Parent ) )
return (FrameworkElement)Parent;

foreach ( DependencyObject child in GetChildren( Parent ) )
{
try
{
FrameworkElement childElement = child as FrameworkElement;
if ( childElement != null )
if ( Predicate( childElement ) )
return childElement;
}
catch { }
}
return null;
}

public static FrameworkElement[] FindAll(
this DependencyObject Parent,
Predicate<FrameworkElement> Predicate )
{
List<FrameworkElement> ret = new List<FrameworkElement>();

FindAllHelper( Parent, Predicate, ret );

return ret.ToArray();
}

private static void FindAllHelper(
DependencyObject Parent,
Predicate<FrameworkElement> Predicate,
List<FrameworkElement> results )
{
if ( Parent is FrameworkElement )
if ( Predicate( (FrameworkElement)Parent ) )
results.Add( (FrameworkElement)Parent );

// rekursja
foreach ( DependencyObject child in GetChildren( Parent ) )
{
FindAllHelper( child, Predicate, results );
}
}

private static IEnumerable<DependencyObject>
GetChildren( DependencyObject Parent )
{
int childCount = VisualTreeHelper.GetChildrenCount( Parent );

for ( int i = 0; i < childCount; i++ )
{
yield return VisualTreeHelper.GetChild( Parent, i );
}
}
}





As an example, suppose you have a complex Silverlight Grid, called TheGrid, and somewhere deeply inside it there are three ToggleButtons (that was an original case which motivated me to write the above snippet). Now I just iterate:




foreach ( ToggleButton toggleButton in
TheGrid /* parent control */
.FindAll( c => c is ToggleButton ) /* searching */
.Select( f => f as ToggleButton ) ) /* projection - casting */
{
/* now I got all my child controls I need */
}



No comments: