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

Hierarchical Data

Updated over 6 months ago

RadVirtualGrid can display hierarchical, master-detail data to an arbitrary number of levels.

WinForms RadVirtualGrid Hierarchical Data

Before proceeding with this article, please refer to the Populating with Data article which demonstrates how to fill data in RadVirtualGrid.

In order to fill the grid with hierarchical data, you should follow the steps below:

1. Handle the CellValueNeeded event. You should specify the Value argument in the VirtualGridCellValueNeededEventArgs.

2. You will also need to set the RowCount and ColumnCount properties so that the grid will know how many rows/columns it needs to display.

3. Handle the QueryHasChildRows event which is fired for each row displayed in the associated VirtualGridViewInfo. Set the VirtualGridQueryHasChildRowsEventArgs.HasChildRows property to true to indicate that the row has child rows.

4. Subscribe to the RowExpanding event in order to specify the ColumnCount and RowCount properties of the ChildViewInfo.

The following example demonstrates how to setup the hierarchy in RadVirtualGrid by using the Northwind.Employees table:

Setup hierarchy

C#
        
List<Employee> data = new List<Employee>();
private void VirtualGridHierarchy_Load(object sender, EventArgs e)
{
    this.employeesTableAdapter.Fill(this.nwindDataSet.Employees);
    this.radVirtualGrid1.CellValueNeeded += radVirtualGrid1_CellValueNeeded;
    this.radVirtualGrid1.QueryHasChildRows += radVirtualGrid1_QueryHasChildRows;
    this.radVirtualGrid1.RowExpanding += radVirtualGrid1_RowExpanding;
    this.radVirtualGrid1.CellFormatting += radVirtualGrid1_CellFormatting;
    LoadData();
    this.radVirtualGrid1.TableElement.RowHeight = 120;
}
        
private void radVirtualGrid1_RowExpanding(object sender, VirtualGridRowExpandingEventArgs e)
{
    e.ChildViewInfo.ColumnCount = Sale.FieldNames.Length;
    e.ChildViewInfo.RowCount = data[e.ChildViewInfo.ParentRowIndex].Sales.Count;
}
        
private void radVirtualGrid1_CellFormatting(object sender, VirtualGridCellElementEventArgs e)
{
    //display the Employee's image
    if (e.CellElement.ColumnIndex < 0)
    {
        return;
    }
    
    if (e.CellElement.Value is Image)
    {
        e.CellElement.Image = (Image)e.CellElement.Value;
        e.CellElement.ImageLayout = ImageLayout.Zoom;
        e.CellElement.Text = "";
    }
    else
    {
        e.CellElement.ResetValue(LightVisualElement.ImageProperty, Telerik.WinControls.ValueResetFlags.Local);
    }
}
        
private void radVirtualGrid1_QueryHasChildRows(object sender, VirtualGridQueryHasChildRowsEventArgs e)
{
    e.HasChildRows = (e.ViewInfo == this.radVirtualGrid1.MasterViewInfo);
}
        
private void radVirtualGrid1_CellValueNeeded(object sender, VirtualGridCellValueNeededEventArgs e)
{
    if (e.ViewInfo == this.radVirtualGrid1.MasterViewInfo)
    {
        if (e.ColumnIndex < 0)
        {
            return;
        }
        
        e.FieldName = Employee.FieldNames[e.ColumnIndex];
        
        if (e.RowIndex == RadVirtualGrid.HeaderRowIndex)
        {
            e.Value = e.FieldName;
        }
        else if (e.RowIndex >= 0)
        {
            e.Value = data[e.RowIndex][e.ColumnIndex];
            if (e.ColumnIndex == 2)
            {
                e.FormatString = "${0:#,###}";
            }
            else if (e.ColumnIndex == 3)
            {
                e.FormatString = "{0:MM/dd/yy}";
            }
        }
    }
    else
    {
        if (e.ColumnIndex < 0)
        {
            return;
        }
        
        e.FieldName = Sale.FieldNames[e.ColumnIndex];
        
        if (e.RowIndex == RadVirtualGrid.HeaderRowIndex)
        {
            e.Value = e.FieldName;
        }
        else if (e.RowIndex >= 0)
        {
            e.Value = data[e.ViewInfo.ParentRowIndex].Sales[e.RowIndex][e.ColumnIndex];
            if (e.ColumnIndex == 1)
            {
                e.FormatString = "#{0}";
            }
            else if (e.ColumnIndex == 3)
            {
                e.FormatString = "{0:F2}%";
            }
            else if (e.ColumnIndex == 4)
            {
                e.FormatString = "${0}";
            }
        }
    }
}
        
private void LoadData()
{
    Random random = new Random();
    for (int i = 0; i < this.nwindDataSet.Employees.Count; i++)
    {
        DataSources.NwindDataSet.EmployeesRow row = this.nwindDataSet.Employees[i];
        Employee employee = new Employee();
        employee.Name = row.FirstName + " " + row.LastName;
        employee.Photo = GetImageFromBytes(row.Photo);
        employee.Salary = random.Next(45000);
        employee.HireDate = row.HireDate;
        employee.Title = row.Title;
        int rowCount = random.Next(20) + 1;
        for (int j = 0; j < rowCount; j++)
        {
            employee.Sales.Add(new Sale()
            {
                Name = employee.Name, ProductNumber = random.Next(1000),
                Quantity = random.Next(50), Discount = random.Next(100), Total = random.Next(10000)
            });
        }
        data.Add(employee);
    }
    this.radVirtualGrid1.RowCount = data.Count;
    this.radVirtualGrid1.ColumnCount = Employee.FieldNames.Length;
}
        
private Image GetImageFromBytes(byte[] bytes)
{
    Image result = null;
    MemoryStream stream = null;
    
    try
    {
        stream = new MemoryStream(bytes, 78, bytes.Length - 78);
        result = Image.FromStream(stream);
    }
    catch
    {
        try
        {
            stream = new MemoryStream(bytes, 0, bytes.Length);
            result = Image.FromStream(stream);
        }
        catch
        {
            result = null;
        }
    }
    finally
    {
        if (stream != null)
            stream.Close();
    }
    
    return result;
}

Employee and Sale classes implementation

C#
        
public class Employee
{
    public static readonly string[] FieldNames = { "Photo", "Name", "Salary", "HireDate", "Title" };
    public Image Photo { get; set; }
    public string Name { get; set; }
    public decimal Salary { get; set; }
    public DateTime HireDate { get; set; }
    public string Title { get; set; }
    public List<Sale> Sales { get; private set; }
    
    public object this[int index]
    {
        get
        {
            switch (index)
            {
                case 0:
                    return Photo;
                case 1:
                    return Name;
                case 2:
                    return Salary;
                case 3:
                    return HireDate;
                case 4:
                    return Title;
                default:
                    return null;
            }
        }
    }
    
    public Employee()
    {
        Sales = new List<Sale>();
    }
}
        
public class Sale
{
    public static readonly string[] FieldNames = { "Name", "ProductNumber", "Quantity", "Discount", "Total" };
    public string Name { get; set; }
    public int ProductNumber { get; set; }
    public int Quantity { get; set; }
    public int Discount { get; set; }
    public int Total { get; set; }
    
    public object this[int index]
    {
        get
        {
            switch (index)
            {
                case 0:
                    return Name;
                case 1:
                    return ProductNumber;
                case 2:
                    return Quantity;
                case 3:
                    return Discount;
                case 4:
                    return Total;
                default:
                    return null;
            }
        }
    }
}
Not finding the help you need?
Contact Support