Silverlight Object Trees
This topic contains the following sections.
- Object Trees
- Object Trees and XAML Markup
- Referencing Object Properties
- Resources and Templates in the Object Tree
- Traversing the Object Tree
- Using VisualTreeHelper
- Traversing into Template Content
- Silverlight Objects and HTML DOM
- Related Topics
An object tree is a conceptualization of how objects that are created and exist in the Silverlight content at run-time are related to each other. The relationships are based on the principle that objects have properties, and many times the value of the property is another object, which in turn also has properties. The object tree has branches because some of these properties are collection properties and hold more than one object. The object tree has a root because the architecture ultimately must reference a single object that is the connection point to concepts that are outside the object tree (such as the browser host, or the Silverlight plug-in that displays the content).
Although there really is a single object tree in concept, the Silverlight API does not expose the full tree to user code. Large parts of the object tree structure are composed of implementation details. Instead, you have object-specific properties that influence the child values of particular points in the tree, and that can report on the parent. In most cases the parent axis is read-only, because you typically build trees from the root up, either in code or through the process of XAML parsing. For example, a Panel has its Children property, which sets the child objects. FrameworkElement has Parent for reporting the parent. Both of these APIs are on base classes, so they are available on a large number of Silverlight objects.
A related tree concept in Silverlight is the visual tree. The visual tree can be conceptualized as being an edited or filtered representation of the larger object tree. The filter applied is that only the objects that have a rendering implication are present in the visual tree. For example, a collection class would not be part of the visual tree, and instead the visual tree abstracts any collection into a "children" concept. However, the visual tree can also include objects that are not immediately apparent if you consider the loaded source XAML markup to be an approximation of the object tree. This is because the visual tree also reports objects that are the composited parts of controls. Objects in the visual tree can come from an applied control template or from resource dictionaries. The visual tree is used internally for the Silverlight rendering process, but knowing something about the visual tree is often important for certain scenarios, such as writing or replacing a control template, or analysis of a control's structure and parts at run-time. For these scenarios, Silverlight provides the VisualTreeHelper API that can examine the visual tree in a more generalized way than you could practically achieve with the object-specific parent and children properties.
The visual tree concept also exists in WPF, and the Silverlight concept of the visual tree is similar. A notable difference however is that WPF also provides an additional filter or conception of the object tree, known as the logical tree. The logical tree concept is relevant for certain property system behaviors. Silverlight does not expose a logical tree concept through a helper class. Some (but not all) of the relevant property behaviors do exist in Silverlight, but without a helper API to access it, the logical tree concept is not useful in Silverlight and thus is not discussed in the documentation. One minor compatibility implication of the logical tree omission is that the FrameworkElement.Parent property behavior is different in Silverlight 5 and is actually reporting the visual tree parent.
Object Trees and XAML Markup
If you were to compare the object tree that is accessed through the Silverlight API and the tree shape of the XAML markup, they would not match exactly, node-for-node. This is because XAML is designed for markup, and ease of use during markup definition. For example, XAML has the concept of a property element, which provides the guidance for which property is being set if one element is found nested within another. In the object tree, this would just look like a property on an object being set by another object. Conversely, XAML also has a concept of a content property, where the property being set is not explicitly designated in markup. XAML has syntaxes and techniques that can instantiate objects based on string values of attributes. XAML can also follow references to objects that already exist but are defined elsewhere in the XAML markup, or are entirely external to the markup. Regardless of these minor inconsistencies, when you define a UI in XAML, you are defining the approximate structure of your eventual Silverlight object tree at run time.
For more information on the specific terminology and the rules for XAML, see XAML Overview.
Referencing Object Properties
Regardless of how you obtain an object reference from the Silverlight object tree, properties in the managed API are directly exposed through an object.property notation, related to the core concept of CLR properties in .NET. Underlying many of the Silverlight properties is the concept of a dependency property. Dependency properties and the property system introduce some additional syntax possibilities for accessing properties other than by object.property, but other than for attached properties these syntax forms are less commonly used and are not discussed in this topic. For more information, see Dependency Properties Overview.
Attached Properties in the Object Tree
Silverlight supports the concept of an attached property. From the perspective of the object tree, an attached property is a property and value that can be attached to any object in the tree, regardless of what type the object is. (Note that in all Silverlight core attached property implementations, that object must at least be a dependency object). Attached property values exist in the object tree, but if you access them using code, you must use a different syntax than pure object.property notation. For more information, see Attached Properties Overview.
Resources and Templates in the Object Tree
Silverlight supports a resource concept known as a resource dictionary. A resource dictionary is used to specify property values that require substantial sub-property settings themselves.
The most common scenario for the ResourceDictionary is to define ResourceDictionary elements in XAML, and then to use the defined resources as property values through XAML attributes, and the StaticResource markup extension. For some cases, the resource can be shared. For example, you can define a LinearGradientBrush resource that contains multiple gradient stops in a ResourceDictionary, which you then apply to multiple Brush in your visual design.
Templates operate by a somewhat different concept (whether defined in page-level or application-level resource dictionaries, in generic.xaml, or inline). The template itself is an object, but the template is often applied to the visual tree multiple times. Once applied, the elements in the template often use a TemplateBinding, which makes it possible to apply the template and still set specific values for each templated object. The concepts behind templates are discussed in the topic Customizing the Appearance of an Existing Control by Using a ControlTemplate.
Traversing the Object Tree
Traversing the object tree (sometimes known colloquially as "walking the tree") is a common technique in object models. Traversing the tree means that you use properties that either reference child objects (typically these are collections) or parent relationships to a containing object (usually this is done from within a collection, and returns the collection itself). As a rough description of the process, you call a succession of child properties and parent properties, or perhaps helper methods, to navigate the axes of the object tree until you retrieve a value that contains the object that you were looking for.
As a general rule, you should be able to construct your content in XAML for Silverlight such that you do not need to query the structure of the tree extensively. To avoid the need to traverse the tree, you should give XAML elements a value for the x:Name / Name attribute in the XAML markup that creates them. This creates an immediate reference that is available for run-time code access, which is a much less error-prone technique for getting object references than is traversing the tree.
Alternatively, if you are creating objects through code constructors rather than by XAML loading, you should declare private fields or variables that retain an object reference at run-time. There should be no need to "walk the tree" to find objects that are created in code.
However, there are cases where it is not possible or practical to give an object a name and keep an object reference in scope. One such scenario is if you are adding dynamic content that is supplied by the user or supplied by data binding. In these cases you cannot predict the number of items added or the structure of the run time object tree. Another scenario is examining an applied template for a control, or a composited section of a control.
Silverlight in general supports the concept of skinning, or otherwise re-styling or re-templating a control. Particularly if you are a control author and are writing the support code for your control, it can be dangerous to assume a particular tree structure. Because most controls support a settable template (regardless of whether you have enabled more specific extension points such as sub-part styles) the run-time visual tree can be different than the tree as created by the applied default template. For more information, see Customizing the Appearance of an Existing Control by Using a ControlTemplate.
Try-catch Logic for Traversing "Children" and Other Collections
If your requirement for traversing the object tree involves finding objects that represent collections that would not be represented as part of the visual tree, you might need to write specialized functions that attempt to find APIs that match particular naming patterns or the object model of a particular class.
Walking the object tree downwards (away from the root) multiple levels is generally possible as long as you know the points at which the contained objects will have collections. You might have to use try/catch techniques or the equivalent to detect this, by checking whether Children exists and Count is nonzero (Children and Count here are placeholders and not literal APIs; Children and Count happen to be common names for these types of properties per the .NET naming guidelines, but the actual properties might have different names depending on the object and its object model). Some collections in the overall Silverlight object model are contained in properties that are not named Children. If you know you are walking into a particular collection property that is not named Children, you should account for this in your logic.
VisualTreeHelper is a utility class that can be useful for traversing the object tree. (The concept of the visual tree was explained earlier in the "Object Trees" section of this topic.)
Because you can operate on the visual tree at run time and traverse into the template parts, this can be a useful technique for examining template composition. Alternatively, you can examine a child collection that might be populated by data binding or where the full nature of the runtime object tree might not be fully known by your application code. You would do this by walking the tree through GetChild, using GetChildrenCount as the determinant of whether the tree node is a single item or a "Children" collection that you should iterate by the count.
Traversing into Template Content
Another technique besides VisualTreeHelper that is useful for traversing into template content is GetTemplateChild. Traversing into template content is often necessary because the behavior of FindName is governed by the concept of a namescope. In this case, template content has a deliberately separate XAML namescope from the rest of the object tree. Templates are shareable, and would otherwise cause name collisions in the object tree when the template is applied more than once. GetTemplateChild finds objects by searching the template-based XAML namescope x:Name values. However, the entry point of the call is from the larger object tree scope of the particular Control where the template is applied.
Note that GetTemplateChild is a protected API. This means that you can only practically use GetTemplateChild if you are the control author and are writing the run-time logic, or if you are overriding an existing control and want to replace or append to any existing control logic from the base class. If you are a control consumer, your best option is often to use VisualTreeHelper API. Alternatively, controls sometimes deliberately expose certain parts as properties to make them easier to customize. For example, DataGrid and its support types expose several properties of type Style or FrameworkTemplate, which provide a way to substitute just a part of the template rather than the entire template.
For more information on the XAML namescope concept, see XAML Namescopes.
Silverlight Objects and HTML DOM
There is also another object model available for scripting to HTML content: the HTML document object model (DOM). However, the DOM does not identify the content that is loaded by a Silverlight plug-in as being a full part of the DOM. For more information about the DOM and the distinction between the DOM and Silverlight object trees, see Silverlight Programming Models, XAML, and the HTML DOM.