Traversing the Visual tree in Windows Phone 8.1 using VisualTreeHelper

 

What’s been going on?

So a few weeks back I was trying to get a windows phone application working that I have now called “Shopping Nanny”. This application is now completed and has been submitted to the store. Once it gets approved, I will make an announcement on here, but in the meanwhile, I thought I would write a few blogs about some headaches I encountered in the hope I can assist others with a few less head banging episodes.

XAML and DataTemplate

One of the most important and elegant things that XAML has to offer is the DataTemplate. It’s great in it’s simplicity to combine various controls into a “template” that is then used in a parent control. Here is a simple example, and the example I will work off of the rest of this post. It uses the ListView control combined with the DataTemplate that holds several child controls to create the look and feel of the ListView.

 

<ListView x:Name="ShoppingListView" Tag="{Binding Id}" ItemsSource="{Binding ShoppingItems}" IsItemClickEnabled="False" IsTapEnabled="True" IsDoubleTapEnabled="True" ContinuumNavigationTransitionInfo.ExitElementContainer="True" FontSize="12" Foreground="#FFF0CCA7" FontStyle="Italic" Loaded="ShoppingListView_Loaded" SelectionMode="Single" SelectionChanged="ShoppingListView_SelectionChanged" > <ListView.ItemTemplate> <DataTemplate> <Grid> <TextBlock x:Name="ShoppingItem" Text="{Binding ItemName}" Foreground="{Binding Foreground}" Width="{Binding ActualWidth, ElementName=ItemName}" Height="{Binding ActualHeight, ElementName=ItemName}" Style="{StaticResource ListViewItemContentTextBlockStyle}" Pivot.SlideInAnimationGroup="1" CommonNavigationTransitionInfo.IsStaggerElement="True" FontSize="25" FontStyle="{Binding FontStyle}" /> </Grid> </DataTemplate> </ListView.ItemTemplate> </ListView>

In the above example, the Listview is doing several things, including data binding, which I wrote a post on here. For this post, the part of interest lies in between the <ListView.ItemTemplate> open and close tags. Here we see  there is a Grid container control along with a TextBlock child control. This is a very typical set up when using a DataTemplate to start the look and feel of your ListView. Once You run the app, your listview might look something like this:

wp_ss_20150330_0003

Obviously, this is a screen shot of my app, but the list portion will look almost identical.

Wait… so The DataTemplate is Great right? What’s the problem?

Well the problem is when you are trying to access the items inside a DataTemplate in your backend code. Unfortunately, this is not as easy as saying this.ListViewName.TextBlockName or anything of the like. Although frustrating, it is understandable if you think about it. A listview is exactly as it sounds, a list. inside this list are “items”. These items each have a static name associated with them usually defined in the x:Name property when you want to access them outside of the XAML view. In the above example, when you look at the TextBlock specifically:

<TextBlock x:Name="ShoppingItem" Text="{Binding ItemName}" Foreground="{Binding Foreground}" Width="{Binding ActualWidth, ElementName=ItemName}" Height="{Binding ActualHeight, ElementName=ItemName}" Style="{StaticResource ListViewItemContentTextBlockStyle}" Pivot.SlideInAnimationGroup="1" CommonNavigationTransitionInfo.IsStaggerElement="True" FontSize="25" FontStyle="{Binding FontStyle}" />

 

The name of the TextBlock is “ShoppingItem” rights? Well if there are a list of them, there is nothing to differentiate one ShoppingItem TextBlock from the other because they are not unique. For this reason, you need to use some technique to walk the XAML tree and get the control you want to get data from. Remember that XAML is just another tree structure like XML, HTML JSON and any others!! Any of these can be traversed one way or the other.

VisualTreeHelper to the rescue

So how does one traverse the XAML tree? Well, there are plenty of ways I’m sure you could manually do it, but the most efficient way to accomplish this is using the VisualTreeHelper to find the control for you. There are several helpful ways to use the VisualTreeHelper to get the results you need depending on your needs at the given time. The foundation of what I designed was based heavily on Jerry Nixon’s blog post here. If you don’t currently follow him, I highly recommend him! We will start with a common albeit simplified scenario and work through it to access the control we need with the example above.

Scenario

So you have the same list as defined above, but now you also have a button on the screen that when pressed, you want to get anything in the list that has a particular text value (lets say apple) and turn that item in the list green. In this case, you cannot key off of what is selected in the list view, because it may not be correct and you cannot key off any other ListView event to get this information because it can be changed by the user at any time prior to clicking the button (we are assuming here to add some complexity that the list is editable). So how does one address this when subscribing to the button’s click event?

Take a look at the code below and then I’ll explain it in detail.

/// <summary> /// Retrieves the children of any type by control in the XAML Visual Tree. /// </summary> /// <typeparam name="T"></typeparam> /// <param name="root">The root.</param> /// <returns></returns> public static List<T> FindChildrenByControl<T>(DependencyObject root) where T : FrameworkElement { var list = new List<T>(); for (int i = 0; i < VisualTreeHelper.GetChildrenCount(root); i++) { var child = VisualTreeHelper.GetChild(root, i); if (child is T) { list.Add(child as T); } else { list.AddRange(FindChildrenByControl<T>(child)); } } return list; }

 

The code above first and foremost utilizes generics to identify the control(s) the caller wants to retrieve and uses the FrameworkElement as the dynamic type that any object being searched must inherit from. Often, the Control class is used which most UIElements fall under. The reason I raised the class to be higher in the tree (UIElement –> FrameworkElement->Control), is because a TextBlock is not considered a control or at least does not inherit from the Control class. I do not remember why, but it does not. I’m sure Microsoft has an interesting reason not to have it in there. in any event, this was my reasoning for doing it.

The object that is passed should be the root control you wish to traverse. In this case, we will pass in the Listview, but technically it can be any object that inherits from DependencyObject. DependencyObject is to XAML what the object is in normal .NET. That’s not 100% true, but close enough for this post. For this reason, by using DependencyObject, you can pass any control into the method, including user controls.

The rest of the method loops through each child in the root object (using VisualTreeHelper.GetChildrenCount(root) and checks  if that child is of the dynamic type mentioned in the call. If it is, it adds it to the list to return. If it doesn’t, then it uses recursion to then traverse that object and see if it has any children that are of the type you are looking for. This continues until it reaches the end of the tree (or no more parent objects). Once this happens, the list of controls matching the type provided is returned.

So Lets put this into action

So how do you call this wonderful method? As you will see below, it’s pretty straightforward.

First, we define the object we want back, in this case TextBlock. Then we pass in the root object, in this case the ListView (which in our example, it’s name was ShoppingListView).

var textBlocks =FindChildrenByControl<ListView>(this.mainPivot).FirstOrDefault(x => x.Text.ToString() == "apples");

 

There is only one piece I added which was the LINQ query to get the first textblock that had it’s text a value of “apples” per the scenarios requirements.

Conclusion

In conclusion, DataTemplates are a very powerful and useful tool for the front end development of your application. it has some pitfalls in the backend development, but nothing that can’t be worked around with a little TLC!!

Happy Coding 🙂

Advertisements

About Gregg Coleman

I am Senior-level Software Engineer working primarily these days with .NET. I have a good working knowledge of ASP.NET MVC, Web Forms, WCF web services and Windows Services. I spend much of my time in the Web Services (SOAP and REST) world in my current job designing and implementing various SOA architectures. I have been in the software engineering industry for about 6 years now and will not now nor ever consider myself an "expert" in programming because there is always so much to learn. My favorite thing about designing software is there are always new emerging technologies and something to learn every day! My current job has me spending much of my job on the bleeding edge of technologies and changing gears all the time, so I'm never bored and always challenged. On my spare time I enjoy weight training, reading and venturing to new places near by. Of course programing and learning new technologies are another hobby of mine.
This entry was posted in .NET, .NET 4.5, ASP.NET, C#, Classes, Data Binding, Design Binding, LINQ, MVVM, Programming, Windows Phone, Windows Phone, Windows Store Apps, WPF, XML and tagged , , , , , , , , , , , . Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s