Wednesday, July 30, 2008

Windows Presentation Foundation, Logical and Visual Trees

In this post, we look at the element trees that are generated at runtime in a WPF application known as the Logical Trees and Visual Trees. For a detailed and accurate content, refer to this MSDN Link.

WPF uses several tree structure metaphors to define relationship between program elements. These structures help developers directly manipulate the elements after the tree is rendered. The documentation talks about trees as

   1: The primary tree structure in WPF is the element tree. 

   2: If you create an application page in XAML, then the tree structure is created based on the nesting relationships of the elements in the markup. 

   3: If you create an application in code, then the tree structure is created based on how you assign property values for properties that implement the content model for a given element. 

   4: In Windows Presentation Foundation (WPF), there are really two ways that the element tree is processed and conceptualized: as the logical tree and as the visual tree.

The logical tree exists so that the content models can readily iterate over their possible child elements which makes the content models extensible. Anyway, the logical tree can be viewed as tree representation of the elements that are created just when the application starts. It is like a Tree-ized version of the XAML document(if there is one that is used). Resources are resolved using logical trees by first looking for a resource specified in the requesting element and then the parent elements.

The Visual Tree describes the structure of the visuals represented by the Visual class. A template for a control defines the visual for that control and this is included in the Visual Tree. It also includes the object that are added at runtime. The visual tree gives control over low-level drawing in case it is required for optimization purposes.

In the subsequent sections, we look at how both Logical Tree and Visual Tree are binded to TreeView control in the WPF. So to get started, first lets look at the XAML for the Tree control we are using for the Visual Tree.

   1: <TreeView Name="VisualTree" Grid.Column="1" Grid.Row="0" Background="AliceBlue">

   2:                 <TreeView.ItemTemplate>

   3:                     <HierarchicalDataTemplate ItemsSource="{Binding Children}">

   4:                         <ContentPresenter Content="{Binding Name}"/>

   5:                     </HierarchicalDataTemplate>

   6:                 </TreeView.ItemTemplate>

   7: </TreeView>

TreeView has an ItemTemplate property which can be customized to define the appearance of the elements in a tree. We must add a DataTemplate to this ItemTemplate property. We chose the HierarchicalDataTemplate in this case where define how each element in the hierarchy should be displayed. More about hierarchical data templates would be presented in the later posts. This also covers the data binding expressions in WPF. In line 3, when we said ItemsSource = {Binding Children}, the framework looks for Children (which is a collection) in the DataContext of the TreeView or its parent. So if treeview's Datacontext property is set to "X", then ItemsSource would be X.Children. For each child in the Children, it is rendered using ContentPresenter where the content is taken from the child's Name property.

So this treeview code should be something like this :

   1: var some_tree; //some_Tree has children property

   2: visualTree.DataContext = some_tree;

The actual code we used in the BuddiPad is shown below.

   1: private void DumpVisualTree ( DependencyObject p )

   2: {

   3:             VisualTree.ItemsSource = new VisualTreeItem ( p ).Children;

   4: }

Notice that we used ItemsSource property instead of DataContext property. So what is the difference between these two? The details would be covered in the Data Binding segment of the talk. Note that VisualTree.DataContext would not work in this case.

The code for the VisualTreeItem class is shown below. It is very simple, recursive and straightforward.

   1: /// <summary>

   2:     /// When we build a Visual Tree to be displayed in a TreeView, this class forms the basis of the TreeViewItems.

   3:     /// </summary>

   4:     public class VisualTreeItem

   5:     {

   6:         /// <summary>

   7:         /// The _element is the Dependency Object whose treeview data item is this instance.

   8:         /// </summary>

   9:         private DependencyObject _element;


  11:         /// <summary>

  12:         /// This is the list of children that the _element has.

  13:         /// </summary>

  14:         private List<VisualTreeItem> _children;




  18:         /// <summary>

  19:         /// Initializes a new instance of the <see cref="VisualTreeItem"/> class.

  20:         /// </summary>

  21:         /// <param name="dop">The Dependency Object/UI element</param>

  22:         public VisualTreeItem ( DependencyObject dop )

  23:         {

  24:             _element = dop;

  25:         }


  27:         /// <summary>

  28:         /// Gets the children.

  29:         /// </summary>

  30:         /// <value>The children.</value>

  31:         public List<VisualTreeItem> Children

  32:         {

  33:             get

  34:             {

  35:                 if (_children == null)

  36:                 {

  37:                     //initialize the list with capacity expected as the number of children

  38:                     _children = new List<VisualTreeItem> ( VisualTreeHelper.GetChildrenCount ( _element ) );

  39:                     for (int i = 0; i < VisualTreeHelper.GetChildrenCount ( _element ); i++)

  40:                     { //for each children in the VisualTree for that dep object where each child is a Dep Object.

  41:                         _children.Add ( new VisualTreeItem ( VisualTreeHelper.GetChild ( _element, i ) ) );

  42:                     }

  43:                 }

  44:                 return _children;

  45:             }

  46:         }


  48:         public string Name

  49:         {

  50:             get

  51:             {

  52:                 FrameworkElement fe = _element as FrameworkElement;

  53:                 if (fe != null && !String.IsNullOrEmpty ( fe.Name ))

  54:                 {

  55:                     return Type + ":" + fe.Name;

  56:                 }

  57:                 else

  58:                 {

  59:                     return Type;

  60:                 }

  61:             }

  62:         }


  64:         public string Type

  65:         {

  66:             get

  67:             {

  68:                 return _element.GetType ( ).Name;

  69:             }

  70:         }



  73:     }

The code is heavily adapted from Kevin Moore Bag-o-Tricks but its the same tree code that I have written numerous times for different projects. The heart of this implementation is usage of VisualTreeHelper class. The recursive part of it is in the Children property. The Logical Tree could be dumped in a similar fashion but using LogicalTreeHelper class.

So far, most of the work involved in getting the BuddiPad running has been covered. Now in the next part, we look at other WPF topics that are scheduled for the talk. In the next post, we compare WinForms with WPF in detail.

No comments: