Scroll Item into View
The RadTreeListView API offers ScrollIntoView support through the following methods:
The goal of this tutorial is to demonstrate how you can bring a particular, deeply nested TreeListViewRow into view. This might be helpful if you need a fast hierarchy control with many nested levels and RadTreeView is slow in your particular scenario with bring into view. This example is configured in such way that RadTreeListView behaves and feels much like RadTreeView.
Initial setting up of the project
For the purpose of this example, you will need to create an empty application project and open it in Visual Studio. The first step is to add references to the following assemblies:
Telerik.Windows.Controls
Telerik.Windows.Controls.GridView
Telerik.Windows.Controls.Input
Telerik.Windows.Data
Then you can define the controls in your view. As the purpose of this tutorial is to demonstrate how to implement the ScrollIntoView methods, we won't focus on the definitions of the controls in xaml.
<Grid x:Name="LayoutRoot" Background="White">
<Grid.RowDefinitions>
<RowDefinition Height="40" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.Resources>
<Style TargetType="telerik:RadTreeListView">
<Setter Property="VerticalGridLinesBrush" Value="{x:Null}" />
<Setter Property="RowIndicatorVisibility" Value="Collapsed" />
<Setter Property="IsFilteringAllowed" Value="False" />
<Setter Property="CanUserFreezeColumns" Value="False" />
<Setter Property="CanUserDeleteRows" Value="False" />
<Setter Property="AutoGenerateColumns" Value="False" />
<Setter Property="ShowGroupPanel" Value="False" />
<Setter Property="ShowColumnHeaders" Value="False" />
<Setter Property="AutoExpandGroups" Value="True" />
<Setter Property="GridLinesVisibility" Value="None" />
<Setter Property="RowHeight" Value="24" />
<Setter Property="BorderThickness" Value="0" />
<Setter Property="BorderBrush" Value="{x:Null}" />
</Style>
</Grid.Resources>
<telerik:RadTreeListView x:Name="treeList"
Grid.Row="1"
Width="500"
IsExpandableBinding="{Binding IsExpandable}"
TreeLinesVisibility="Visible">
<telerik:RadTreeListView.ChildTableDefinitions>
<telerik:TreeListViewTableDefinition ItemsSource="{Binding Children}" />
</telerik:RadTreeListView.ChildTableDefinitions>
<telerik:RadTreeListView.Columns>
<telerik:GridViewDataColumn DataMemberBinding="{Binding Name}" Header="Name" />
</telerik:RadTreeListView.Columns>
</telerik:RadTreeListView>
<StackPanel HorizontalAlignment="Center" Orientation="Horizontal">
<telerik:RadButton Width="350"
Click="RadButton_Click_1"
Content="ScrollIntoViewAsync Recursively" />
<telerik:RadButton Width="350"
Click="RadButton_Click_2"
Content="Expand in a Loop and Then Scroll" />
</StackPanel>
</Grid>
The next step is to define the business object.
public class DataItem : ViewModelBase
{
private string name;
public string Name
{
get { return this.name; }
set
{
if (this.name != value)
{
this.name = value;
this.OnPropertyChanged("Name");
}
}
}
private ObservableCollection<DataItem> children;
public ObservableCollection<DataItem> Children
{
get
{
if (children == null)
{
children = new ObservableCollection<DataItem>();
for (int i = 0; i < 100; i++)
{
DataItem item = new DataItem() { Name = this.Name + "." + i};
children.Add(item);
}
}
return children;
}
}
public bool IsExpandable
{
get
{
return true;
}
}
}
Public Class DataItem
Inherits ViewModelBase
Private name_Renamed As String
Public Property Name() As String
Get
Return Me.name_Renamed
End Get
Set(ByVal value As String)
If Me.name_Renamed <> value Then
Me.name_Renamed = value
Me.OnPropertyChanged("Name")
End If
End Set
End Property
Private children_Renamed As ObservableCollection(Of DataItem)
Public ReadOnly Property Children() As ObservableCollection(Of DataItem)
Get
If children_Renamed Is Nothing Then
children_Renamed = New ObservableCollection(Of DataItem)()
For i As Integer = 0 To 99
Dim item As New DataItem() With {.Name = Me.Name & "." & i}
children_Renamed.Add(item)
Next i
End If
Return children_Renamed
End Get
End Property
Public ReadOnly Property IsExpandable() As Boolean
Get
Return True
End Get
End Property
End Class
What comes next, is to populate with data.
public partial class Example : UserControl
{
DateTime listBringStart;
private ObservableCollection<DataItem> list;
public Example()
{
InitializeComponent();
this.LoadData();
}
private void LoadData()
{
list = new ObservableCollection<DataItem>();
for (int i = 0; i < 100; i++)
{
DataItem root = new DataItem() { Name = "Item " + i };
list.Add(root);
}
this.treeList.ItemsSource = list;
}
}
Partial Public Class Example
Inherits UserControl
Private listBringStart As Date
Private list As ObservableCollection(Of DataItem)
Public Sub New()
InitializeComponent()
Me.LoadData()
End Sub
Private Sub LoadData()
list = New ObservableCollection(Of DataItem)()
For i As Integer = 0 To 99
Dim root As New DataItem() With {.Name = "Item " & i}
list.Add(root)
Next i
Me.treeList.ItemsSource = list
End Sub
End Class
ScrollIntoViewAsync()
Clicking the first button will bring an item which is 20 levels deep with updating the UI on every level expansion. For this purpose we will use the ScrollIntoViewAsync (Object dataItem, Action(FrameworkElement) scrollFinishedCallback, bool expandItem) method, which scrolls the row containing the data item into view in an asynchronous manner.
private void RadButton_Click_1(object sender, RoutedEventArgs e)
{
this.listBringStart = DateTime.Now;
this.ScrollIntoViewRecursive(0, list[85]);
}
private void ScrollIntoViewRecursive(int level, DataItem item)
{
if (level >= 20)
{
MessageBox.Show(DateTime.Now.Subtract(this.listBringStart).TotalSeconds.ToString() + " sec.");
return;
}
var newItem = item.Children[85];
this.treeList.ScrollIntoViewAsync(item, (f) => { ScrollIntoViewRecursive(++level, newItem); }, true);
}
Private Sub RadButton_Click_1(ByVal sender As Object, ByVal e As RoutedEventArgs)
Me.listBringStart = Date.Now
Me.ScrollIntoViewRecursive(0, list(85))
End Sub
Private Sub ScrollIntoViewRecursive(ByVal level As Integer, ByVal item As DataItem)
If level >= 20 Then
MessageBox.Show(Date.Now.Subtract(Me.listBringStart).TotalSeconds.ToString() & " sec.")
Return
End If
Dim newItem = item.Children(85)
Me.treeList.ScrollIntoViewAsync(item, Sub(f)
level += 1
ScrollIntoViewRecursive(level, newItem)
End Sub, True)
End Sub
ScrollIntoView()
Clicking the second button will update the UI only when the bring operation is finished. Here we are using the ScrollIntoView(Object dataItem, bool expandItem) method, which scrolls the row containing the data item into view.
private void RadButton_Click_2(object sender, RoutedEventArgs e)
{
DateTime startTime = DateTime.Now;
DataItem start = this.list[85];
for (int i = 0; i < 20; i++)
{
this.treeList.ExpandHierarchyItem(start);
start = start.Children[85];
}
this.treeList.ScrollIntoView(start, false);
this.treeList.SelectedItems.Add(start);
DateTime end = DateTime.Now;
MessageBox.Show(end.Subtract(startTime).TotalSeconds.ToString() + " sec.");
}
Private Sub RadButton_Click_2(ByVal sender As Object, ByVal e As RoutedEventArgs)
Dim startTime As Date = Date.Now
Dim start As DataItem = Me.list(85)
For i As Integer = 0 To 19
Me.treeList.ExpandHierarchyItem(start)
start = start.Children(85)
Next i
Me.treeList.ScrollIntoView(start, False)
Me.treeList.SelectedItems.Add(start)
Dim [end] As Date = Date.Now
MessageBox.Show([end].Subtract(startTime).TotalSeconds.ToString() & " sec.")
End Sub
Please have in mind that running the application without debugger (Ctrl + F5 when in VS) will result in faster user experience.
Find a runnable project of the previous example in the WPF Samples GitHub repository.