Control Monster Explores Multi-Level XamTrees - Part I

[Infragistics] Curtis Taylor / Wednesday, June 23, 2010

Displaying hierarchical data in software is a strategically useful task which can get complicated fast depending on how deep the hierarchy goes. You may have a database that lists accounts for a set of users where each account contains a list of transactions. You may even have a recursive tree, such as an org chart where each team member contains a list of team members they are responsible for. A large hierarchical organization may have several levels of relationships. Abstracting that to data means each team member item would contain a collection of team member items.
The above example of the XamTree with a custom template is from the Silverlight NetAdvantage Feature Browser.
The Infragistics Silverlight XamTree solves this by providing a flexible model for defining n number of hierarchical data templates for each tier within the hierarchy. The API is easy to use and is basically data driven. I will show you a few technicques for applying data templates to various levels within a hierarchy.
XamTree was formerly named XamWebTree. In our effort to consolidate the names of our WPF and Silverlight products, XamTree became the new name for this control.
I will be creating a test application using the Silverlight control from the Silverlight NetAdvantage Web Client 2010.2. I will also be using Expression Blend version 4 and Silverlight 4. You can use the previous version of all of these tools. Just remember that in the last version the XamTree was named XamWebTree.
To start this sample, go ahead and create a Silverlight application using either Expression Blend or Visual Studio. Add the NetAdvantage default assembly and the XamTree assembly to the project. With NetAdvantage 2010.2, you will be adding the following two assembly DLLs located in the Program Files folder in the Infragistics\NetAdvantage 2010.2\SilverlightCommon\Bin folder:
InfragisticsSL4.v10.2.dll
InfragisticsSL4.Controls.Menus.XamTree.v10.2.dll
Create a Grid with two Columns each with Column Width set to 1*.
If you created the project in Blend, make sure that the root UserControl element does not have hard-coded Width and Height properties set. If so, set them both to Auto.
Add a XamTree to the first column.
And set to stretch vertically and horizontally within the column.
Next we will add sample data (see my last blog for information how to use Blend to create sample data).
I renamed the default collection, Franchises. I renamed the first default string property StoreName. Left the string format as Lorem ipsum with the word count set to 1 and word length as 8.
In the drop down menu next to Franchises I add a Collection property, naming it Branches. Then I add an address formatted string property, a telephone formatted string property and an Image property to the Branches collection. You can provide a source folder for the test images or leave the Image fields blank to allow Blend apply its own test images. The Image property I’ve renamed Icon.
Sample data in place in less than two minutes, to apply it to the XamTree, I right-click on the XamTree in the Objects and Timeline panel and locate the ‘Data bind ItemsSource to Data…’ menu item.
In the Create Data Binding dialog I locate the Franchises collection in the SampleDataSource. Simply selecting this and dismissing the dialog by pressing OK will bind the sample data to the XamTree.
At this point you should see the XamTree display a list of class names. Since the collection is more complex than a list of strings, we will want to utilize Hierarchical Data Templates to present the data.
In the example, I intend to apply different DataTemplates to the Franchises collection and to the Branches child collections. To first apply a template to the parent Franchises collection, locate the breadcrumb control at the top of the design view and navigate to the following command:
Edit Additional Templates -> Edit Generated Items (ItemTemplate) -> Create Empty…
The ItemTemplate is the default DataTemplate applied to generated items in the XamTree. It works identically to the ItemTemplate of any ListBox or other ItemsControl elements.
In the ‘Create DataTemplate Resource’ dialog I’ve named the new DataTemplate ‘FranchiseDataTemplate’.
Blend will switch the designer to a template editor view. In this view I will add a TextBlock to the given Grid. I will then bind the Text property of the TextBlock to the StoreName property in the Franchise collection item. To do this in Blend, click on the Advanced Options popup menu in the Properties panel and choose the Data Binding option.
This will bring up the Create Data Binding dialog. From here you can easily select the StoreName property. This adds the binding expression for you which minimizes the chance of mis-typing a word in the XAML extension brackets (which is the main convention for creating binding expressions in XAML).
We are done with this DataTemplate. You can now easily return the design view scope back to the main UserControl interface by clicking on the Return scope button in the Objects and Timeline panel.
You should now have the Lorem ipsum sample text appear in the XamTree control. Incidentally, Lorem ipsum is a convention in graphic design for using auto-generated placeholder text in text layouts. You can read more about it in Wikipedia:
http://en.wikipedia.org/wiki/Lorem_ipsum
You may have noticed in the Additional Templates drop down there was nothing there for secondary or tertiary levels of the tree. As you can imagine, every element in a collection can have a collection of items, which can have more collections of items. The hiararchical nature of assigning templates add a level of complexity that is solved with the HierarchicalItemTemplate property in the XamTree.
You can locate the HierarchicalItemTemplate in the XamTree section of properties. At this point I click on the New button next to this property to instantiate one of these templates within the XamTree. On doing so, you will notice that our rudimentary DataTemplate is no longer applied. To re-apply this template, we need to tie in the template the the data source to the new HierarchicalDataTemplate which gets assigned to the HierarchicalItemTemplate property.
The new HierarchicalDataTemplate contains an ItemsSource property. Using Advanced Options, create a new data binding to this property. Since we want to communicate the next level of the tree, we will need to bind this property to the Branches property. However, Blend is not aware of the templating nature of this binding. So if you select Branches from the DataContext tab, Blend will create an incorrect expression, “Franchises[0].Branches”.
To correct this, select the ‘Use a custom path expression’ checkbox and strip the first part of the expression until it simply reads as ‘Branches’.
  
Next click on the Advanced Options button for the Template property in the HierarchicalDataTemplate. In the ‘Local Resource’ sub-menu the original DataTemplate will be displayed. Select this resource to re-apply this template to the parent part of the XamTree.
After doing this you won’t see anything work yet as we will need to remove the resource assignment to the ItemTemplate property in the XamTree. To do this, find the ItemTemplate property in the Miscellaneous section and choose Reset in the Advanced Options menu.
On doing so, the XamTree in the designer will now display the Lorem ipsum names and triangle controls for expanding the tree.
To complete this, we still need to add a DataTemplate for each Branch item. We will create a DataTemplate to present the Branch information and assign it to the ItemTemplate property in our HierarchicalDataTemplate.
One way to do this is to duplicate the first DataTemplate and modify the duplicate. To duplicate the existing DataTemplate, switch to the Resources panel, locate the FranchiseDataTemplate in the MainPage.xaml UserControl. Right-click on this resource and choose Copy, then right-click on the UserControl and choose Paste. Next click on the duplicate resource label to select it. Rename it BranchDataTemplate.
To the right of the new resource, click on the rounded rectangle to enter editing mode for this resource.
In the DataTemplate editor, change the Grid layout to display two rows and two columns. Set the first column Width to Auto and the Height for boths rows to Auto. Leave the second column Width as 1*.
Add an Image element to the first column spanning both rows. Center this Image vertically and horizontally and set its Width and Height to 40.
Set the existing TextBlock to be in column 1 and row 0. Add a second TextBlock to column 1 and row 1.
There are a couple of ways to assign Rows and Columns to a Grid. If you have not done this before, simply locate the ColumnDefinitions and RowDefinitions properties in the expanded Layout section of the Properties panel. There is a button that will take you to an editor dialog for these properties.
Since we do not have a current DataContext to work with, this is one of the few times we would be working blind with a template editor. So somethings to look for:
Make sure the Grid and the two TextBlock Layout Width, Height, VerticalAlignment, HorizontalAlignment and Margin properties are all clear of any values. You should Reset these properties if Blend added any setting to these. When working with items in the designer, Blend will change these settings. Also make sure that the Row, Column, RowSpan, and ColumnSpan are set up to support the Image taking up the two rows in the first column and the two TextBlocks taking the first and second rows in the second column.
To make sure the layout is correct, you can assign temporary text and an image Source to the Image. This will help you make sure your layout is good.
Bind the first TextBlock Text property to the custom path expression “Address” (assuming that is the name you used for your Address field). Make sure you do this with DataContext selected in the tabs and not Data Field. If Data Field is selected, it will create a new connection to the sample data. We need to use the data that is already coming through the data context of the data that is already assigned to the XamTree ItemsSource.
Do the same for the second TextBlock Text property assigning it the custom expression “Phone” (which is the name I used for my Branch Telephone property).
Finally, bind the Image Source property to the custom path expression “Icon” (which is the property name I used in sample data for the Image property).
Return scope to the UserControl. Finally, we will need to assign the last DataTemplate to the HierarchicalDataTemplate. In the HierarchicalItemTemplate locate the ItemTemplate property. In the Advanced Options for this property choose the new BranchDataTemplate in the ‘Local Resources’ sub-menu.
Run the project and expand the tree. You will find the XamTree utilizing DataTemplates for both parent and child items. To go onto a third tier of data, the HierarchicalDataTemplate contains its own HierarchicalItemTemplate property. You would use this in the same way we used it for the relationship between level 1 and 2. You could assign a ItemTemplate to the third tier hierarchy of data and specify which property goes to the ItemsSource of the next tier.
What if you have a Tree that is like a linked list, a recursive tree, where one of the properties in the tree item is a list of the tree items of the same class as the parent. In part 2 I will explore some of the techniques you can use  with this scenario. I will also show how you can bind a Boolean property in your data to the IsChecked and the IsSelected properties in the XamTreeItem.