Often we need to display in a TreeView flat, self-referencing data, loaded from a database, that has properties ID and ParentID (or similar) that define the hierarchy. The Telerik TreeView for ASP.NET can automatically bind itself to such data, but our Silverlight TreeView cannot do this out of the box. Fortunately, this “limitation” can be easily avoided with a simple value converter. There is a little trick, however – each data item needs a reference to its parent collection.

Consider the following very simple data object:

public class DataItem : INotifyPropertyChanged
{
    private string text; 
 
 public int ID { get; set; }
    public int ParentID { get; set; }
    public DataItemCollection Owner { get; private set; }
    public string Text
    {
        // Standard INotifyPropertyChanged get/set
    }

    internal void SetOwner(DataItemCollection collection)
    {
        this.Owner = collection;
    }

    // INotifyPropertyChanged implementation goes here
}

 

Those data objects are added into a special DataItemCollection class, that inherits ObservableCollection<T> and overrides SetItem(), InsertItem(), RemoveItem() and ClearItems() methods. In each override we call AdoptItem and DiscardItem, respectively, that set the Owner property of the DataItem class:

public class DataItemCollection : ObservableCollection<DataItem>
{
    protected override void InsertItem(int index, DataItem item)
    {
        this.AdoptItem(item);
        base.InsertItem(index, item);
    }

    protected override void RemoveItem(int index)
    {
        this.DiscardItem(this[index]);
        base.RemoveItem(index);
    }

    protected override void SetItem(int index, DataItem item)
    {
        this.AdoptItem(item);
        base.SetItem(index, item);
    }

    protected override void ClearItems()
    {
        foreach (DataItem item in this)
        {
            this.DiscardItem(item);
        }
        base.ClearItems();
    }

    private void AdoptItem(DataItem item)
    {
        item.SetOwner(this);
    }

    private void DiscardItem(DataItem item)
    {
        item.SetOwner(null);
    }
}

 

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. You should add partial classes in your application that extend the auto-generated according my code above.

Now we are ready to data-bind our RadTreeView:

<UserControl.Resources> <local:HierarchyConverter x:Key="HierarchyConverter" /> <telerik:HierarchicalDataTemplate x:Name="ItemTemplate" ItemsSource="{Binding Converter={StaticResource HierarchyConverter}}"> <TextBlock Text="{Binding Text}" /> </telerik:HierarchicalDataTemplate> </UserControl.Resources> <StackPanel x:Name="LayoutRoot"> <telerikNavigation:RadTreeView x:Name="TreeView1" ItemTemplate="{StaticResource ItemTemplate}" ItemsSource="{Binding Converter={StaticResource HierarchyConverter}}" 
 SelectedValuePath="Text" /> <TextBlock Text="{Binding SelectedValue, ElementName=TreeView1}" /> </StackPanel>

 

There is one non-standard thing: all ItemsSource bindings are made through a ValueConverter. This ValueConverter will create the “real” hierarchy for us:

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)
        {
            return item.Owner.Where(i => i.ParentID == item.ID);
        }

        // We are binding the treeview
        DataItemCollection items = value as DataItemCollection;
        if (items != null)
        {
            return items.Where(i => i.ParentID == 0);
        }

        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 TreeView, 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 cases. I did it like this for simplicity, but if you want, you could split the code in two classes.

 

Here is the sample project:

 

Thank you for your time, I hope this article will be of help.


Related Posts

Comments