Self-Referencing GridView
Except displaying hierarchical data and applying some custom criteria about when the subset of data is to be displayed, RadGridView allows you to define a relation that points back to the same collection. In the cases when the hierarchical data is build from one type of items, you can use a self referencing RadGridView to display the data.
This tutorial demonstrates how you can build a self-referencing hierarchy.
Example 1 demonstrates an ordinary RadGridView declaration, populated with some data:
Example 1: RadGridView declaration
<Window.Resources>
<my:MyViewModel x:Key="MyViewModel"/>
</Window.Resources>
<telerik:RadGridView DataContext="{StaticResource MyViewModel}"
ItemsSource="{Binding Employees}"
AutoGenerateColumns="False">
<telerik:RadGridView.Columns>
<telerik:GridViewDataColumn DataMemberBinding="{Binding Id}"
Header="Id" />
<telerik:GridViewDataColumn DataMemberBinding="{Binding Name}"
Header="Name" />
<telerik:GridViewDataColumn DataMemberBinding="{Binding Title}"
Header="Title" />
</telerik:RadGridView.Columns>
</telerik:RadGridView>
Example 2: Creating the ViewModel
public class MyViewModel
{
private ObservableCollection<Employee> employees;
public ObservableCollection<Employee> Employees
{
get
{
if (this.employees == null)
{
this.employees = EmployeeService.GetEmployees();
}
return this.employees;
}
}
}
public class EmployeeService
{
public static ObservableCollection<Employee> GetEmployees()
{
ObservableCollection<Employee> employees = new ObservableCollection<Employee>();
employees.Add(new Employee()
{
Name = "Sarah Blake",
Title = "Supplied Manager",
Id = 1,
ManagerId = null
});
employees.Add(new Employee()
{
Id = 2,
ManagerId = 1,
Name = "Jane Simpson",
Title = "Security",
});
employees.Add(new Employee()
{
Id = 3,
ManagerId = 1,
Name = "John Peterson",
Title = "Consultant"
});
employees.Add(new Employee()
{
Id = 4,
ManagerId = 3,
Name = "Peter Bush",
Title = "Cashier"
});
employees.Add(new Employee()
{
Id = 5,
ManagerId = 3,
Name = "Will Grigs",
Title = "Consultant"
});
return employees;
}
}
public class Employee : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private int id;
private int? managerId;
private string name;
private string title;
public int Id
{
get { return this.id; }
set
{
if (value != this.id)
{
this.id = value;
this.OnPropertyChanged("Id");
}
}
}
public int? ManagerId
{
get { return this.managerId; }
set
{
if (value != this.managerId)
{
this.managerId = value;
this.OnPropertyChanged("ManagerId");
}
}
}
public string Name
{
get { return this.name; }
set
{
if (value != this.name)
{
this.name = value;
this.OnPropertyChanged("Name");
}
}
}
public string Title
{
get { return this.title; }
set
{
if (value != this.title)
{
this.title = value;
this.OnPropertyChanged("Title");
}
}
}
public Employee()
{
}
public Employee(int id, int managerId, string name, string title)
{
this.name = name;
this.id = id;
this.managerId = managerId;
this.title = title;
}
protected virtual void OnPropertyChanged(PropertyChangedEventArgs args)
{
PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
{
handler(this, args);
}
}
private void OnPropertyChanged(string propertyName)
{
this.OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
}
}
Public Class MyViewModel
Public ReadOnly Property Employees() As ObservableCollection(Of Employee)
Get
If Me.employees Is Nothing Then
Me.employees = EmployeeService.GetEmployees()
End If
Return Me.employees
End Get
End Property
End Class
Public Class EmployeeService
Public Shared Function GetEmployees() As ObservableCollection(Of Employee)
Dim employees As New ObservableCollection(Of Employee)()
employees.Add(New Employee() With {
.Name = "Sarah Blake",
.Title = "Supplied Manager",
.Id = 1,
.ManagerId = Nothing
})
employees.Add(New Employee() With {
.Id = 2,
.ManagerId = 1,
.Name = "Jane Simpson",
.Title = "Security"
})
employees.Add(New Employee() With {
.Id = 3,
.ManagerId = 1,
.Name = "John Peterson",
.Title = "Consultant"
})
employees.Add(New Employee() With {
.Id = 4,
.ManagerId = 3,
.Name = "Peter Bush",
.Title = "Cashier"
})
employees.Add(New Employee() With {
.Id = 5,
.ManagerId = 3,
.Name = "Will Grigs",
.Title = "Consultant"
})
Return employees
End Function
End Class
Public Class Employee
Implements INotifyPropertyChanged
Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
Private _id As Integer
Private _managerId As Integer
Private _name As String
Private _title As String
Public Property Id() As Integer
Get
Return Me._id
End Get
Set(ByVal value As Integer)
If value <> Me._id Then
Me._id = value
Me.OnPropertyChanged("Id")
End If
End Set
End Property
Public Property ManagerId() As Integer?
Get
Return Me._managerId
End Get
Set(ByVal value? As Integer)
If Not Me._managerId.Equals(value) Then
Me._managerId = value
Me.OnPropertyChanged("ManagerId")
End If
End Set
End Property
Public Property Name() As String
Get
Return Me._name
End Get
Set(ByVal value As String)
If value <> Me._name Then
Me._name = value
Me.OnPropertyChanged("Name")
End If
End Set
End Property
Public Property Title() As String
Get
Return Me._title
End Get
Set(ByVal value As String)
If value <> Me._title Then
Me._title = value
Me.OnPropertyChanged("Title")
End If
End Set
End Property
Public Sub New()
End Sub
Public Sub New(ByVal id As Integer, ByVal managerId As Integer, ByVal name As String, ByVal title As String)
Me._name = name
Me._id = id
Me._managerId = managerId
Me._title = title
End Sub
Protected Overridable Sub OnPropertyChanged(ByVal args As PropertyChangedEventArgs)
Dim handler As PropertyChangedEventHandler = Me.PropertyChangedEvent
If handler IsNot Nothing Then
handler(Me, args)
End If
End Sub
Private Sub OnPropertyChanged(ByVal propertyName As String)
Me.OnPropertyChanged(New PropertyChangedEventArgs(propertyName))
End Sub
End Class
The ChildTableDefinitions property of the RadGridView allows you to display objects which are related to objects of the same type, thus modelling a tree structure. Example 3 demonstrates how this can be achieved with the Employee class defined in Example 2.
Example 3: Setting the ChildTableDefinitions
<telerik:RadGridView DataContext="{StaticResource MyViewModel}"
ItemsSource="{Binding Employees}"
AutoGenerateColumns="False">
<telerik:GridViewTableDefinition.Relation>
<telerik:TableRelation IsSelfReference="True">
<telerik:TableRelation.FieldNames>
<telerik:FieldDescriptorNamePair
ParentFieldDescriptorName="Id"
ChildFieldDescriptorName="ManagerId" />
</telerik:TableRelation.FieldNames>
</telerik:TableRelation>
</telerik:GridViewTableDefinition.Relation>
<!--...-->
</telerik:RadGridView>
ParentFieldDescriptorName - specifies the name of the property of the object in the parent RadGridView, that is on one side of the relation.
ChildFieldDescriptorName - specifies the name of the property of the object in the child RadGridView, that is on the other side of the relation.
In the child RadGridView the entries of the RadGridView's source are displayed, which values of the property, specified as the ChildFieldDescriptorName, are equal to the value of the property, specified as the ParentFieldDescriptorName.
Figure 1 demonstrates the result from Example 3:
Figure 1: Self-referencing RadGridView
You can specify more than one FiledDescriptorNamePair. For example, define a relation that specifies that the child objects should have the same title as the parent object.
Example 4: Specifying two FiledDescriptorNamePair
<telerik:RadGridView DataContext="{StaticResource MyViewModel}"
ItemsSource="{Binding Employees}"
AutoGenerateColumns="False">
<telerik:RadGridView.ChildTableDefinitions>
<telerik:GridViewTableDefinition>
<telerik:GridViewTableDefinition.Relation>
<telerik:TableRelation IsSelfReference="True">
<telerik:TableRelation.FieldNames>
<telerik:FieldDescriptorNamePair
ParentFieldDescriptorName="Id"
ChildFieldDescriptorName="ManagerId" />
</telerik:TableRelation.FieldNames>
</telerik:TableRelation>
</telerik:GridViewTableDefinition.Relation>
</telerik:GridViewTableDefinition>
</telerik:RadGridView.ChildTableDefinitions>
<!--...-->
</telerik:RadGridView>
Figure 2: Custom relation
In order to hide the GridViewToggleButton that expands the hierarchy for items that do not have subitems, you can introduce a boolean property in the Employee class and use the IsExpandableBinding property of the RadGridView.
In order to learn how to modify the child RadGridViews, check out the How-to: Set properties to the child gridview article.