Colin Eberhardt's Adventures in WPF

Silverlight MultiBinding updated, adding support for ElementName and TwoWay binding

August 12th, 2010

This blog post describes an update to the Silverlight 4 MultiBinding technique I blogged about a couple of months ago to add support for ElementName binding and TwoWay binding.

A few months ago I posted an update to my MultiBinding solution for Silverlight 4. This technique allows you to perform the same kind of multibindings which are possible in WPF, where a property value is bound to multiple sources via a special value converter that implements the IMultiValueConverter interface, which describes how these values are combined. This update proved popular once again, and I received a few requests to add support for ElementName and TwoWay binding. I like challenge! This blog post describes how these two features were implemented, but if you just want to grab the code, you will find the link at the end of this article.

A brief recap

Before I go into the implementation details for the new features I will provide a brief recap of how my multibinding solution works. The XAML for creating a multibinding looks like the following:

<TextBlock Foreground="White" 
  <local:BindingUtil.MultiBindings>
    <local:MultiBindings>
      <local:MultiBinding TargetProperty="Text"
                          Converter="{StaticResource TitleConverter}">
        <local:BindingCollection>
          <Binding Path="Surname"/>
          <Binding Path="Forename"/>
        </local:BindingCollection>
      </local:MultiBinding>
      <local:MultiBinding TargetProperty="ToolTipService.ToolTip"
                          Converter="{StaticResource TitleConverter}">
        <local:BindingCollection>
          <Binding Path="Surname"/>
          <Binding Path="Forename"/>
        </local:BindingCollection>
      </local:MultiBinding>
    </local:MultiBindings>
  </local:BindingUtil.MultiBindings>
</TextBlock>

Here the Text property and attached ToolTip property of the TextBlock are bound to both the Surname and Forename property of the business object which is set as the DataContext of our view.

The value converter is as follows:

public class TitleConverter : IMultiValueConverter
{
 
  public object Convert(object[] values, Type targetType,
    object parameter, CultureInfo culture)
  {
    string forename = values[0] as string;
    string surname = values[1] as string;
 
    return string.Format("{0}, {1}", surname, forename);
  }
 
  ...
}

In the example below you can see that as you change the forename or surname, the title text and tooltip are updated by the multibinding.

So how does this work?

When a multibinding is created and added to an element via the BindingUtil.MultiBindings attached property, the MultiBinding instance is added to a virtual-branch, this is a branch of the visual tree that obtains the DataContext of the element to which it is bound, but is not added to the visual tree itself. The MultiBinding then creates a BindingSlave instance for each of the given Bindings. These ‘slave’ elements inherit the MultiBinding DataContext and are used to evaluate each of the individual Bindings. The MultiBinding instance aggregate the results of each binding with the IMultiValueConverter used to compute the ConvertedValue property which is bound to the target property on the element to which this MultiBinding as attached.

ElementName binding

So why does the above solution not work if one of the multibindings uses ElementName to locate the source rather than using the inherited DataContext? The problem here is that the MultiBinding and its BindingSlave instances are located in a virtual branch and are therefore not in the same namescope as the target element. As a result, they cannot perform look up of named elements.

In order to solve this problem I created a BindingSlave subclass specifically for ElementName binding. This slave locates the source element named via the ElementName property and sets it as the Source of the binding it uses to compute its Value property.

The only minor complication I encountered is that the MultiBinding might be constructed before the element referenced via ElementName. For this reason the binding slave must handle LayoutUpdated events to ensure that the named element is located if it is constructed after the multibinding.

public class ElementNameBindingSlave : BindingSlave
{
  private FrameworkElement _multiBindingTarget;
 
  /// <summary>
  /// The source element named in the ElementName binding
  /// </summary>
  private FrameworkElement _elementNameSource;
 
  private Binding _binding;
 
  public ElementNameBindingSlave(FrameworkElement target, Binding binding)
  {
    _multiBindingTarget = target;
    _binding = binding;
 
    // try to locate the named element
    ResolveElementNameBinding();
 
    _multiBindingTarget.LayoutUpdated += MultiBindingTarget_LayoutUpdated;
  }
 
  /// <summary>
  /// Try to locate the named element. If the element can be located, create the required
  /// binding.
  /// </summary>
  private void ResolveElementNameBinding()
  {
    _elementNameSource = _multiBindingTarget.FindName(_binding.ElementName) as FrameworkElement;
    if (_elementNameSource != null)
    {
      SetBinding(BindingSlave.ValueProperty, new Binding()
      {
        Source = _elementNameSource,
        Path = _binding.Path,
        Converter = _binding.Converter,
        ConverterParameter = _binding.ConverterParameter
      });
    }
  }
 
  private void MultiBindingTarget_LayoutUpdated(object sender, EventArgs e)
  {
    // try to locate the named element 
    ResolveElementNameBinding();
  }
}

The example below demonstrates element name binding by binding the Value of two Slider elements to a TextBlock which displays the sum of the two values.

<TextBlock Grid.Column="1">
  <local:BindingUtil.MultiBindings>
    <local:MultiBindings>
      <local:MultiBinding TargetProperty="Text"
                          Converter="{StaticResource SliderValueConverter}">
        <local:BindingCollection>
          <Binding ElementName="sliderOne" Path="Value"/>
          <Binding ElementName="sliderTwo" Path="Value"/>
        </local:BindingCollection>
      </local:MultiBinding>
    </local:MultiBindings>
  </local:BindingUtil.MultiBindings>
</TextBlock>
 
<Slider x:Name="sliderOne" Value="10"/>
 
<Slider x:Name="sliderTwo"  Value="20"/>

Here is the value converter that is used to compute the sum:

public class SliderValueConverter : IMultiValueConverter
{
 
  public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
  {
    if (values.Length != 2 ||
        values[0] == null ||
        values[1] == null)
      return null;
 
    return (double)values[0] + (double)values[1];
  }
 
  ...
}

You could of course use element name binding to combine the surname / forename in the first example in this blog post, but in this context it is more likely that you will want to back your UI with a business object. It is also possible to mix element name and regular (i.e. Source=DataContext) binding.

I find multibindings which use ElementName references most useful when creating complex UI layouts that do not lend themselves to being implemented using Panels. For a good example see my article on the implementation of a BulletGraph control which makes extensive use of this technique in order that the layout of the control is performed entirely within XAML.

TwoWay Binding

A two-way multibinding must be able to handle updates to the combined value and split it up into its constituent parts in order to update the multiple source bindings. In the first example where a forename and surname property are combined into a “surname, forename” string it is possible to convert back the other way, but in the example above where the values of two Sliders are combined a reverse conversion is not possible.

Modifying this multibinding solution to permit two way binding was relatively straightforward. A property changed event handler was added to the MultiBindig.ConverterValue (which is bound to the multi binding target) so that we can determine when changes have been made. This handler then uses the IMultiValueConverter to convert the value into the multiple source properties and updates the binding slaves accordingly:

/// <summary>
/// Handles property changes for the ConvertedValue property
/// </summary>
private void OnConvertedValuePropertyChanged()
{
    OnPropertyChanged("ConvertedValue");
 
    // if the value is being updated, but not due to one of the multibindings
    // then the target property has changed.
    if (!_updatingConvertedValue)
    {
      // convert back
      object[] convertedValues = Converter.ConvertBack(ConvertedValue, null,
          ConverterParameter, CultureInfo.InvariantCulture);
 
      // update all the binding slaves
      if (Children.Count == convertedValues.Length)
      {
        for (int index = 0; index < convertedValues.Length; index++)
        {
          ((BindingSlave)Children[index]).Value = convertedValues[index];
        }
      }
    }
}

In order to use two way multibinding both the multibinding mode and the bindings within the BindingCollection must be set to TwoWay. See the example given below:

<TextBlock Text="Surname:"/>
<TextBox  Grid.Column="1" Text="{Binding Path=Surname, Mode=TwoWay}"/>
 
<TextBlock Grid.Row="1" Text="Forename:"/>
<TextBox Grid.Row="1"  Grid.Column="1" Text="{Binding Path=Forename, Mode=TwoWay}"/>
 
<TextBlock Grid.Row="2" Text="Combined:"/>
<TextBox Grid.Row="2"  Grid.Column="1" >
  <local:BindingUtil.MultiBindings>
    <local:MultiBindings>
      <local:MultiBinding TargetProperty="Text"
                        Converter="{StaticResource TitleConverter}"
                        Mode="TwoWay">
        <local:BindingCollection>
          <Binding Path="Surname" Mode="TwoWay"/>
          <Binding Path="Forename" Mode="TwoWay"/>
        </local:BindingCollection>
      </local:MultiBinding>
    </local:MultiBindings>
  </local:BindingUtil.MultiBindings>
</TextBox>

You can have try of two way multibinding below where the values of the forename and surname text boxes are combined in the box below. However, you ca also make updates to the combined result which will then be converted back into its constituent parts:

And here is the value converter:

public class TitleConverter : IMultiValueConverter
{
  #region IMultiValueConverter Members
 
  public object Convert(object[] values, Type targetType,
    object parameter, System.Globalization.CultureInfo culture)
  {
    string forename = values[0] as string;
    string surname = values[1] as string;
 
    return string.Format("{0}, {1}", surname, forename);
  }
 
  public object[] ConvertBack(object value, Type[] targetTypes,
    object parameter, System.Globalization.CultureInfo culture)
  {
    string source = value as string;
    var pos = source.IndexOf(", ");
 
    string forename = source.Substring(pos + 2);
    string surname = source.Substring(0, pos);
 
    return new object[] { forename, surname };
  }
 
  #endregion
}

So, there you have it, two-way and element name multibinding :-)

You can download the full source, including a WPF build (thanks to Stefan Olson) here: SLMultiBinding.zip

Now … go forth and multibind.

Regards, Colin E.

Silverlight MultiBinding solution for Silverlight 4

May 10th, 2010

In this post I describe an update to the Silverlight MultiBinding solution I presented last year. This update includes support for Silverlight 4, attached properties and multiple bindings on a single object.

UPDATE: I have updated this code to include ElementName and TwoWay binding. Grab the latest copy here.

MultiBinding is a WPF feature that allows you to bind a single property to a number of sources, with the source values being combined by a value converter. This is a feature that is missing from Silverlight. About a year ago I developed a MultiBinding solution for Silverlight, which has proven very popular! I even had an email from an Microsoft Attorney asking if they could use it in the Silverlight Facebook client (How cool is that :-) ). I was also very happy when Stefan Olson made a few updates to this code to allow multiple MultiBindings on a single object and spotted the SL4 issue. This blog post is a quick demonstration of these new features …

The following application is an example of MultiBindings in action:

In the above application, the TextBox at the top of the page is bound to both the surname and forename properties of our data-object via converter that takes the first letter of the forename and the surname. If you edit the surname or forename (hitting enter or changing focus to commit), the title is updated automatically. The title tooltip is bound to the all three object properties via a different value converter that concatenates all three.

The XAML for this binding is show below:

<TextBlock x:Name="Block" Foreground="White" FontSize="13" Margin="5,0,0,0">
    <local:BindingUtil.MultiBindings>
        <local:MultiBindings>
            <local:MultiBinding TargetProperty="Text"
                                Converter="{StaticResource TitleSummaryConverter}">
                <local:MultiBinding.Bindings>
                    <local:BindingCollection>
                        <Binding Path="Surname"/>                            
                        <Binding Path="Forename"/>
                        </local:BindingCollection>
                </local:MultiBinding.Bindings>
            </local:MultiBinding>
            <local:MultiBinding TargetProperty="ToolTipService.ToolTip"
                                Converter="{StaticResource TitleConverter}">
                <local:MultiBinding.Bindings>
                    <local:BindingCollection>
                        <Binding Path="Surname"/>                            
                        <Binding Path="Forename"/>
                        <Binding Path="Age"/>
                    </local:BindingCollection>
                </local:MultiBinding.Bindings>
            </local:MultiBinding>
        </local:MultiBindings>
    </local:BindingUtil.MultiBindings>
</TextBlock>

In the above you can see that our TextBlock has two multibindings, one on Forename and Surname, and the other which includes all three properties. Note, the second multibinding is on the ToolTipService.ToolTip attached property.

You can download the source for Silverlight MultiBinding here: SLMultiBindingUpdate.zip – thanks again to Stefan Olson who added WPF support to this technique.

If you are interested in the technical details of how this works, I refer you to the original article which describes how the code builds a ‘virtual’ branch on the visual tree in order to evaluate your bindings:

I hope this update is of use to SL4 developers. If you have any feedback, please leave a comment below.

You can download the source for Silverlight MultiBinding here: SLMultiBindingUpdate.zip

Regards, Colin E.

Ineffective Data Visualisation … and how to fix it

April 30th, 2010

This blog post looks at a recently published set of charts in a UK newspaper and how they fail to help in the comprehension of the data which they visualise. I will also look at much more effective ways of displaying this same data.

At Scott Logic we tend to spend quite a bit of our time thinking about the effective visualisation of data. In the financial sector data abounds, with stock prices changing every second, traders and analysts have a lot of data at their disposal. Without methods to analyse and visualise this data it is easy to gets lost in the sheer quantity. For this reason, the works of Edward Tufte and Stephen Few are often passed round the office!

With the UK General election looming, statistics and trends are a common feature in our news. Unfortunately these seems to lead to a whole slew of charts and graphics which succeed in their artistry but fail miserably in helping the reader understand the data which the graphics represent.

Just this morning I was reading an article in the Metro newspaper about the changes in party support over the past week’s opinion polls and the voting habits of different age groups. The article was supported by the following graphic:

One of the key ideas behind the charting and visualising of data is to allow the reader to rapidly digest the data, spot trends, understand relationships, etc… Unfortunately, the graphics above fail miserably in this respect. Here are some of the faults I spotted:

(1) Chart title – the main chart title relates to the chart on the right, but not to the chart on the left.

(2) Choice of colours - if you look at the datapoints on the right-hand chart it is not easy to determine which party they relate to due to a poor choice of colour, peach and salmon?!

(3) Trends are hidden - the main purpose of the right hand chart is to illustrate the trends in party support with relation to age. To do this you have to hunt for the same coloured point from one age band to the next.

(4) Gridlines – the right-hand chart has labels every 5 percent point, but gridlines every 2 points. This means that there is not a gridline for each label, this makes it very hard to determine the actual value of each datapoint.

(5) Doughnut – the doughnut (i.e. the stylised pie-chart with a yummy hole) has a couple of problems, which week does it represent the split in party support for? this week? last week? Also, which is the bigger pie piece, Lib. Dem. or Conservative? It is impossible to tell without reaching for your protractor (I seem to have left mine at home today).

(6) Arbitrary graphics – I cannot see any reason, other than artistic licence, for the vertical highlights on the right-hand chart. This is misleading, it draws the eye to these areas of the chart with the expectation that they are highlighted for some reason.

(7) Change not visualised – the change in support from last week to this week is not visualised in any way, it is presented in tabular form. This means that the reader might miss important information, for example, a 10 percent point raise from 10 to 20 is clearly more significant than a rise from 70 to 80, this is made quite obvious if we visualise the change.

(8) Units – the indication of units is quite distant from the data.

I am sure there are more problems … if you spot any others, leave a comment.

So, let’s see if we can rectify some of these issues. Starting with the chart on the right, its main purpose is to illustrate the relationship between age group and party support. In this case it is vital that the reader of this chart can easily navigate from the datapoint which indicated Conservative party support (for example) in one age range the next. With this in a mind, a line chart is much more appropriate and the trends become immediately visible:

Note also the colours, these are no longer arbitrarily assigned. Each political party has a party colour which, if used, allows most people to instantly determine the party each line relates to. The gridlines are also more sensibly placed and we have lost the ‘artistic’ highlights. Finally, the Y axis starts at zero, this allow the reader to instantly see the scale of the differences between the popularity figures without having to read the axis range.

Now, let’s turn our attention to the doughnut and the accompanying table. The reader should be able to determine two key pieces of information from these, (1) The relative popularity of each party and (2) The change in popularity since last week. It would be ideal if the two could be combined so that the reader can also compare the scale of this change with the overall difference in popularity. In order to allow this, it is much better to display the information in a single chart:

With the above chart we can see at-a-glance the relative popularity of each party again displayed in party colours. I must admit it took me a little while to work out how to indicate which columns represented this week’s figures and which were last week’s. I tried using variations in the column intensity, but this is a hard concept to indicate via a key, I also added small labels, but this just complicates and clutters. Finally I realised that by adding a pattern I could maintain the party colour, yet clearly relate the columns for the previous week (This makes use of the Gestalt Principle of Similarity). Unfortunately Excel 2007, which I used to create these charts, does not support patterns, however I found this excellent add-in from Andy Pope, and I thoroughly recommend it.

I think the two charts I have presented are much clearer than those in the original graphics from the newspaper article. However, a direct comparison between the two would not be entirely fair. The graphics used in the media often have further constraints imposed on them, (1) They are often restricted in size, having to fit within a fixed page size layout, (2) They should be eye-catching and visually appealing, drawing a potential reader towards the article.

With this in mind, I have re-worked the graphs above into the same layout and size as the originals. I have even added drop-shadows for visual appeal …

I think the above is a good compromise between providing an artistically pleasing graphic whilst still allowing the reader to understand the data (and from there spot trends etc…).

Regards, Colin E.

Update: Thanks to Graham Odds for a few extra ideas about tidying up the final charts.

Linq to Visual Tree

March 4th, 2010

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 sourcecode 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;
 
// all items within the visual tree
IEnumerable<DependencyObject> allDescendants = this.Descendants();
/*
gives ...
0 {Grid} 	[GridOne]
1 {TextBox} 	[TextBoxOne]
2 {StackPanel} 	[StackPanelOne]
3 {TextBox} 	[TextBoxTwo]
4 {Grid} 	[GridTwo]
5 {TextBox} 	[TextBoxThree]
6 {StackPanel} 	[StackPanelTwo]
7 {TextBox} 	[TextBoxFour]
*/
 
// all items within the visual tree of 'GridTwo'
var descendantsOfGridTwo = GridTwo.Descendants();
/*
gives ...
0 {TextBox} 	[TextBoxThree]
1 {StackPanel} 	[StackPanelTwo]
2 {TextBox} 	[TextBoxFour]
*/

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:

// all items within the visual tree of 'GridTwo' that are textboxes
var textBoxDescendantsOfGridTwo = GridTwo.Descendants()
                                         .Where(i => i is TextBox);
/*
0 {TextBox} 	[TextBoxThree]
1 {TextBox} 	[TextBoxFour]
*/
 
// a shorthand using the generic version of Descendants
var textBoxDescendantsOfGridTwo2 = GridTwo.Descendants<TextBox>();
/*
0 {TextBox} 	[TextBoxThree]
1 {TextBox} 	[TextBoxFour]
*/

Elements

The elements extension method obtains all the direct children of an item in the visual tree:

// find all direct children of this user control 
var userControlChildren = this.Elements();
/*
0 {Grid} 	[GridOne]
*/
 
// find all direct children of the grid 'GridTwo'
var gridChildren = GridTwo.Elements();
/*
0 {TextBox} 	[TextBoxThree]
1 {StackPanel} 	[StackPanelTwo]
*/
 
// find all direct children of the grid 'GridTwo' that are StackPanels
var gridChildren2 = GridTwo.Elements<StackPanel>();
/*
0 {StackPanel} 	[StackPanelTwo]
*/

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:

// the ancestors for 'TextBoxFour'
var ancestors = TextBoxFour.Ancestors();
/*
0 {StackPanel} 	[StackPanelTwo]
1 {Grid} 	[GridTwo]
2 {StackPanel} 	[StackPanelOne]
3 {Grid} 	[GridOne]
4 {MainPage} 	[]
*/
 
// the ancestors for 'TextBoxFour' that are StackPanels
var stackPanelAncestors = TextBoxFour.Ancestors<StackPanel>();
/*
0 {StackPanel} 	[StackPanelTwo]
1 {StackPanel} 	[StackPanelOne]
*/

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 TextBoxs 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;
/*
0 {TextBox} 	[TextBoxOne]
1 {TextBox} 	[TextBoxThree]
*/

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 StackPanels that are within another StackPanels 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;
/*
0 {StackPanel} 	[StackPanelTwo]
*/

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
{
    /// <summary>
    /// Adapts a DependencyObject to provide methods required for generate
    /// a Linq To Tree API
    /// </summary>
    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
{
    /// <summary>
    /// Defines an interface that must be implemented to generate the LinqToTree methods
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public interface ILinqTree<T>
    {
        IEnumerable<T> Children();

        T Parent { get; }
    }

    public static class TreeExtensions
    {
        /// <summary>
        /// Returns a collection of descendant elements.
        /// </summary>
        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;
                }
            }
        }

        /// <summary>
        /// Returns a collection containing this element and all descendant elements.
        /// </summary>
        public static IEnumerable<DependencyObject> DescendantsAndSelf(this DependencyObject item)
        {
            yield return item;

            foreach (var child in item.Descendants())
            {
                yield return child;
            }
        }

        /// <summary>
        /// Returns a collection of ancestor elements.
        /// </summary>
        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;
            }
        }

        /// <summary>
        /// Returns a collection containing this element and all ancestor elements.
        /// </summary>
        public static IEnumerable<DependencyObject> AncestorsAndSelf(this DependencyObject item)
        {
            yield return item;

            foreach (var ancestor in item.Ancestors())
            {
                yield return ancestor;
            }
        }

        /// <summary>
        /// Returns a collection of child elements.
        /// </summary>
        public static IEnumerable<DependencyObject> Elements(this DependencyObject item)
        {
            ILinqTree<DependencyObject> adapter = new VisualTreeAdapter(item);
            foreach (var child in adapter.Children())
            {
                yield return child;
            }
        }

        /// <summary>
        /// Returns a collection of the sibling elements before this node, in document order.
        /// </summary>
        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;
            }
        }

        /// <summary>
        /// Returns a collection of the after elements after this node, in document order.
        /// </summary>
        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;
            }
        }

        /// <summary>
        /// Returns a collection containing this element and all child elements.
        /// </summary>
        public static IEnumerable<DependencyObject> ElementsAndSelf(this DependencyObject item)
        {
            yield return item;

            foreach (var child in item.Elements())
            {
                yield return child;
            }
        }

        /// <summary>
        /// Returns a collection of descendant elements which match the given type.
        /// </summary>
        public static IEnumerable<DependencyObject> Descendants<T>(this DependencyObject item)
        {
            return item.Descendants().Where(i => i is T).Cast<DependencyObject>();
        }

        /// <summary>
        /// Returns a collection of the sibling elements before this node, in document order
        /// which match the given type.
        /// </summary>
        public static IEnumerable<DependencyObject> ElementsBeforeSelf<T>(this DependencyObject item)
        {
            return item.ElementsBeforeSelf().Where(i => i is T).Cast<DependencyObject>();
        }

        /// <summary>
        /// Returns a collection of the after elements after this node, in document order
        /// which match the given type.
        /// </summary>
        public static IEnumerable<DependencyObject> ElementsAfterSelf<T>(this DependencyObject item)
        {
            return item.ElementsAfterSelf().Where(i => i is T).Cast<DependencyObject>();
        }

        /// <summary>
        /// Returns a collection containing this element and all descendant elements
        /// which match the given type.
        /// </summary>
        public static IEnumerable<DependencyObject> DescendantsAndSelf<T>(this DependencyObject item)
        {
            return item.DescendantsAndSelf().Where(i => i is T).Cast<DependencyObject>();
        }

        /// <summary>
        /// Returns a collection of ancestor elements which match the given type.
        /// </summary>
        public static IEnumerable<DependencyObject> Ancestors<T>(this DependencyObject item)
        {
            return item.Ancestors().Where(i => i is T).Cast<DependencyObject>();
        }

        /// <summary>
        /// Returns a collection containing this element and all ancestor elements
        /// which match the given type.
        /// </summary>
        public static IEnumerable<DependencyObject> AncestorsAndSelf<T>(this DependencyObject item)
        {
            return item.AncestorsAndSelf().Where(i => i is T).Cast<DependencyObject>();
        }

        /// <summary>
        /// Returns a collection of child elements which match the given type.
        /// </summary>
        public static IEnumerable<DependencyObject> Elements<T>(this DependencyObject item)
        {
            return item.Elements().Where(i => i is T).Cast<DependencyObject>();
        }

        /// <summary>
        /// Returns a collection containing this element and all child elements.
        /// which match the given type.
        /// </summary>
        public static IEnumerable<DependencyObject> ElementsAndSelf<T>(this DependencyObject item)
        {
            return item.ElementsAndSelf().Where(i => i is T).Cast<DependencyObject>();
        }

    }

    public static class EnumerableTreeExtensions
    {
        /// <summary>
        /// Applies the given function to each of the items in the supplied
        /// IEnumerable.
        /// </summary>
        private static IEnumerable<DependencyObject> DrillDown(this IEnumerable<DependencyObject> items,
            Func<DependencyObject, IEnumerable<DependencyObject>> function)
        {
            foreach (var item in items)
            {
                foreach (var itemChild in function(item))
                {
                    yield return itemChild;
                }
            }
        }

        /// <summary>
        /// Applies the given function to each of the items in the supplied
        /// IEnumerable, which match the given type.
        /// </summary>
        public static IEnumerable<DependencyObject> DrillDown<T>(this IEnumerable<DependencyObject> items,
            Func<DependencyObject, IEnumerable<DependencyObject>> function)
            where T : DependencyObject
        {
            foreach (var item in items)
            {
                foreach (var itemChild in function(item))
                {
                    if (itemChild is T)
                    {
                        yield return (T)itemChild;
                    }
                }
            }
        }

        /// <summary>
        /// Returns a collection of descendant elements.
        /// </summary>
        public static IEnumerable<DependencyObject> Descendants(this IEnumerable<DependencyObject> items)
        {
            return items.DrillDown(i => i.Descendants());
        }

        /// <summary>
        /// Returns a collection containing this element and all descendant elements.
        /// </summary>
        public static IEnumerable<DependencyObject> DescendantsAndSelf(this IEnumerable<DependencyObject> items)
        {
            return items.DrillDown(i => i.DescendantsAndSelf());
        }

        /// <summary>
        /// Returns a collection of ancestor elements.
        /// </summary>
        public static IEnumerable<DependencyObject> Ancestors(this IEnumerable<DependencyObject> items)
        {
            return items.DrillDown(i => i.Ancestors());
        }

        /// <summary>
        /// Returns a collection containing this element and all ancestor elements.
        /// </summary>
        public static IEnumerable<DependencyObject> AncestorsAndSelf(this IEnumerable<DependencyObject> items)
        {
            return items.DrillDown(i => i.AncestorsAndSelf());
        }

        /// <summary>
        /// Returns a collection of child elements.
        /// </summary>
        public static IEnumerable<DependencyObject> Elements(this IEnumerable<DependencyObject> items)
        {
            return items.DrillDown(i => i.Elements());
        }

        /// <summary>
        /// Returns a collection containing this element and all child elements.
        /// </summary>
        public static IEnumerable<DependencyObject> ElementsAndSelf(this IEnumerable<DependencyObject> items)
        {
            return items.DrillDown(i => i.ElementsAndSelf());
        }

        /// <summary>
        /// Returns a collection of descendant elements which match the given type.
        /// </summary>
        public static IEnumerable<DependencyObject> Descendants<T>(this IEnumerable<DependencyObject> items)
            where T : DependencyObject
        {
            return items.DrillDown<T>(i => i.Descendants());
        }

        /// <summary>
        /// Returns a collection containing this element and all descendant elements.
        /// which match the given type.
        /// </summary>
        public static IEnumerable<DependencyObject> DescendantsAndSelf<T>(this IEnumerable<DependencyObject> items)
            where T : DependencyObject
        {
            return items.DrillDown<T>(i => i.DescendantsAndSelf());
        }

        /// <summary>
        /// Returns a collection of ancestor elements which match the given type.
        /// </summary>
        public static IEnumerable<DependencyObject> Ancestors<T>(this IEnumerable<DependencyObject> items)
            where T : DependencyObject
        {
            return items.DrillDown<T>(i => i.Ancestors());
        }

        /// <summary>
        /// Returns a collection containing this element and all ancestor elements.
        /// which match the given type.
        /// </summary>
        public static IEnumerable<DependencyObject> AncestorsAndSelf<T>(this IEnumerable<DependencyObject> items)
            where T : DependencyObject
        {
            return items.DrillDown<T>(i => i.AncestorsAndSelf());
        }

        /// <summary>
        /// Returns a collection of child elements which match the given type.
        /// </summary>
        public static IEnumerable<DependencyObject> Elements<T>(this IEnumerable<DependencyObject> items)
            where T : DependencyObject
        {
            return items.DrillDown<T>(i => i.Elements());
        }

        /// <summary>
        /// Returns a collection containing this element and all child elements.
        /// which match the given type.
        /// </summary>
        public static IEnumerable<DependencyObject> ElementsAndSelf<T>(this IEnumerable<DependencyObject> items)
            where T : DependencyObject
        {
            return items.DrillDown<T>(i => i.ElementsAndSelf());
        }
    }
}

Regards, Colin E.