One of the unique and popular features of the FlexGrid control is the ability to add hierarchical grouping to regular data grid and display it in a tree like structure called Tree Grid. The Tree Grid control is very similar to the one you see in a TreeView control. It shows an indented structure with collapse or expand icon next to each node row so the user can expand and collapse the outline to see the desired level of detail by clicking on the nodes.
Tree Grid helps you implement more advanced use cases such as displaying customer and order details. In a usual grid showing such data, it is difficult to see the details of each customer and order. In such case, you can create a Tree Grid to group the data in a hierarchical structure for better view and accessibility of information.
FlexGrid provides ChildItemsPath property that allows you to create a hierarchical structure of data to create a collapsible tree structure. It also provides the following properties based on TreeView object model, which can be used to manage TreeGrid.
Properties | Description |
---|---|
TreeExpandMode | Indicates how the tree is loaded and displayed in the grid. By default, this property has the GridTreeExpandMode.OnDemand value, meaning the tree is initially collapsed, and the subtrees are loaded on demand as the parent’s get expanded. This is the fastest mode to load, and is very useful for scenarios where the ChildItemsPath property is pointing to a late-evaluated property or huge trees. For other scenarios it can be useful or required the tree to be expanded from scratch. To make this you can simply set TreeExpandMode to Expanded mode and the whole tree is evaluated at loading time and all its nodes are expanded by default. Similarly, there is the Collapsed mode, which evaluates the whole tree and creates all the rows at loading time, but it shows all the nodes of the tree collapsed. |
TreeColumnIndex | Allows you to specify the index of the column that shows the toggle buttons. By default it will be zero, meaning the first column will show the buttons and -1 can be set to avoid showing the buttons. |
TreeIndent | Enables you to specify the length of the indentation displayed in the cells whose columns correspond to the TreeColumnIndex. |
Now, let us create a hierarchical structure of tasks, where each task can have sub-tasks, allowing for a detailed breakdown of project work. This kind of organization is common in project management and software development to manage complex projects effectively. To implement TreeGrid in FlexGrid control, perform the following steps:
ProjectTask.cs |
Copy Code
|
---|---|
public class ProjectTask : INotifyPropertyChanged { private bool _isExpanded = true; public ProjectTask() { SubTasks = new List<ProjectTask>(); IsExpanded = true; } public string WBS { get; set; } [Display(Name = "Task Name")] public string Name { get; set; } public TimeSpan Duration { get; set; } public DateTime Start { get; set; } public DateTime Finish { get { return Start + Duration; } } [Display(AutoGenerateField = false)] public ProjectTask ParentTask { get; set; } [Display(AutoGenerateField = false)] public List<ProjectTask> SubTasks { get; set; } [Display(AutoGenerateField = false)] public bool IsExpanded { get { return _isExpanded; } set { _isExpanded = value; OnPropertyChanged(); OnIsVisibleChanged(); } } private void OnIsVisibleChanged() { OnPropertyChanged("IsVisible"); foreach (var subTask in SubTasks) { subTask.OnIsVisibleChanged(); } } [Display(AutoGenerateField = false)] public bool IsVisible { get { if (ParentTask == null) { return true; } else if (!ParentTask.IsExpanded) { return false; } else { return ParentTask.IsVisible; } } } [Display(AutoGenerateField = false)] public int Level { get { if (ParentTask == null) { return 0; } else { return ParentTask.Level + 1; } } } private void OnPropertyChanged([CallerMemberName] string propertyName = null) { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } } public event PropertyChangedEventHandler PropertyChanged; } |
Home.razor |
Copy Code
|
---|---|
@code { FlexGrid grid; List<ProjectTask> tasks; protected override async Task OnInitializedAsync() { //create tasks var task1 = new ProjectTask() { WBS = "1", Name = "Requirements", Duration = new TimeSpan(50, 0, 0, 0), Start = new DateTime(2009, 12, 4) }; var task11 = new ProjectTask() { WBS = "1.1", Name = "Analysis", Duration = new TimeSpan(38, 3, 0, 0), Start = new DateTime(2009, 12, 4), ParentTask = task1 }; var task111 = new ProjectTask() { WBS = "1.1.1", Name = "Analyze online reservations", Duration = new TimeSpan(12, 12, 0, 0), Start = new DateTime(2009, 12, 4), ParentTask = task11 }; var task112 = new ProjectTask() { WBS = "1.1.2", Name = "Analyze query processes", Duration = new TimeSpan(12, 12, 0, 0), Start = new DateTime(2009, 12, 4), ParentTask = task11 }; var task113 = new ProjectTask() { WBS = "1.1.3", Name = "Analyze multimedia enhancements", Duration = new TimeSpan(12, 12, 0, 0), Start = new DateTime(2010, 1, 4), ParentTask = task11 }; var task114 = new ProjectTask() { WBS = "1.1.4", Name = "Draft Preliminary requirements", Duration = new TimeSpan(5, 0, 0, 0), Start = new DateTime(2010, 1, 14), ParentTask = task11 }; var task115 = new ProjectTask() { WBS = "1.1.5", Name = "Review preliminary requirements", Duration = new TimeSpan(2, 12, 0, 0), Start = new DateTime(2010, 1, 14), ParentTask = task11 }; task11.SubTasks = new List<ProjectTask> { task111, task112, task113, task114, task115 }; var task12 = new ProjectTask() { WBS = "1.2", Name = "Acceptance Test Plan", Duration = new TimeSpan(12, 3, 0, 0), Start = new DateTime(2010, 6, 23), ParentTask = task1 }; var task121 = new ProjectTask() { WBS = "1.2.1", Name = "Write acceptance test plans", Duration = new TimeSpan(5, 2, 0, 0), Start = new DateTime(2010, 6, 23), ParentTask = task12 }; var task122 = new ProjectTask() { WBS = "1.2.2", Name = "Draft acceptance test plan", Duration = new TimeSpan(5, 0, 0, 0), Start = new DateTime(2010, 6, 23), ParentTask = task12 }; task12.SubTasks = new List<ProjectTask> { task121, task122 }; task1.SubTasks = new List<ProjectTask> { task11, task12 }; var task2 = new ProjectTask() { WBS = "2", Name = "Design", Duration = new TimeSpan(55, 0, 0, 0), Start = new DateTime(2010, 8, 12) }; var task21 = new ProjectTask() { WBS = "2.1", Name = "Top-level Design", Duration = new TimeSpan(27, 12, 0, 0), Start = new DateTime(2010, 8, 12), ParentTask = task2 }; var task211 = new ProjectTask() { WBS = "2.1.1", Name = "Design online reservations", Duration = new TimeSpan(10, 0, 0, 0), Start = new DateTime(2010, 9, 7), ParentTask = task21 }; var task212 = new ProjectTask() { WBS = "2.1.2", Name = "Design query processes", Duration = new TimeSpan(10, 12, 0, 0), Start = new DateTime(2010, 9, 14), ParentTask = task21 }; var task213 = new ProjectTask() { WBS = "2.1.3", Name = "Design multimedia enhancements", Duration = new TimeSpan(10, 6, 0, 0), Start = new DateTime(2010, 10, 4), ParentTask = task21 }; var task214 = new ProjectTask() { WBS = "2.1.4", Name = "Review design specification", Duration = new TimeSpan(5, 12, 0, 0), Start = new DateTime(2010, 10, 9), ParentTask = task21 }; task21.SubTasks = new List<ProjectTask> { task211, task212, task213, task214 }; var task22 = new ProjectTask() { WBS = "2.2", Name = "Detailed Design", Duration = new TimeSpan(23, 0, 0, 0), Start = new DateTime(2010, 12, 4), ParentTask = task2 }; var task221 = new ProjectTask() { WBS = "2.2.1", Name = "Draft design specifications", Duration = new TimeSpan(17, 12, 0, 0), Start = new DateTime(2010, 12, 4), ParentTask = task22 }; var task222 = new ProjectTask() { WBS = "2.2.2", Name = "Review design specifications", Duration = new TimeSpan(17, 0, 0, 0), Start = new DateTime(2010, 12, 8), ParentTask = task22 }; var task223 = new ProjectTask() { WBS = "2.2.3", Name = "Incorporate feedback on design specifications", Duration = new TimeSpan(17, 6, 0, 0), Start = new DateTime(2010, 12, 14), ParentTask = task22 }; task22.SubTasks = new List<ProjectTask> { task221, task222, task223 }; task2.SubTasks = new List<ProjectTask> { task21, task22 }; var task3 = new ProjectTask() { WBS = "3", Name = "Code and Unit Test", Duration = new TimeSpan(32, 4, 0, 0), Start = new DateTime(2010, 12, 4) }; var task31 = new ProjectTask() { WBS = "3.1", Name = "Assign development staff", Duration = new TimeSpan(2, 3, 0, 0), Start = new DateTime(2010, 12, 4), ParentTask = task3 }; var task32 = new ProjectTask() { WBS = "3.2", Name = "Develop Code", Duration = new TimeSpan(10, 3, 0, 0), Start = new DateTime(2010, 12, 4), ParentTask = task3 }; var task321 = new ProjectTask() { WBS = "3.2.1", Name = "Develop online reservations", Duration = new TimeSpan(10, 2, 0, 0), Start = new DateTime(2010, 12, 4), ParentTask = task32 }; var task322 = new ProjectTask() { WBS = "3.2.2", Name = "Test online reservations", Duration = new TimeSpan(1, 11, 0, 0), Start = new DateTime(2010, 12, 4), ParentTask = task32 }; var task323 = new ProjectTask() { WBS = "3.2.3", Name = "Develop query processes", Duration = new TimeSpan(10, 0, 0, 0), Start = new DateTime(2010, 12, 4), ParentTask = task32 }; var task324 = new ProjectTask() { WBS = "3.2.4", Name = "Test query processes", Duration = new TimeSpan(1, 4, 0, 0), Start = new DateTime(2010, 12, 4), ParentTask = task32 }; task32.SubTasks = new List<ProjectTask> { task321, task322, task323, task324 }; task3.SubTasks = new List<ProjectTask> { task31, task32 }; tasks = new List<ProjectTask> { task1, task2, task3 }; } |
Home.razor |
Copy Code
|
---|---|
private void OnAutoGeneratingColumn(object sender, C1.Blazor.Grid.GridAutoGeneratingColumnEventArgs e) { if (e.Property.Name == nameof(ProjectTask.Name)) { e.Column.AllowResizing = false; e.Column.MinWidth = 300; e.Column.Width = new GridLength(1, GridUnitType.Star); } if (e.Property.Name == nameof(ProjectTask.WBS)) e.Column.Width = 90; if (e.Property.Name == nameof(ProjectTask.Duration)) e.Column.ValueConverter = DelegateConverter.Create( (value, type, parameter, culture) => string.Format("{0:N1} days?", ((TimeSpan)value).TotalDays), (value, type, parameter, culture) => { var str = value?.ToString() ?? ""; TimeSpan timeSpan; if (TimeSpan.TryParse(str, out timeSpan)) return timeSpan; if (str.EndsWith(" days?")) str = str.Substring(0, str.Length - " days?".Length); double totalDays; if (double.TryParse(str, out totalDays)) return TimeSpan.FromDays(totalDays); return TimeSpan.Zero; }); if (e.Column is GridDateTimeColumn) e.Column.Format = "ddd d/M/yyyy"; } } |
Home.razor |
Copy Code
|
---|---|
@using C1.Blazor.Grid @using C1.Blazor.Core @using FlexGrid_Tree.Data <FlexGrid @ref="grid" ChildItemsPath="SubTasks" TreeIndent="14" TreeColumnIndex="1" TreeExpandMode="GridTreeExpandMode.OnDemand" AutoGeneratingColumn="@OnAutoGeneratingColumn" ItemsSource="tasks" Style="@("max-height:150vh")" /> |