Bind RadTreeView to Self-Referencing Data
This tutorial will show you how to display a RadTreeView with flat, self-referencing data, loaded from a database, that has properties ID and ParentID (or similar) which define the hierarchy.
Consider the following very simple data object:
Example 1: Defining the DataItem class
public class DataItem
{
public int Id
{
get;
set;
}
public int ParentId
{
get;
set;
}
public string Text
{
get;
set;
}
public DataItemCollection OwnerCollection
{
get;
protected set;
}
internal void SetOwnerCollection(DataItemCollection collection)
{
this.OwnerCollection = collection;
}
}Those data objects are added into a special DataItemCollection class, that inherits ObservableCollection
Example 2: Defining DataItemCollection
public class DataItemCollection : ObservableCollection<DataItem>
{
public DataItemCollection()
: base()
{
}
public DataItemCollection(IEnumerable<DataItem> collection)
: base(collection)
{
}
public DataItem AssociatedItem
{
get;
protected set;
}
public void SetAssociatedItem(DataItem item)
{
this.AssociatedItem = item;
}
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
base.OnCollectionChanged(e);
if (e.Action == NotifyCollectionChangedAction.Add)
{
foreach (DataItem item in e.NewItems)
{
if (this.AssociatedItem != null && item.ParentId != this.AssociatedItem.Id)
{
item.ParentId = this.AssociatedItem.Id;
}
}
}
}
}Normally when you load your data objects from a service in your application, you will have auto-generated partial classes, that are relatively easy to extend.
Now we are ready to data-bind our RadTreeView:
Example 3: Defining the resources
<example:HierarchyConverter x:Key="HierarchyConverter" />
<HierarchicalDataTemplate x:Key="ItemTemplate"
ItemsSource="{Binding Converter={StaticResource HierarchyConverter}}">
<TextBlock Text="{Binding Text}" />
</HierarchicalDataTemplate>
Example 4: Defining the RadTreeView
<telerik:RadTreeView x:Name="radTreeView"
ItemTemplate="{StaticResource ItemTemplate}"
ItemsSource="{Binding Converter={StaticResource HierarchyConverter}}"/>
There is one non-standard thing: all ItemsSource bindings are made through a ValueConverter. This ValueConverter will create the "real" hierarchy for us:
Example 5: Defining the HierarchyConverter
public class HierarchyConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
// We are binding an item
DataItem item = value as DataItem;
if (item != null)
{
var children = item.OwnerCollection.Where(i => i.ParentId == item.Id);
var collection = new DataItemCollection(children);
collection.SetAssociatedItem(item);
return collection;
}
// We are binding the treeview
DataItemCollection items = value as DataItemCollection;
if (items != null)
{
var children = items.Where(i => i.ParentId == 0);
return new DataItemCollection(children);
}
return null;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}When a DataItem object is passed as value, we are binding a TreeViewItem, so the Convert() method will return all DataItem objects from the Owner collection that have ParentID equal to the ID of the passed DataItem. When a DataItemCollection is passed, we are binding the RadTreeView, so the Convert() method will return the root-level DataItem objects, that have ParentID=0. Of course, it is up to you to decide whether you want a single, or separate converters for both of the cases. It is done in this way for simplicity, but if you want, you could split the code into two classes.
Example 6: Populating the RadTreeView
public MainWindow()
{
InitializeComponent();
var source = new DataItemCollection()
{
new DataItem () { Text = "Item 1", Id = 1, ParentId = 0 },
new DataItem () { Text = "Item 2", Id = 2, ParentId = 0 },
new DataItem () { Text = "Item 3", Id = 3, ParentId = 0 },
new DataItem () { Text = "Item 1.1", Id = 5, ParentId = 1 },
new DataItem () { Text = "Item 1.2", Id = 6, ParentId = 1 },
new DataItem () { Text = "Item 1.3", Id = 7, ParentId = 1 },
new DataItem () { Text = "Item 2.1", Id = 8, ParentId = 2 },
new DataItem () { Text = "Item 2.2", Id = 9, ParentId = 2 },
new DataItem () { Text = "Item 2.3", Id = 10, ParentId = 2 },
new DataItem () { Text = "Item 3.1", Id = 11, ParentId = 3 },
new DataItem () { Text = "Item 3.2", Id = 12, ParentId = 3 },
new DataItem () { Text = "Item 3.3", Id = 13, ParentId = 3, }
};
foreach (var item in source)
{
item.SetOwnerCollection(source);
}
this.DataContext = source;
}Image 1: Self-Referencing RadTreeView

You can check out this example in the RadTreeView SDK examples or in the SDK Samples Browser that provides a more convenient approach in exploring and executing the examples in the Telerik XAML SDK repository. The SDK Samples Browser application is available for download from this link.