Bind RadTreeView to Hierarchical Data from WCF Service and Use Load on Demand
The purpose of this tutorial is to show you how to populate RadTreeView with hierarchical data loaded from WCF service.
Here is a simple treeview declaration:
<UserControl.Resources>
<example:HierarchicalDataSource x:Key="Source" />
<HierarchicalDataTemplate x:Key="NodeTemplate"
ItemsSource="{Binding Children}">
<TextBlock Text="{Binding Text}"/>
</HierarchicalDataTemplate>
</UserControl.Resources>
<Grid x:Name="LayoutRoot" Background="White">
<telerik:RadTreeView x:Name="radTreeView" Margin="8"
ItemsSource="{Binding Source={StaticResource Source}}"
ItemTemplate="{StaticResource NodeTemplate}"/>
</Grid>
The web service will return an observable collection with objects of type TableItem. Here is the TableItem structure:
public class TableItem
{
public TableItem()
{
this.Children = new List<TableItem>();
}
public int Id
{
get;
set;
}
public int ParentId
{
get;
set;
}
public string Text
{
get;
set;
}
public List<TableItem> Children
{
get;
set;
}
}
Public Class TableItem
Public Sub New()
Me.Children = New List(Of TableItem)()
End Sub
Private _Id As Integer
Public Property Id() As Integer
Get
Return _Id
End Get
Set(ByVal value As Integer)
_Id = value
End Set
End Property
Private _ParentId As Integer
Public Property ParentId() As Integer
Get
Return _ParentId
End Get
Set(ByVal value As Integer)
_ParentId = value
End Set
End Property
Private _Text As String
Public Property Text() As String
Get
Return _Text
End Get
Set(ByVal value As String)
_Text = value
End Set
End Property
Private _Children As List(Of TableItem)
Public Property Children() As List(Of TableItem)
Get
Return _Children
End Get
Set(ByVal value As List(Of TableItem))
_Children = value
End Set
End Property
End Class
Now that you have the basis set up, it's time to go on. First you should create your data source object. Add a new class named HierarchicalDataSource which derives from ObservableCollection of TableItem.
public class HierarchicalDataSource : ObservableCollection<TableItem>
{
public HierarchicalDataSource()
{
}
}
Public Class HierarchicalDataSource
Inherits ObservableCollection(Of TableItem)
Public Sub New()
End Sub
End Class
Next, you need to add a reference to the WCF service and load the data. You also need a list that holds all items that come from the web service result.
public class HierarchicalDataSource : ObservableCollection<TableItem>
{
// This list holds all the items that come from the web service result
private List<TableItem> unsortedList = new List<TableItem>();
public HierarchicalDataSource()
{
SampleWcfServiceClient serviceClient = new SampleWcfServiceClient();
serviceClient.LoadHierarchicalDataCompleted += new EventHandler<LoadHierarchicalDataCompletedEventArgs>( serviceClient_LoadHierarchicalDataCompleted );
serviceClient.LoadHierarchicalDataAsync();
}
private void serviceClient_LoadHierarchicalDataCompleted( object sender, LoadHierarchicalDataCompletedEventArgs e )
{
// transfer all the items from the result to the unsorted list
foreach ( TableItem item in e.Result )
{
TableItem genericItem = new TableItem()
{
Text = item.Text,
Id = item.Id,
ParentId = item.ParentId
};
this.unsortedList.Add( genericItem );
}
// Get all the first level nodes.
var rootNodes = this.unsortedList.Where( x => x.ParentId == 0 );
// Foreach root node, get all its children and add the node to the HierarchicalDataSource.
// see below how the FindChildren method works
foreach ( TableItem node in rootNodes )
{
this.FindChildren( node );
this.Add( node );
}
}
}
Public Class HierarchicalDataSource
Inherits ObservableCollection(Of TableItem)
' This list holds all the items that come from the web service result'
Private unsortedList As New List(Of TableItem)()
Public Sub New()
Dim serviceClient As New SampleWcfServiceClient()
AddHandler serviceClient.LoadHierarchicalDataCompleted, AddressOf serviceClient_LoadHierarchicalDataCompleted
serviceClient.LoadHierarchicalDataAsync()
End Sub
Private Sub serviceClient_LoadHierarchicalDataCompleted(ByVal sender As Object, ByVal e As LoadHierarchicalDataCompletedEventArgs)
' transfer all the items from the result to the unsorted list'
For Each item As TableItem In e.Result
Dim genericItem As New TableItem()
Me.unsortedList.Add(genericItem)
Next
' Get all the first level nodes.'
Dim rootNodes = Me.unsortedList.Where(Function(x) x.ParentId = 0)
' Foreach root node, get all its children and add the node to the HierarchicalDataSource.'
' see below how the FindChildren method works'
For Each node As TableItem In rootNodes
Me.FindChildren(node)
Me.Add(node)
Next
End Sub
End Class
Add the FindChildren() method to the HierarchicalDataSource file. It will find all child nodes by a given item.
private void FindChildren( TableItem item )
{
// find all the children of the item
var children = unsortedList.Where( x => x.ParentId == item.Id && x.Id != item.Id );
// add the child to the item's children collection and call the FindChildren recursively, in case the child has children
foreach ( TableItem child in children )
{
// By not calling iteratively FindChildren() here we prevent
// the automatic loading of all items in the data
// source and load only the next level in the hierarchy
item.Children.Add( child );
}
}
Private Sub FindChildren(ByVal item As TableItem)
' find all the children of the item'
Dim children = unsortedList.Where(Function(x) x.ParentId = item.Id AndAlso x.Id <> item.Id)
' add the child to the items children collection and call the FindChildren recursively, in case the child has children'
For Each child As TableItem In children
' By not calling iteratively FindChildren() here we prevent'
' the automatic loading of all items in the data'
' source and load only the next level in the hierarchy'
item.Children.Add(child)
Next
End Sub
Add a public method named LoadItemChildren(). This method visits all current items and adds their direct children to the data source, if there are any.
public void LoadItemChildren( TableItem item )
{
foreach ( TableItem i in item.Children )
{
FindChildren( i );
}
}
Public Sub LoadItemChildren(ByVal item As TableItem)
For Each i As TableItem In item.Children
FindChildren(i)
Next
End Sub
Finally add an event handler in your treeview declaration for the Expanded event.
<telerik:RadTreeView x:Name="radTreeView" Margin="8"
Expanded="radTreeView_Expanded"
ItemsSource="{Binding Source={StaticResource Source}}"
ItemTemplate="{StaticResource NodeTemplate}"/>
Switch to the code-behind and the following code to handle the event.
private void radTreeView_Expanded( object sender, Telerik.Windows.RadRoutedEventArgs e )
{
Telerik.Windows.Controls.RadTreeView tree = sender as Telerik.Windows.Controls.RadTreeView;
RadTreeViewItem item = e.OriginalSource as RadTreeViewItem;
if ( ( tree != null ) && ( item != null ) )
{
// Load the next level from the data hierarchy
HierarchicalDataSource source = this.Resources[ "Source" ] as HierarchicalDataSource;
TableItem ti = item.DataContext as TableItem;
if ( ( ti != null ) && ( source != null ) )
{
source.LoadItemChildren( ti );
}
}
}
Private Sub radTreeView_Expanded(ByVal sender As Object, ByVal e As Telerik.Windows.RadRoutedEventArgs)
Dim tree As Telerik.Windows.Controls.RadTreeView = TryCast(sender, Telerik.Windows.Controls.RadTreeView)
Dim item As RadTreeViewItem = TryCast(e.OriginalSource, RadTreeViewItem)
If (tree IsNot Nothing) AndAlso (item IsNot Nothing) Then
' Load the next level from the data hierarchy'
Dim source As HierarchicalDataSource = TryCast(Me.Resources("Source"), HierarchicalDataSource)
Dim ti As TableItem = TryCast(item.DataContext, TableItem)
If (ti IsNot Nothing) AndAlso (source IsNot Nothing) Then
source.LoadItemChildren(ti)
End If
End If
End Sub
Here, you first get references to the treeview and the item that was expanded. Then, you get a reference to the hierarchical data source and call its LoadItemChildren() method and pass the value of the expanded item. What the method does is fetching the children of that data item via the web service and adding them as children of the treeview.