Edit this page

Self-Referencing Hierarchy

RELATED VIDEOS
Binding RadGridView to a Self Referencing Hierarchy
In this video, you will learn how to display a self referencing hierarchy inside of RadGridView for WinForms.
gridview-hierarchical-grid-self-referencing-hierarchy 002

RadGridView allows you to define a relation that points back to the same table. 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 modes gives you all the functionality a TreeList control would provide, with a lot more.

Self-Referencing Example

The following example demonstrates how RadGridView displays a hierarchy based on the data provided by the file system where one folder can have files and folders that can have other files and folders, etc.

gridview-hierarchical-grid-self-referencing-hierarchy 001

In order to achieve the look of the RadGridView from the above image, you need to follow these steps:

1. Create a data source with an appropriate structure where each data record contains Id that serves as a unique identifier for the record and ParentId that determines the parent of the data record. In our case this is a business object of type FileSystemItem that can serve as a folder and as a document.

public class FileSystemItem
{
    int id;
    string name;
    DateTime creationTime;
    int parentFolderId;
    private string type;
    public int Id
    {
        get
        {
            return id;
        }
        set
        {
            id = value;
        }
    }
    public string FileSystemInfoType
    {
        get
        {
            return type;
        }
        set
        {
            type = value;
        }
    }
    public string Name
    {
        get
        {
            return name;
        }
        set
        {
            name = value;
        }
    }
    public DateTime CreationTime
    {
        get
        {
            return creationTime;
        }
        set
        {
            creationTime = value;
        }
    }
    public int ParentFolderId
    {
        get
        {
            return parentFolderId;
        }
        set
        {
            parentFolderId = value;
        }
    }
    public FileSystemItem(int id, string type, string name, DateTime creationTime, int parentFolderId)
    {
        this.id = id;
        this.type = type;
        this.name = name;
        this.creationTime = creationTime;
        this.parentFolderId = parentFolderId;
    }
}

Public Class FileSystemItem
    Private id_Renamed As Integer
    Private name_Renamed As String
    Private creationTime_Renamed As Date
    Private parentFolderId_Renamed As Integer
    Private type As String
    Public Property Id() As Integer
        Get
            Return id_Renamed
        End Get
        Set(ByVal value As Integer)
            id_Renamed = value
        End Set
    End Property
    Public Property FileSystemInfoType() As String
        Get
            Return type
        End Get
        Set(ByVal value As String)
            type = value
        End Set
    End Property
    Public Property Name() As String
        Get
            Return name_Renamed
        End Get
        Set(ByVal value As String)
            name_Renamed = value
        End Set
    End Property
    Public Property CreationTime() As Date
        Get
            Return creationTime_Renamed
        End Get
        Set(ByVal value As Date)
            creationTime_Renamed = value
        End Set
    End Property
    Public Property ParentFolderId() As Integer
        Get
            Return parentFolderId_Renamed
        End Get
        Set(ByVal value As Integer)
            parentFolderId_Renamed = value
        End Set
    End Property
    Public Sub New(ByVal id As Integer, ByVal type As String, ByVal name As String, ByVal creationTime As Date, ByVal parentFolderId As Integer)
        Me.id_Renamed = id
        Me.type = type
        Me.name_Renamed = name
        Me.creationTime_Renamed = creationTime
        Me.parentFolderId_Renamed = parentFolderId
    End Sub
End Class

2. Fill a BindingList with objects of type FileSystemItem. The content of the list will depend on the content of the "C:\Program Files (x86)\Telerik" folder:

BindingList<FileSystemItem> list = new BindingList<FileSystemItem>();
int fileFolderIndex = 0;
public void GetFilesAndFolders(string dir, int parentId)
{
    DirectoryInfo di = new DirectoryInfo(dir);
    FileInfo[] rgFiles = di.GetFiles();
    foreach (FileInfo fi in rgFiles)
    {
        fileFolderIndex++;
        list.Add(new FileSystemItem(fileFolderIndex, "File", fi.Name, fi.CreationTime, parentId));
    }
    DirectoryInfo[] dirs = di.GetDirectories();
    foreach (DirectoryInfo d in dirs)
    {
        fileFolderIndex++;
        list.Add(new FileSystemItem(fileFolderIndex, "Folder", d.Name, d.CreationTime, parentId));
        GetFilesAndFolders(d.FullName, fileFolderIndex);
    }
}

Private list As New BindingList(Of FileSystemItem)()
Private fileFolderIndex As Integer = 0
Public Sub GetFilesAndFolders(ByVal dir As String, ByVal parentId As Integer)
    Dim di As New DirectoryInfo(dir)
    Dim rgFiles() As FileInfo = di.GetFiles()
    For Each fi As FileInfo In rgFiles
        fileFolderIndex += 1
        list.Add(New FileSystemItem(fileFolderIndex, "File", fi.Name, fi.CreationTime, parentId))
    Next fi
    Dim dirs() As DirectoryInfo = di.GetDirectories()
    For Each d As DirectoryInfo In dirs
        fileFolderIndex += 1
        list.Add(New FileSystemItem(fileFolderIndex, "Folder", d.Name, d.CreationTime, parentId))
        GetFilesAndFolders(d.FullName, fileFolderIndex)
    Next d
End Sub

3. The most important step of this example - setting up the self-referencing mode of RadGridView. To do this, you should call the AddSelfReference method of the Relations collection passing the template that should reflect the structure of the business object and the properties that should determine the parent-child relation:

this.radGridView1.Relations.AddSelfReference(this.radGridView1.MasterTemplate, "Id", "ParentFolderId");
this.radGridView1.DataSource = list;

Me.RadGridView1.Relations.AddSelfReference(Me.RadGridView1.MasterTemplate, "Id", "ParentFolderId")
Me.RadGridView1.DataSource = list

4. Hide the columns that are not useful to your clients. These columns in our case are Id, ParentFolderId and FileSystemInfoType:

this.radGridView1.Columns["Id"].IsVisible = false;
this.radGridView1.Columns["ParentFolderId"].IsVisible = false;
this.radGridView1.Columns["FileSystemInfoType"].IsVisible = false;

Me.RadGridView1.Columns("Id").IsVisible = False
Me.RadGridView1.Columns("ParentFolderId").IsVisible = False
Me.RadGridView1.Columns("FileSystemInfoType").IsVisible = False

5. Last, but not least, you may want to show different images depending on the actual file system types. To do this, you should handle the CellFormatting event, check if the FileSystemInfoType is File or Folder and set the appropriate image accordingly:

// Getting the images from the resources of the project
Image documentImage = SamplesCS.Properties.Resources.Document;
Image folderImage = SamplesCS.Properties.Resources.Folder;
void radGridView1_CellFormatting(object sender, Telerik.WinControls.UI.CellFormattingEventArgs e)
{
    GridDataCellElement dataCell = e.CellElement as GridDataCellElement;
    if (dataCell.ColumnInfo.Name == "Name")
    {
        GridViewDataRowInfo dataRow = dataCell.RowInfo as GridViewDataRowInfo;
        if (dataRow != null)
        {
            dataCell.ImageAlignment = ContentAlignment.MiddleLeft;
            string valueType = Convert.ToString(dataRow.Cells["FileSystemInfoType"].Value).ToUpperInvariant();
            if (valueType.Contains("FILE"))
            {
                dataCell.Image = documentImage;
            }
            else
            {
                dataCell.Image = folderImage;
            }
            dataCell.TextImageRelation = TextImageRelation.ImageBeforeText;
        }
    }
    else
    {
        dataCell.ResetValue(LightVisualElement.ImageProperty, Telerik.WinControls.ValueResetFlags.Local);
        dataCell.ResetValue(LightVisualElement.ImageAlignmentProperty, Telerik.WinControls.ValueResetFlags.Local);
        dataCell.ResetValue(LightVisualElement.TextImageRelationProperty, Telerik.WinControls.ValueResetFlags.Local);
        dataCell.ResetValue(LightVisualElement.ImageLayoutProperty, Telerik.WinControls.ValueResetFlags.Local);
    }
}

' Getting the images from the resources of the project
Private documentImage As Image = SamplesVB.My.Resources.Document
Private folderImage As Image = SamplesVB.My.Resources.Folder
Private Sub radGridView1_CellFormatting(ByVal sender As Object, ByVal e As Telerik.WinControls.UI.CellFormattingEventArgs)
    Dim dataCell As GridDataCellElement = TryCast(e.CellElement, GridDataCellElement)
    If dataCell.ColumnInfo.Name = "Name" Then
        Dim dataRow As GridViewDataRowInfo = TryCast(dataCell.RowInfo, GridViewDataRowInfo)
        If dataRow IsNot Nothing Then
            dataCell.ImageAlignment = ContentAlignment.MiddleLeft
            Dim valueType As String = Convert.ToString(dataRow.Cells("FileSystemInfoType").Value).ToUpperInvariant()
            If valueType.Contains("FILE") Then
                dataCell.Image = documentImage
            Else
                dataCell.Image = folderImage
            End If
            dataCell.TextImageRelation = TextImageRelation.ImageBeforeText
        End If
    Else
        dataCell.ResetValue(LightVisualElement.ImageProperty, Telerik.WinControls.ValueResetFlags.Local)
        dataCell.ResetValue(LightVisualElement.ImageAlignmentProperty, Telerik.WinControls.ValueResetFlags.Local)
        dataCell.ResetValue(LightVisualElement.TextImageRelationProperty, Telerik.WinControls.ValueResetFlags.Local)
        dataCell.ResetValue(LightVisualElement.ImageLayoutProperty, Telerik.WinControls.ValueResetFlags.Local)
    End If
End Sub

It is possible to put the self-reference expander in any column by setting the SelfReferenceExpanderColumn property of the MasterTemplate:

gridview-hierarchical-grid-self-referencing-hierarchy 003

this.radGridView1.MasterTemplate.SelfReferenceExpanderColumn = this.radGridView1.Columns["ParentFolderId"];

Me.RadGridView1.MasterTemplate.SelfReferenceExpanderColumn = Me.RadGridView1.Columns("ParentFolderId")
'#End Region
End Sub
'#region fillingList
Private list As New BindingList(Of FileSystemItem)()
Private fileFolderIndex As Integer = 0
Public Sub GetFilesAndFolders(ByVal dir As String, ByVal parentId As Integer)
Dim di As New DirectoryInfo(dir)
Dim rgFiles() As FileInfo = di.GetFiles()
For Each fi As FileInfo In rgFiles
    fileFolderIndex += 1
    list.Add(New FileSystemItem(fileFolderIndex, "File", fi.Name, fi.CreationTime, parentId))
Next fi
Dim dirs() As DirectoryInfo = di.GetDirectories()
For Each d As DirectoryInfo In dirs
    fileFolderIndex += 1
    list.Add(New FileSystemItem(fileFolderIndex, "Folder", d.Name, d.CreationTime, parentId))
    GetFilesAndFolders(d.FullName, fileFolderIndex)
Next d
End Sub

note

You can use the TableElement.ShowSelfReferenceLines property to show lines that are connecting the parent/child rows.

See Also