How to Use BringPathIntoView Method

This tutorial describes how to bring a virtualized item into view using the RadTreeView.BringPathIntoView() method.

It will guids you through the implementation of two common RadTreeView scenarios:

  • Bring a virtualized item, that isn't in the viewport, into view and select it;
  • Add a new item in the RadTreeView.ItemsSource collection, bring it into view and select it.

Set Up the Business Models

Let's start by setting up the business models that will define the hierarchy displayed inside the RadTreeView control. First we'll need a ViewModel describing the items in the tree. Please have in mind that in order to use the BringPathIntoView() method, we need to have the path to each item. This is why we will build a method inside each business item that constructs its full path. However, in order to do so, we'll have to keep a reference to the parent of each item.

public class BusinessItem  
{ 
    public BusinessItem(BusinessItem parent) 
    { 
        this.Items = new ObservableCollection<BusinessItem>(); 
        this.Parent = parent; 
    } 
 
    public ObservableCollection<BusinessItem> Items { get; set; } 
    public BusinessItem Parent { get; private set; } 
 
    public string Name { get; set; } 
 
    public string GetPath() 
    { 
        string path = this.Name; 
        BusinessItem nextParent = this.Parent; 
 
        while (nextParent != null) 
        { 
            path = nextParent.Name + @"\" + path; 
            nextParent = nextParent.Parent; 
        } 
 
        return path; 
    } 
} 
Public Class BusinessItem 
    Public Sub New(ByVal parent As BusinessItem) 
        Me.Items = New ObservableCollection(Of BusinessItem)() 
        Me.Parent = parent 
    End Sub 
 
    Public Property Items() As ObservableCollection(Of BusinessItem) 
    Private privateParent As BusinessItem 
    Public Property Parent() As BusinessItem 
        Get 
            Return privateParent 
        End Get 
        Private Set(ByVal value As BusinessItem) 
            privateParent = value 
        End Set 
    End Property 
 
    Public Property Name() As String 
 
    Public Function GetPath() As String 
        Dim path As String = Me.Name 
        Dim nextParent As BusinessItem = Me.Parent 
 
        Do While nextParent IsNot Nothing 
            path = nextParent.Name & "\" & path 
            nextParent = nextParent.Parent 
        Loop 
 
        Return path 
    End Function 
End Class 

In order to complete the BusinessItem class implementation, we'll only add an IsSelected property so that we can easily control the selected state of each RadTreeViewItem. We'll also have to implement the INotifyPropertyChanged interface in order to notify the view (respectively the RadTreeView) for any changes in the value of the IsSelected property.

public class BusinessItem : INotifyPropertyChanged 
{ 
    ... 
 
    private bool selected; 
    public bool IsSelected 
    { 
        get 
        { 
            return selected; 
        } 
        set 
        { 
            if (value != selected) 
            { 
                selected = value; 
                OnPropertyChanged("IsSelected"); 
            } 
        } 
    } 
 
    public event PropertyChangedEventHandler PropertyChanged; 
 
    protected virtual void OnPropertyChanged(String propertyName) 
    { 
        if (PropertyChanged != null) 
        { 
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); 
        } 
    } 
} 
Public Class BusinessItem 
    Implements INotifyPropertyChanged 
 
    ... 
 
    Private selected As Boolean 
    Public Property IsSelected() As Boolean 
        Get 
            Return selected 
        End Get 
        Set(ByVal value As Boolean) 
            If value <> selected Then 
                selected = value 
                OnPropertyChanged("IsSelected") 
            End If 
        End Set 
    End Property 
 
    Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged 
 
    Protected Overridable Sub OnPropertyChanged(ByVal propertyName As String) 
        RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName)) 
    End Sub 
 
End Class 

Next, we need a ViewModel describing the collection of BusinessItems. For the purpose of this tutorial, we'll create a SampleViewModel inheriting an ObservableCollection of BusinessItems and we will populate it with items:

public class SampleViewModel : ObservableCollection<BusinessItem> 
{ 
    public SampleViewModel() 
    { 
        for (int i = 0; i < 100; i++) 
        { 
            BusinessItem bi1 = new BusinessItem(null) 
            { 
                Name = i.ToString(), 
                IsSelected = false 
            }; 
 
            for (int j = 0; j < 5; j++) 
            { 
                BusinessItem bi2 = new BusinessItem(bi1) 
                { 
                    Name = bi1.Name + "." + j, 
                    IsSelected = false 
                }; 
 
                for (int k = 0; k < 5; k++) 
                { 
                    BusinessItem bi3 = new BusinessItem(bi2) 
                    { 
                        Name = bi2.Name + "." + k, 
                        IsSelected = false 
                    }; 
                    bi2.Items.Add(bi3); 
 
                    for (int l = 0; l < 5; l++) 
                    { 
                        BusinessItem bi4 = new BusinessItem(bi3) 
                        { 
                            Name = bi3.Name + "." + l, 
                            IsSelected = false 
                        }; 
                        bi3.Items.Add(bi4); 
                    } 
                } 
 
                bi1.Items.Add(bi2); 
            } 
 
            this.Add(bi1); 
        } 
    } 
} 
Public Class SampleViewModel 
    Inherits ObservableCollection(Of BusinessItem) 
 
    Public Sub New() 
        For i As Integer = 0 To 99 
            Dim bi1 As New BusinessItem(Nothing) With {.Name = i.ToString(), .IsSelected = False} 
 
            For j As Integer = 0 To 4 
                Dim bi2 As New BusinessItem(bi1) With {.Name = bi1.Name & "." & j, .IsSelected = False} 
 
                For k As Integer = 0 To 4 
                    Dim bi3 As New BusinessItem(bi2) With {.Name = bi2.Name & "." & k, .IsSelected = False} 
                    bi2.Items.Add(bi3) 
 
                    For l As Integer = 0 To 4 
                        Dim bi4 As New BusinessItem(bi3) With {.Name = bi3.Name & "." & l, .IsSelected = False} 
                        bi3.Items.Add(bi4) 
                    Next l 
                Next k 
 
                bi1.Items.Add(bi2) 
            Next j 
 
            Me.Add(bi1) 
        Next i 
    End Sub 
End Class 

Finally, in order to make this example a bit more user-friendly, we will allow our users to bring an item into view, just by entering its header in a TextBox and hitting a BringIntoView button. In order to implement this functionality, we'll need a method that finds a BusinessItem based on its Header. This is why, we will extend the SampleViewModel definition by implementing a GetItemByName() method:

public class SampleViewModel : ObservableCollection<BusinessItem> 
{ 
    public BusinessItem GetItemByName(string name) 
    { 
        BusinessItem item = null; 
        string[] parts = name == null ? new string[] { "0" } : name.Split('.'); 
        foreach (string s in parts) 
        { 
            try 
            { 
                int index = Int32.Parse(s); 
 
                if (item == null) 
                { 
                    item = (index >= 0 && index < this.Items.Count) ? this[index] : null; 
                } 
                else 
                { 
                    item = (index >= 0 && index < item.Items.Count) ? item.Items[index] : null; 
                } 
            } 
            catch 
            { 
                item = null; 
                break; 
            } 
        } 
 
        return item; 
    }    
 
    ... 
 
    } 
} 
Public Class SampleViewModel 
    Inherits ObservableCollection(Of BusinessItem) 
    Public Function GetItemByName(ByVal name As String) As BusinessItem 
        Dim item As BusinessItem = Nothing 
        Dim parts() As String = If(name Is Nothing, New String() { "0" }, name.Split("."c)) 
        For Each s As String In parts 
            Try 
                Dim index As Integer = Int32.Parse(s) 
 
                If item Is Nothing Then 
                    item = If(index >= 0 AndAlso index < Me.Items.Count, Me(index), Nothing) 
                Else 
                    item = If(index >= 0 AndAlso index < item.Items.Count, item.Items(index), Nothing) 
                End If 
            Catch 
                item = Nothing 
                Exit For 
            End Try 
        Next s 
 
        Return item 
    End Function 
 
    ... 
End Class          

Now that our ViewModels are all in place, we can define our view and the RadTreeView control.

Set Up the View

We can start by adding a sample RadTreeView definition. As we will be displaying a large collection of items, it's best to virtualize the control:

<telerik:RadTreeView x:Name="myTreeView" Width="300" IsVirtualizing="True"> 
    <telerik:RadTreeView.ItemTemplate> 
        <HierarchicalDataTemplate ItemsSource="{Binding Items}"> 
            <TextBlock Text="{Binding Name}" /> 
        </HierarchicalDataTemplate> 
    </telerik:RadTreeView.ItemTemplate> 
</telerik:RadTreeView> 

Please keep in mind that the BringPathIntoView() method will work properly only if you set the TextSearch.TextPath attached property. This is due to the fact that the method internally uses the TextPath value to match the path of each business item to its corresponding container of type RadTreeViewItem. Therefore the value of the property has to be the name of the business property that is used to create a path to each node.

As the BusinessItem class defines the Path through the Name property, we need to extend the RadTreeView definition by setting the TextSearch.TextPath property to Name. As our scenario requires us to keep track of the selection in the RadTreeView control, we'll also add a RadTreeViewItem Style to bind the IsSelected property to the appropriate data object.

<telerik:RadTreeView x:Name="myTreeView" Width="300" IsVirtualizing="True" telerik:TextSearch.TextPath="Name"> 
    <telerik:RadTreeView.ItemTemplate> 
        <HierarchicalDataTemplate ItemsSource="{Binding Items}"> 
            <TextBlock Text="{Binding Name}" /> 
        </HierarchicalDataTemplate> 
    </telerik:RadTreeView.ItemTemplate> 
    <telerik:RadTreeView.ItemContainerStyle> 
        <Style TargetType="telerik:RadTreeViewItem"> 
            <Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" /> 
        </Style> 
    </telerik:RadTreeView.ItemContainerStyle> 
</telerik:RadTreeView>       

Next, we'll extend our UI by adding a few TextBoxes and Buttons to allow our users to enter a header of an item and bring it into view. We'll also allow the users to add new items in the RadTreeView by entering the Header of their parent.

<StackPanel Orientation="Horizontal"> 
    <telerik:RadTreeView x:Name="myTreeView" 
                         Width="300" 
                         IsVirtualizing="True" 
                         telerik:TextSearch.TextPath="Name"> 
        <telerik:RadTreeView.ItemTemplate> 
            <HierarchicalDataTemplate ItemsSource="{Binding Items}"> 
                <TextBlock Text="{Binding Name}" /> 
            </HierarchicalDataTemplate> 
        </telerik:RadTreeView.ItemTemplate> 
        <telerik:RadTreeView.ItemContainerStyle> 
            <Style TargetType="telerik:RadTreeViewItem"> 
                <Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" /> 
            </Style> 
        </telerik:RadTreeView.ItemContainerStyle> 
    </telerik:RadTreeView> 
    <StackPanel> 
        <Button Height="30" 
                Click="BringItem" 
                Content="Bring Item" /> 
        <TextBox x:Name="textBox" 
                 Width="300" 
                 Height="30" /> 
        <TextBlock Margin="0,20,0,0" Text="Add new item and bring it into view" /> 
        <TextBlock Text="Add item in parent : " /> 
        <TextBox x:Name="parentBox" 
                 Width="300" 
                 Height="30" 
                 Text="Name of the parent..." /> 
        <Button Height="30" 
                Click="AddItem" 
                Content="Add Item" /> 
    </StackPanel> 
</StackPanel>          

Rad Tree View-How To-BPIV-UI

Set Up the Code-Behind Logic

Now that we've defined both our viewmodels and our view, all we have left is to implement our code-behind logic for bringing an item into view and for adding a new item in the RadTreeView.ItemsSource collection.

First, we'll set the ItemsSource of the RadTreeView and immediately bring the 90.3.3.3 item into view.

public partial class Example : UserControl 
{ 
    private SampleViewModel sampleVM; 
    public Example() 
    { 
        InitializeComponent(); 
 
        sampleVM = new SampleViewModel(); 
        myTreeView.ItemsSource = sampleVM; 
 
        this.textBox.Text = "90.3.3.3"; 
 
        BusinessItem item = this.sampleVM.GetItemByName(this.textBox.Text); 
        if (item != null) 
        { 
            item.IsSelected = true; 
            string path = item.GetPath(); 
            myTreeView.BringPathIntoView(path); 
        } 
    } 
}          
Partial Public Class Example 
    Inherits UserControl 
    Private sampleVM As SampleViewModel 
    Public Sub New() 
        InitializeComponent() 
 
        sampleVM = New SampleViewModel() 
        myTreeView.ItemsSource = sampleVM 
 
        Me.textBox.Text = "90.3.3.3" 
 
        Dim item As BusinessItem = Me.sampleVM.GetItemByName(Me.textBox.Text) 
        If item IsNot Nothing Then 
            item.IsSelected = True 
            Dim path As String = item.GetPath() 
            myTreeView.BringPathIntoView(path) 
        End If 
    End Sub 
End Class 

The above logic will build the RadTreeView Items collection, and as soon as the RadTreeView is properly loaded, the BringPathIntoView() method will find and bring the item with Header of 90.3.3.3 into view.

Next, we will need to implement the Click event handler of the Bring Item button. It has to use the header entered by the user to find the BusienssItem that has to be brought into view and this is why we'll call the SampleViewModel.GetItemByName() method. Then as soon as we find the item, we will set its IsSelected property to True and find its path. Then we can use the RadTreeView.BringPathIntoView() method to bring it into view:

private void BringItem(object sender, RoutedEventArgs e) 
{ 
    BusinessItem item = this.sampleVM.GetItemByName(this.textBox.Text); 
    if (item != null) 
    { 
        item.IsSelected = true; 
        string path = item.GetPath(); 
        myTreeView.BringPathIntoView(path); 
    } 
} 
Private Sub BringItem(ByVal sender As Object, ByVal e As RoutedEventArgs) 
    Dim item As BusinessItem = Me.sampleVM.GetItemByName(Me.textBox.Text) 
    If item IsNot Nothing Then 
        item.IsSelected = True 
        Dim path As String = item.GetPath() 
        myTreeView.BringPathIntoView(path) 
    End If 
End Sub 

Finally, we have to implement a logic that adds a new item when the Add Item button is clicked. As this button should create a new item in the ItemsSource collection of an item defined by the user, we again have to use the SampleViewModel.GetItemByName() method to find the business object that will parent the new item. Then we can add and bring the new item into view:

private void AddItem(object sender, RoutedEventArgs e) 
{ 
    BusinessItem parent = this.sampleVM.GetItemByName(this.parentBox.Text); 
 
    if (parent != null) 
    { 
        BusinessItem newItem = new BusinessItem(parent) 
        { 
            Name = parent.Name + "." + parent.Items.Count, 
            IsSelected = true 
        }; 
 
        parent.Items.Add(newItem); 
 
        string path = newItem.GetPath(); 
        this.myTreeView.BringPathIntoView(path); 
    } 
} 
Private Sub AddItem(ByVal sender As Object, ByVal e As RoutedEventArgs) 
    Dim parent As BusinessItem = Me.sampleVM.GetItemByName(Me.parentBox.Text) 
 
    If parent IsNot Nothing Then 
        Dim newItem As New BusinessItem(parent) With {.Name = parent.Name & "." & parent.Items.Count, .IsSelected = True} 
 
        parent.Items.Add(newItem) 
 
        Dim path As String = newItem.GetPath() 
        Me.myTreeView.BringPathIntoView(path) 
    End If 
End Sub 

Find a complete solution showing this approach in the WPF Samples GitHub repository

In this article