Colin Eberhardt's Adventures in WPF

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.

Declarative Dependency Property Definition with T4 + DTE

August 18th, 2009

This blog post describes a technique for specifying WPF / Silverlight Dependency Properties declaritively via attributes as illustrated by the following example:

[DependencyPropertyDecl("Maximum", typeof(double), 0.0)]
[DependencyPropertyDecl("Minimum", typeof(double), 0.0)]
public partial class RangeControl : UserControl
{
    ...
}

At design-time the declarations are read via a T4 template and the required code is generated. For more information, read on …

A few months ago I wrote an article on codeproject about a technique for generating dependency properties via T4 templates. In brief; with this technique, you provide an XML description of your classes and their dependency properties, run the T4 template, and partial classes are generated which contain these properties – no more writing DP boiler plate code! For those of you who are unfamiliar with T4, it is a code generation engine which is built into Visual Studio, for a primer, I refer you to my codeproject article.

A few days back Daniel Vaughan published a blog post about using T4 templates to generate project meta-data. He demonstrated how this could be used to solve the age-old problem of hard-coded property name strings when implementing INotifyPropertyChanged. His solution uses both T4 and DTE – an automation library which allows you to programmatically access the items within your projects and solutions. Daniel’s blog post inspired me to revisit my DP code generation solution in order to simplify it and remove the need for the XML file.

My first step was to define a custom attribute which can be used to indicate that a class requires the template to generate a DP:

[AttributeUsage(AttributeTargets.Class , AllowMultiple = true)]
public class DependencyPropertyDecl : Attribute
{
    public DependencyPropertyDecl(string name, Type type, object defaultValue)
    {
        this.name = name;
        this.type = type;
        this.defaultValue = defaultValue;
    }
 
    public string name;
    public Type type;
    public object defaultValue;
}

With DTE it should be possible to inspect all the classes in the project, identifying those for which this attribute exists and generate the required DPs. However, looking at the DTE APIs this is not such an easy task, whilst the API defines a ‘base’ interface CodeElement from which CodeClass, CodeNamespace etc… derive, there is no unified concept of ‘Children’, also the Project and Solution are not CodeElements. In other words, in order to find all the classes in a solution you must recurse over a heterogeneous collection of classes and properties.

In order to ease the task of locating the classes within the project that have the DependencyPropertyDecl attribute associated, I created a simple Linq-to-DTE implementation:

public IEnumerable<CodeElement> CodeElementsInProjectItems(ProjectItems projectItems)
{
    foreach (ProjectItem projectItem in projectItems)
    {
        foreach(CodeElement el in CodeElementsInProjectItem(projectItem))
        {
            yield return el;
        }
    }
}
 
public IEnumerable<CodeElement> CodeElementsInProjectItem(ProjectItem projectItem)
{
    FileCodeModel fileCodeModel = projectItem.FileCodeModel;
 
    if (fileCodeModel != null)
    {
        foreach (CodeElement codeElement in fileCodeModel.CodeElements)
        {
            //WalkElements(codeElement, null);
            foreach(CodeElement el in CodeElementDescendantsAndSelf(codeElement))
            {
                yield return el;
            }
        }
    }
 
    if (projectItem.ProjectItems != null)
    {
        foreach (ProjectItem childItem in projectItem.ProjectItems)
        {
            foreach (CodeElement el in CodeElementsInProjectItem(childItem))
            {
                yield return el;
            }
        }
    }        
}
 
 
public IEnumerable<CodeElement> CodeElementsDescendants(CodeElements codeElements)
{
    foreach(CodeElement element in codeElements)
    {
        foreach (CodeElement descendant in CodeElementDescendantsAndSelf(element))
        {
            yield return descendant;                
        }
    }
}
 
public IEnumerable<CodeElement> CodeElementDescendantsAndSelf(CodeElement codeElement)
{
    yield return codeElement;
 
    CodeElements codeElements;
 
    switch(codeElement.Kind)
    {        
 
        /* namespaces */
        case vsCMElement.vsCMElementNamespace:
        {
            CodeNamespace codeNamespace = (CodeNamespace)codeElement;                                        
            codeElements = codeNamespace.Members;
            foreach(CodeElement descendant in CodeElementsDescendants(codeElements))
            {
                yield return descendant;                
            }
            break;
        }
 
        /* Process classes */
        case vsCMElement.vsCMElementClass:
        {            
            CodeClass codeClass = (CodeClass)codeElement;            
            codeElements = codeClass.Members;
            foreach(CodeElement descendant in CodeElementsDescendants(codeElements))
            {                
                yield return descendant;                
            }            
            break;    
        }        
    }    
}

There might be simpler way of achieving this, it is the first time I have created a recursive IEnumerable implementation, so please let me know if I have done something stupid! For a much more elegant solution to the problem of creating an IEnumerable implementation on a tree like structure via recursion have a look at this blog post by David Jade. However, as I mentioned earlier, the heterogeneous nature of the DTE API onto the project structure prohibits this method.

Anyhow, with this nasty IEnumerable implementation out of the way, we can use the power of Linq to provide a concise implementation of ‘Find all the classes in the solution which have one or more DependencyPropertyDecl attributes’, and perform the required code-gen as follows:

// for details of how the DTE 'project' is located see Daniel's blog post or the attached source of this blog.
var elements = CodeElementsInProjectItems(project.ProjectItems);
var classes = elements.Where(el => el.Kind == vsCMElement.vsCMElementClass)
                               .Cast<CodeClass>()
                               .Where(cl => Attributes(cl).Any(at => at.Name=="DependencyPropertyDecl"));
 
foreach(var clazz in classes)
{
    GenerateClass(clazz);
}

The GenerateClass function is a simple modification of the one I wrote for generating classes based on XML descriptions. In this implementation we acquire the CodeAttribute.Value property for each of our custom attribute instances and from there obtain the DP name, type and default value:

<#+
/// 
/// Generates a class along with its associated DPs
/// 
private void GenerateClass(CodeClass clazz)
{
    string classNamespace = clazz.Namespace.Name;
    string className =  clazz.Name;

    bool classRaisesPropertyChanged = false;

#>

namespace <#= classNamespace #>
{
    public partial class <#= className #> <#+ if(classRaisesPropertyChanged){ #>: INotifyPropertyChanged<#+ } #>
    {
<#+
    var attributes = Attributes(clazz).Where(att => att.Name=="DependencyPropertyDecl");
    foreach(CodeAttribute attribute in attributes)
    {
        string[] attributeValues = attribute.Value.Split(',');

        string propertyName = attributeValues[0].Trim().Replace("\"","");
        string propertyType = attributeValues[1].Trim().Substring(7, attributeValues[1].Length - 9);
        string summary = null;
        string metadata = null;
        string defaultValue = attributeValues[2].Trim();
        string typeConverter = null;
        bool propertyChangedCallback = true;
        bool isAttached = false;
        #>

        #region <#= propertyName #>
        <#+        

        GenerateCLRAccessor(typeConverter, propertyType, propertyName, summary);

        bool handleDPPropertyChanged = propertyChangedCallback || classRaisesPropertyChanged;

        GenerateDependencyProperty(className, propertyType, defaultValue, propertyName,
                                   handleDPPropertyChanged, isAttached, metadata, summary);        

        if (handleDPPropertyChanged)
        {
            GenerateChangeEventHandler(className, propertyName, propertyChangedCallback, classRaisesPropertyChanged);
        } 

        if (isAttached)
        {
            GenerateAttachedPropertyAccessor(propertyName, propertyType);
        }
        #>
        #endregion
    <#+
    } // end foreach dps

    if (classRaisesPropertyChanged)
    {
        GenerateINotifyPropertChangedImpl();
    }
    #>
    }
}

<#+
}
#>

(please forgive the lack of syntax highlighting wordpress/codeproject do not support T4 templates!)

The GenerateCLRAccessor, GenerateDependencyProperty, … functions are re-used from my previous XML-based approach to DP generation.

For a simple demonstration of this approach, here is the complete implementation for a simple range control which has a Maximum and Minimum property, where if the user enters a Minimum which is greater that the Maximum, the values are swapped:

<UserControl x:Class="WpfDeclarativeDPCodeGen.RangeControl"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    DataContext="{Binding RelativeSource={RelativeSource Self}}">
    <StackPanel x:Name="LayoutRoot" Background="White" Orientation="Horizontal">
        <TextBox  Width="50" Text="{Binding Minimum}"/>
        <TextBlock Text=" : " VerticalAlignment="Center"/>
        <TextBox  Width="50" Text="{Binding Maximum}"/>
    </StackPanel>
</UserControl>
[DependencyPropertyDecl("Maximum", typeof(double), 0.0)]
[DependencyPropertyDecl("Minimum", typeof(double), 0.0)]
[TypeConverter(typeof(string))]
public partial class RangeControl : UserControl
{
    public RangeControl()
    {
        InitializeComponent();
    }
 
    /// <summary>
    /// If max is less than min, swap their values
    /// </summary>
    private void Swap()
    {
        if (Maximum < Minimum)
        {
            double swap = Minimum;
            Minimum = Maximum;
            Maximum = swap;
        }
    }
 
    partial void OnMaximumPropertyChanged(DependencyPropertyChangedEventArgs e)
    {
        Swap();
    }
 
    partial void OnMinimumPropertyChanged(DependencyPropertyChangedEventArgs e)
    {
        Swap();
    }
}

When using this approach the workflow is quite simple, create your class, add the DependencyPropertyDecl attributes, then execute the code generation template. The output is an accompanying partial class which contains the DP code itself, plus partial methods which can be implemented in order to perform logic when the property changes.

I have not-yet completely ported my XML-based DP generation templates to this new declarative approach, for example the DependencyPropertyDecl attribute needs to also indicate property meta-data, whether a property is attached etc … However, I thought I would share my ideas in case I didn’t get the time (or urge) to make a complete implementation.

I feel that this approach has great potential far beyond DP code generation, simple examples could include attributes that indicate that a T4 template should generate a ‘generic’ INotifyPropertyChanged or IEditableObject implementation, and of course, the more complex application-specific possibilities are endless.

You can download an example project containing the RangeControl above: WpfDeclarativeDpCodeGen.zip

Try adding new DependencyPropertyDecl attributes, and re-run the code generation template (right click CodeGen/GeneratedObjects.tt -> Run Custom Tool), and inspect the generated output – CodeGen/GeneratedObjects.cs.

Finally, thanks to Daniel Vaughan for the inspiration.

Regards,
Colin E.