MAUI | ComponentOne
Controls / FlexGrid / TreeGrid
In This Topic
    TreeGrid
    In This Topic

    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 TreeGrid. TreeGrid is quite similar to the 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.

    TreeGrid helps you implement more advanced use cases such as displaying hierarchical structure of tasks, where each task can have sub-tasks, allowing a detailed breakdown of project work. This kind of organization is common in project management and software development to manage complex projects effectively.

    Treegrid in FlexGrid

    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.

    To implement TreeGrid in a FlexGrid control, perform the following steps:

    1. Create a class named ProjectTask and define the following properties for the grid columns:
      C#
      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;
                  }
              }
          }
      
          #region INotifyPropertyChanged Members
      
          private void OnPropertyChanged([CallerMemberName] string propertyName = null)
          {
              if (PropertyChanged != null)
              {
                  PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
              }
          }
      
          public event PropertyChangedEventHandler PropertyChanged;
      
    2. Create a collection with the name tasks which contains objects of type ProjectTask class. In the collection, task1 represents the top-level or parent task in the project, focusing on requirements and task11 represents a sub-task of task1. Similarly, task111, task112, task113, task114, task115 tasks further represent sub-tasks of task11. You can relate these sub tasks to their immediate parent task using the SubTasks property. Finally, relate all the sub tasks created with top level parent task, i.e., tasks, to complete the tree format and display these tasks in the FlexGrid control. Then, bind tasks with FlexGird using its ItemsSource property as shown in the following code snippet:
      C#
      Copy Code
      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 };
      task21.SubTasks = new List<ProjectTask> { task211, task212, task213 };
      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 };
      task22.SubTasks = new List<ProjectTask> { task221, task222 };
      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 };
      var tasks = new List<ProjectTask> { task1, task2, task3 };
      
      grid.ItemsSource = tasks;
      grid.CollapseGroups(3);
      
    3. Create a method with the name OnAutoGeneratingColumn to handle the configuration of columns in a grid used for displaying ProjectTask data. For example, the first property configured in the following code is WBS, which sets the width of the column to 90. Next, we configures the property named Duration, which sets up a value converter for the column. This converter formats TimeSpan values into a string representation of days with one decimal place and adds a question mark. It also converts back from strings to TimeSpan values. Finally, we add the column of type GridDateTimeColumn, which sets the date format to "ddd d/M/yyyy", for displaying dates in a specific format.
      C#
      Copy Code
      private void OnAutoGeneratingColumn(object sender, C1.Maui.Grid.GridAutoGeneratingColumnEventArgs e)
      {
          if (e.Property.Name == "Name")
          {
              e.Column.AllowResizing = false;
              e.Column.MinWidth = 300;
              e.Column.Width = new GridLength(1, GridUnitType.Star);
          }
          if (e.Property.Name == "WBS")
              e.Column.Width = GridLength.Auto;
          if (e.Column is GridDateTimeColumn)
              e.Column.Format = "ddd d/M/yyyy";
      }
      
    4. Switch to the XAML view and add the following code to set the ChildItemsPath property of the FlexGrid class to SubTasks property that generates the child rows or sub tasks.
      XAML
      Copy Code
      <c1:FlexGrid x:Name="grid" AutoGeneratingColumn="OnAutoGeneratingColumn" AllowFiltering="False"
                   AllowSorting="False" IsReadOnly="True" ChildItemsPath="SubTasks" TreeColumnIndex="1"/>