This blog post demonstrates a Linq API which can be used to query the WPF / Silverlight Visual Tree. You can find a few other Linq to Visual Tree techniques on other blogs, but what makes this one unique is that it retains, and allows queries that make use of the tree like structure rather than simply flattening it.
I have recently published an article on CodeProject which describes a technique for generating Linq API for querying tree-like structures. This blog post makes use of a generated API for WPF / Silverlight. If you are interested in the more generic approach, and how this API was constructed, (and how it is influenced by XPath) head on over to CodeProject …
What I will provide here is a brief overview of the Linq to Visual Tree API. The full source code for this API is at the end of this article.
The Linq to Visual Tree API defines a number of extension methods on DependencyObject
that provide mechanisms for navigating to other DependencyObject
instances. I will provide a few examples that query the following simple markup:
<Grid x:Name="GridOne" Background="White">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBox x:Name="TextBoxOne" Text="One" Grid.Row="0" />
<StackPanel x:Name="StackPanelOne" Grid.Row="1">
<TextBox x:Name="TextBoxTwo" Text="Two" />
<Grid x:Name="GridTwo">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBox x:Name="TextBoxThree" Text="Three" Grid.Row="0" />
<StackPanel x:Name="StackPanelTwo" Grid.Row="1">
<TextBox x:Name="TextBoxFour" Text="Four"/>
</StackPanel>
</Grid>
</StackPanel>
</Grid>
We’ll start with a simple example. Include the Linq to Visual Tree namespace, then use the Descendants method to obtain all the descendants (i.e., children and children’s children, etc.) of an object within the visual tree.
Descendants
using LinqToVisualTree;
IEnumerable<DependencyObject> allDescendants = this.Descendants();
var descendantsOfGridTwo = GridTwo.Descendants();
Each of the extension methods also has a corresponding method with a generic type parameter that filters the collection to find elements of a specific type:
var textBoxDescendantsOfGridTwo = GridTwo.Descendants()
.Where(i => i is TextBox);
var textBoxDescendantsOfGridTwo2 = GridTwo.Descendants<TextBox>();
Elements
The elements extension method obtains all the direct children of an item in the visual tree:
var userControlChildren = this.Elements();
var gridChildren = GridTwo.Elements();
var gridChildren2 = GridTwo.Elements<StackPanel>();
There are also, ElementsBeforeSelf
and ElementsAfterSelf
methods that return the elements before and after the item which the method is being invoked upon.
Ancestors
The ancestors methods traverse the tree towards the root, finding all the ancestors:
var ancestors = TextBoxFour.Ancestors();
var stackPanelAncestors = TextBoxFour.Ancestors<StackPanel>();
Putting It All Together
The Linq to Tree API not only defines extension methods on DependencyObject
, but also the same extension methods on IEnumerable<DependencyObject>
. Unless you have previous experience of Linq to XML, I would strongly suggest reading my CodeProject article to understand how this works!
This allows you to form much more complex queries. For example, you can find all TextBox
s that have a Grid
as a direct parent:
var itemsFluent = this.Descendants<TextBox>()
.Where(i => i.Ancestors().FirstOrDefault() is Grid);
var itemsQuery = from v in this.Descendants<TextBox>()
where v.Ancestors().FirstOrDefault() is Grid
select v;
Here, you can also see we are mixing the fluent and query syntax for Linq. Both give the same result.
The next example finds all StackPanel
s that are within another StackPanel
s visual tree:
var items2Fluent = this.Descendants<StackPanel>()
.Descendants<StackPanel>();
var items2Query = from i in
(from v in this.Descendants<StackPanel>()
select v).Descendants<StackPanel>()
select i;
Finally, this one-liner outputs the entire visual tree in ASCII! It makes use of the DescendantsAndSelf
, Ancestors
and ElementsBeforeSelf
methods, plus the funky Linq Aggregate
method.
string tree = this.DescendantsAndSelf().Aggregate("",
(bc, n) => bc + n.Ancestors().Aggregate("",
(ac, m) => (m.ElementsAfterSelf().Any() ? "| " : " ") + ac,
ac => ac + (n.ElementsAfterSelf().Any() ? "+-" : "\\-")) + n.GetType().Name + "\n");
\-MainPage
\-Grid
+-TextBox
| \-Grid
| +-Border
| | \-Grid
| | +-Border
| | \-Border
| | \-ScrollViewer
| | \-Border
| | \-Grid
| | +-ScrollContentPresenter
| | | \-TextBoxView
| | +-Rectangle
| | +-ScrollBar
| | \-ScrollBar
| +-Border
| +-Border
| \-Border
| \-Grid
| +-Path
| \-Path
\-StackPanel
+-TextBox
| \-Grid
| +-Border
| | \-Grid
| | +-Border
| | \-Border
| | \-ScrollViewer
| | \-Border
| | \-Grid
| | +-ScrollContentPresenter
| | | \-TextBoxView
| | +-Rectangle
| | +-ScrollBar
| | \-ScrollBar
| +-Border
| +-Border
| \-Border
| \-Grid
| +-Path
| \-Path
\-Grid
+-TextBox
| \-Grid
| +-Border
| | \-Grid
| | +-Border
| | \-Border
| | \-ScrollViewer
| | \-Border
| | \-Grid
| | +-ScrollContentPresenter
| | | \-TextBoxView
| | +-Rectangle
| | +-ScrollBar
| | \-ScrollBar
| +-Border
| +-Border
| \-Border
| \-Grid
| +-Path
| \-Path
\-StackPanel
\-TextBox
\-Grid
+-Border
| \-Grid
| +-Border
| \-Border
| \-ScrollViewer
| \-Border
| \-Grid
| +-ScrollContentPresenter
| | \-TextBoxView
| +-Rectangle
| +-ScrollBar
| \-ScrollBar
+-Border
+-Border
\-Border
\-Grid
+-Path
\-Path
Note: This was invoked after the LayoutUpdated
event so that we not only see the elements from our XAML, but also the elements created from their templates, giving us our full run-time visual tree.
You can download a simple Silverlight application that demonstrated all the examples given above: LinqToTree.zip.
Or, if you just want the Linq to VisualTree code, you can copy-n-paste from the windows below which has the entire API, which includes the methods illustrated above, plus their IEnumerable
equivalents, and a few others I have not illustrated.
using System;
using System.Linq;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Media;
namespace LinqToVisualTree
{
public class VisualTreeAdapter : ILinqTree<dependencyobject>
{
private DependencyObject _item;
public VisualTreeAdapter(DependencyObject item)
{
_item = item;
}
public IEnumerable<dependencyobject> Children()
{
int childrenCount = VisualTreeHelper.GetChildrenCount(_item);
for (int i = 0; i < childrenCount; i++)
{
yield return VisualTreeHelper.GetChild(_item, i);
}
}
public DependencyObject Parent
{
get
{
return VisualTreeHelper.GetParent(_item);
}
}
}
}
namespace LinqToVisualTree
{
public interface ILinqTree<t>
{
IEnumerable<t> Children();
T Parent { get; }
}
public static class TreeExtensions
{
public static IEnumerable<dependencyobject>
Descendants(this DependencyObject item)
{
ILinqTree<dependencyobject> adapter = new VisualTreeAdapter(item);
foreach (var child in adapter.Children())
{
yield return child;
foreach (var grandChild in child.Descendants())
{
yield return grandChild;
}
}
}
public static IEnumerable<dependencyobject>
DescendantsAndSelf(this DependencyObject item)
{
yield return item;
foreach (var child in item.Descendants())
{
yield return child;
}
}
public static IEnumerable<dependencyobject> Ancestors(this DependencyObject item)
{
ILinqTree<dependencyobject> adapter = new VisualTreeAdapter(item);
var parent = adapter.Parent;
while (parent != null)
{
yield return parent;
adapter = new VisualTreeAdapter(parent);
parent = adapter.Parent;
}
}
public static IEnumerable<dependencyobject>
AncestorsAndSelf(this DependencyObject item)
{
yield return item;
foreach (var ancestor in item.Ancestors())
{
yield return ancestor;
}
}
public static IEnumerable<dependencyobject> Elements(this DependencyObject item)
{
ILinqTree<dependencyobject> adapter = new VisualTreeAdapter(item);
foreach (var child in adapter.Children())
{
yield return child;
}
}
public static IEnumerable<dependencyobject>
ElementsBeforeSelf(this DependencyObject item)
{
if (item.Ancestors().FirstOrDefault() == null)
yield break;
foreach (var child in item.Ancestors().First().Elements())
{
if (child.Equals(item))
break;
yield return child;
}
}
public static IEnumerable<dependencyobject>
ElementsAfterSelf(this DependencyObject item)
{
if (item.Ancestors().FirstOrDefault() == null)
yield break;
bool afterSelf = false;
foreach (var child in item.Ancestors().First().Elements())
{
if (afterSelf)
yield return child;
if (child.Equals(item))
afterSelf = true;
}
}
public static IEnumerable<dependencyobject>
ElementsAndSelf(this DependencyObject item)
{
yield return item;
foreach (var child in item.Elements())
{
yield return child;
}
}
public static IEnumerable<dependencyobject>
Descendants<t>(this DependencyObject item)
{
return item.Descendants().Where(i => i is T).Cast<dependencyobject>();
}
public static IEnumerable<dependencyobject>
ElementsBeforeSelf<t>(this DependencyObject item)
{
return item.ElementsBeforeSelf().Where(i => i is T).Cast<dependencyobject>();
}
public static IEnumerable<dependencyobject>
ElementsAfterSelf<t>(this DependencyObject item)
{
return item.ElementsAfterSelf().Where(i => i is T).Cast<dependencyobject>();
}
public static IEnumerable<dependencyobject>
DescendantsAndSelf<t>(this DependencyObject item)
{
return item.DescendantsAndSelf().Where(i => i is T).Cast<dependencyobject>();
}
public static IEnumerable<dependencyobject>
Ancestors<t>(this DependencyObject item)
{
return item.Ancestors().Where(i => i is T).Cast<dependencyobject>();
}
public static IEnumerable<dependencyobject>
AncestorsAndSelf<t>(this DependencyObject item)
{
return item.AncestorsAndSelf().Where(i => i is T).Cast<dependencyobject>();
}
public static IEnumerable<dependencyobject>
Elements<t>(this DependencyObject item)
{
return item.Elements().Where(i => i is T).Cast<dependencyobject>();
}
public static IEnumerable<dependencyobject>
ElementsAndSelf<t>(this DependencyObject item)
{
return item.ElementsAndSelf().Where(i => i is T).Cast<dependencyobject>();
}
}
public static class EnumerableTreeExtensions
{
private static IEnumerable<dependencyobject>
DrillDown(this IEnumerable<dependencyobject> items,
Func<dependencyobject,>> function)
{
foreach (var item in items)
{
foreach (var itemChild in function(item))
{
yield return itemChild;
}
}
}
public static IEnumerable<dependencyobject>
DrillDown<t>(this IEnumerable<dependencyobject> items,
Func<dependencyobject,>> function)
where T : DependencyObject
{
foreach (var item in items)
{
foreach (var itemChild in function(item))
{
if (itemChild is T)
{
yield return (T)itemChild;
}
}
}
}
public static IEnumerable<dependencyobject>
Descendants(this IEnumerable<dependencyobject> items)
{
return items.DrillDown(i => i.Descendants());
}
public static IEnumerable<dependencyobject>
DescendantsAndSelf(this IEnumerable<dependencyobject> items)
{
return items.DrillDown(i => i.DescendantsAndSelf());
}
public static IEnumerable<dependencyobject>
Ancestors(this IEnumerable<dependencyobject> items)
{
return items.DrillDown(i => i.Ancestors());
}
public static IEnumerable<dependencyobject>
AncestorsAndSelf(this IEnumerable<dependencyobject> items)
{
return items.DrillDown(i => i.AncestorsAndSelf());
}
public static IEnumerable<dependencyobject>
Elements(this IEnumerable<dependencyobject> items)
{
return items.DrillDown(i => i.Elements());
}
public static IEnumerable<dependencyobject>
ElementsAndSelf(this IEnumerable<dependencyobject> items)
{
return items.DrillDown(i => i.ElementsAndSelf());
}
public static IEnumerable<dependencyobject>
Descendants<t>(this IEnumerable<dependencyobject> items)
where T : DependencyObject
{
return items.DrillDown<t>(i => i.Descendants());
}
public static IEnumerable<dependencyobject>
DescendantsAndSelf<t>(this IEnumerable<dependencyobject> items)
where T : DependencyObject
{
return items.DrillDown<t>(i => i.DescendantsAndSelf());
}
public static IEnumerable<dependencyobject>
Ancestors<t>(this IEnumerable<dependencyobject> items)
where T : DependencyObject
{
return items.DrillDown<t>(i => i.Ancestors());
}
public static IEnumerable<dependencyobject>
AncestorsAndSelf<t>(this IEnumerable<dependencyobject> items)
where T : DependencyObject
{
return items.DrillDown<t>(i => i.AncestorsAndSelf());
}
public static IEnumerable<dependencyobject>
Elements<t>(this IEnumerable<dependencyobject> items)
where T : DependencyObject
{
return items.DrillDown<t>(i => i.Elements());
}
public static IEnumerable<dependencyobject>
ElementsAndSelf<t>(this IEnumerable<dependencyobject> items)
where T : DependencyObject
{
return items.DrillDown<t>(i => i.ElementsAndSelf());
}
}
}
Regards,
Colin E.