New to Telerik UI for WPFStart a free 30-day trial

Bind RadTreeView to Self-Referencing Data

Updated on Sep 15, 2025

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

C#
	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 and implements an AssociatedItem property that holds the root of each node.

Example 2: Defining DataItemCollection

C#
	 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

XAML
	<example:HierarchyConverter x:Key="HierarchyConverter" />
	
	<HierarchicalDataTemplate x:Key="ItemTemplate"
	  ItemsSource="{Binding Converter={StaticResource HierarchyConverter}}">
	    <TextBlock Text="{Binding Text}" />
	</HierarchicalDataTemplate>

Example 4: Defining the RadTreeView

XAML
	<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

C#
	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

C#
	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

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.

See Also

In this article
See Also
Not finding the help you need?
Contact Support