LA.NET [EN]

Jan 15

Object trees in Silverlight

Posted in Silverlight      Comments Off on Object trees in Silverlight

Whenever you use XAML to build an interface, you’ll end up generating Silverlight objects (obtained from the XAML parsing at runtime). In practice, you’ll end up with an object tree (this is really obvious when you look at the XAML – and yes, I’m assuming you’re using indentation in you XAML files) based on the relationships established between those objects (after all, objects have properties which can hold other objects or collection of objects). In Silverlight, most of this object tree structure is treated as an implementation detail, though you can still interact with it from code (ex.: many elements inherit from FrameworkElement which exposes the Parent property that lets you navigate from the “current” element to its parent).

WPF has two interesting concepts: logical and visual trees. Both views apply “filters” to the object tree and will only return specific elements (which depend on the filter). The best way to understand these concepts is to look at a simple example. Take a look at the following XAML:

<UserControl x:Class="Tests.MainPage" xmlns=
"http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> <StackPanel> <TextBlock>This is the title</TextBlock> <ListBox> <ListBoxItem Content="Item 1" /> <ListBoxItem Content="Item 2" /> </ListBox> <Button Content="Click me!" /> </StackPanel> </UserControl>

The logical tree associated with the previous snippet would look like this:

* UserControl

    * StackPanel

         * TextBlock

            * This is the title

         * ListBox

            * ListBoxItem

                 * Item 1

As you can see, the logical tree will have all the objects (and their properties/events) which have been set up in the code (XAML or procedural code). In WPF, you can effectively access this logical tree and navigate through it (though the LogicalTreeHelper class). In Silverlight, that is not possible (since there is no LogicalTreeHelper class). You might be wondering why it’s important to care about logical trees since it’s only used internally by Siverlight. It happens that several important behaviors available are tied to this logical tree. For instance, property values are (sometimes) propagated from parent to children (more about this in future posts) and this only happens because Silverlight is able to build a logical tree. Unfortunately, the logical tree used by Silverlight only supports a subset of the WPF available behaviors (but that should be enough for most scenarios).

The visual tree is another tree which is built from the original object tree. In practice,the visual tree breaks down the objects maintained in the object tree into their core *visual* components. For instance,here’s the visual tree for the previous XAML:

*START* 0—Tests.MainPage

*START* 1—System.Windows.Controls.StackPanel

*START* 2—System.Windows.Controls.TextBlock

*END* System.Windows.Controls.TextBlock

*START* 2—System.Windows.Controls.ListBox

*START* 3—System.Windows.Controls.Grid

*START* 4—System.Windows.Controls.Border

*START* 5—System.Windows.Controls.ScrollViewer

*START* 6—System.Windows.Controls.Border

*START* 7—System.Windows.Controls.Grid

*START* 8—System.Windows.Controls.ScrollContentPresenter

*START* 9—System.Windows.Controls.ItemsPresenter

*START* 10—System.Windows.Controls.VirtualizingStackPanel

*START* 11—System.Windows.Controls.ListBoxItem

*START* 12—System.Windows.Controls.Grid

*START* 13—System.Windows.Shapes.Rectangle

*END* System.Windows.Shapes.Rectangle

*START* 13—System.Windows.Shapes.Rectangle

*END* System.Windows.Shapes.Rectangle

*START* 13—System.Windows.Controls.ContentPresenter

*START* 14—System.Windows.Controls.Grid

*START* 15—System.Windows.Controls.TextBlock

*END* System.Windows.Controls.TextBlock

*END* System.Windows.Controls.Grid

*END* System.Windows.Controls.ContentPresenter

*START* 13—System.Windows.Shapes.Rectangle

*END* System.Windows.Shapes.Rectangle

*END* System.Windows.Controls.Grid

*END* System.Windows.Controls.ListBoxItem

*START* 11—System.Windows.Controls.ListBoxItem

*START* 12—System.Windows.Controls.Grid

*START* 13—System.Windows.Shapes.Rectangle

*END* System.Windows.Shapes.Rectangle

*START* 13—System.Windows.Shapes.Rectangle

*END* System.Windows.Shapes.Rectangle

*START* 13—System.Windows.Controls.ContentPresenter

*START* 14—System.Windows.Controls.Grid

*START* 15—System.Windows.Controls.TextBlock

*END* System.Windows.Controls.TextBlock

*END* System.Windows.Controls.Grid

*END* System.Windows.Controls.ContentPresenter

*START* 13—System.Windows.Shapes.Rectangle

*END* System.Windows.Shapes.Rectangle

*END* System.Windows.Controls.Grid

*END* System.Windows.Controls.ListBoxItem

*END* System.Windows.Controls.VirtualizingStackPanel

*END* System.Windows.Controls.ItemsPresenter

*END* System.Windows.Controls.ScrollContentPresenter

*START* 8—System.Windows.Shapes.Rectangle

*END* System.Windows.Shapes.Rectangle

*START* 8—System.Windows.Controls.Primitives.ScrollBar Minimum:0 Maximum:0 Value:0

*END* System.Windows.Controls.Primitives.ScrollBar Minimum:0 Maximum:0 Value:0

*START* 8—System.Windows.Controls.Primitives.ScrollBar Minimum:0 Maximum:0 Value:0

*END* System.Windows.Controls.Primitives.ScrollBar Minimum:0 Maximum:0 Value:0

*END* System.Windows.Controls.Grid

*END* System.Windows.Controls.Border

*END* System.Windows.Controls.ScrollViewer

*END* System.Windows.Controls.Border

*START* 4—System.Windows.Controls.Border

*START* 5—System.Windows.Controls.Grid

*START* 6—System.Windows.Shapes.Path

*END* System.Windows.Shapes.Path

*START* 6—System.Windows.Shapes.Path

*END* System.Windows.Shapes.Path

*END* System.Windows.Controls.Grid

*END* System.Windows.Controls.Border

*END* System.Windows.Controls.Grid

*END* System.Windows.Controls.ListBox

*START* 2—System.Windows.Controls.Button

*START* 3—System.Windows.Controls.Grid

*START* 4—System.Windows.Controls.Border

*START* 5—System.Windows.Controls.Grid

*START* 6—System.Windows.Controls.Border

*END* System.Windows.Controls.Border

*START* 6—System.Windows.Shapes.Rectangle

*END* System.Windows.Shapes.Rectangle

*END* System.Windows.Controls.Grid

*END* System.Windows.Controls.Border

*START* 4—System.Windows.Controls.ContentPresenter

*START* 5—System.Windows.Controls.Grid

*START* 6—System.Windows.Controls.TextBlock

*END* System.Windows.Controls.TextBlock

*END* System.Windows.Controls.Grid

*END* System.Windows.Controls.ContentPresenter

*START* 4—System.Windows.Shapes.Rectangle

*END* System.Windows.Shapes.Rectangle

*START* 4—System.Windows.Shapes.Rectangle

*END* System.Windows.Shapes.Rectangle

*END* System.Windows.Controls.Grid

*END* System.Windows.Controls.Button

*END* System.Windows.Controls.StackPanel

*END* Tests.MainPage

As you can see, several of the controls used in the previous XAML end up using other elements for generating its appearance (in future posts, we’ll see how and why this works like this and how we can customize the looks of a control). Btw, here’s the code I’ve used for getting the visual tree:

public partial class MainPage : UserControl {
 public MainPage() {
   InitializeComponent();
   bt.Click += (sender, e) => { PrintVisualTree(this, 0); };
 }
 void PrintVisualTree(DependencyObject obj, Int32 depth) {
   Debug.WriteLine(
String.Format("*START* {0}---{1}",depth, obj) ); var totalCount = VisualTreeHelper.GetChildrenCount(obj); for (var i = 0; i < totalCount; i++) { PrintVisualTree(
VisualTreeHelper.GetChild(obj, i), depth+1); } Debug.WriteLine(String.Format("*END* {0}", obj)); } }

So, now you know that the object tree we have can be “filtered” into two important trees: logical and visual trees. On the one hand, logical trees end up supporting several of the behaviors we’ve got in Silverlight. On the other hand, visual trees are used for rendering the controls at runtime. And that’s it for now. Stay tuned for more in Silverlight.