Logical and Visual Trees

The existence of templates leads to an API design dilemma that the WPF architects had to resolve. If a developer wishes to access the elements in the UI, should she see the fully expanded tree, containing all the instantiated templates? Although this would put the developer in full control, it might be rather cumbersome; often a developer only really cares that there is a Button present, not about the structure of its appearance. On the other hand, to present the simple pre-expansion view would be unnecessarily limiting.

To solve this problem, WPF lets you work with either the logical tree or the visual tree. The visual tree contains most* of the elements originally specified (either in markup or in code) plus all the extra elements added as a result of template instantiation. The logical tree is a subset of the visual tree that omits the elements added as a result of control template instantiation. WPF provides two helper classes for working with these two trees: VisualTreeHelper and LogicalTreeHelper.

For example, consider the following snippet of XAML:

<WrapPanel Name="rootPanel"> <Button>_Click me</Button> </WrapPanel>

Walking this logical tree at runtime using the LogicalTreeHelper looks like Example 9-12.

Example 9-12. Dumping the logical tree public Windowl() { InitializeComponent();

// Can dump the logical tree anytime after InitComp DumpLogicalTree(rootPanel, 0);

void DumpLogicalTree(object parent, int level) { string typeName = parent.GetType().Name; string name = null;

DependencyObject doParent = parent as DependencyObject;

* As one example, no FrameworkContentElement objects, as described in Chapter 14, will show up in the visual tree even though they're in the logical tree.

Example 9-12. Dumping the logical tree (continued)

// Not everything in the logical tree is a dependency object if( doParent != null ) { name = (string)(doParent.GetValue(FrameworkElement.NameProperty) ?? "");

name = parent.ToString();

Debug.Write(" ".Substring(0, level * 2));

Debug.WriteLine(string.Format("{0}: {1}", typeName, name)); if( doParent == null ) { return; }

foreach( object child in LogicalTreeHelper.GetChildren(doParent) ) {

DumpLogicalTree(child, level + 1);

Notice that we're watching for objects that aren't instances of the DependencyObject class (almost the lowest level in the WPF type hierarchy—only the DispatcherObject is lower). Not everything in the logical tree is part of the WPF type hierarchy (e.g., the string we pass in as the text content of the Button is going to stay a string when we examine it):

WrapPanel: rootPanel Button: String: _Click me

The code to walk the instantiated objects with the VisualTreeHelper is simpler because everything it encounters is at least a DependencyObject (see Example 9-13).

Example 9-13. Dumping the visual tree protected override void OnContentRendered(EventArgs e) { base.OnContentRendered(e);

// Need to wait for layout before visual tree is ready Debug.WriteLine("Visual tree:"); DumpVisualTree(rootPanel, 0);

void DumpVisualTree(DependencyObject parent, int level) { string typeName = parent.GetType().Name;

string name = (string)(parent.GetValue(FrameworkElement.NameProperty) ?? ""); Debug.Write(" ".Substring(0, level * 2));

Debug.WriteLine(string.Format("{0}: {1}", typeName, name));

for( int i = 0; i != VisualTreeHelper.GetChildrenCount(parent); ++i ) { DependencyObject child = VisualTreeHelper.GetChild(parent, i);

DumpVisualTree(child, level + 1);

In Example 9-13, you'll notice that we're careful to only walk the visual tree in the OnContentRendered event, which guarantees that at least a portion of the visual tree has been rendered. This is important, because the visual tree isn't expanded until it needs to be. However, once it's instantiated, the visual tree is considerably more verbose than the logical tree:

WrapPanel: rootPanel Button: ButtonChrome: Chrome ContentPresenter: AccessText: TextBlock:

The difference, of course, is that the button control template was instantiated. If you'd like to explore the visual tree produced by a bit of XAML interactively, I suggest the XamlPad tool that comes with the Windows Platform SDK. XamlPad lets you type in XAML and shows you the results as soon as you've entered valid XAML. It also has a button that will show you the visual tree of the XAML you've typed in. Figure 9-13 shows the visual tree for our sample XAML in a slightly nicer way.

Figure 9-13. Showing the visual tree inside XamlPad

Was this article helpful?

0 0
Project Management Made Easy

Project Management Made Easy

What you need to know about… Project Management Made Easy! Project management consists of more than just a large building project and can encompass small projects as well. No matter what the size of your project, you need to have some sort of project management. How you manage your project has everything to do with its outcome.

Get My Free Ebook


Post a comment