Skip to main content

DevExpress v24.2 Update — Your Feedback Matters

Our What's New in v24.2 webpage includes product-specific surveys. Your response to our survey questions will help us measure product satisfaction for features released in this major update and help us refine our plans for our next major release.

Take the survey Not interested

Display a Tree List using the ITreeNode Interface

  • 7 minutes to read

To display data in a tree-like structure, implement the ITreeNode interface in the corresponding business classes. XAF uses the DxTreeList, ASPxTreeList, and TreeList controls provided by the TreeList Editors module and DevExpress.ExpressApp.Blazor base module to display objects that support ITreeNode.

For more information about the ITreeNode interface, refer to the following topic: TreeList Editors Module Overview.

This topic demonstrates the implementation of the ITreeNode interface.

Tip

A complete sample project is available in the DevExpress Code Examples database at https://supportcenter.devexpress.com/ticket/details/e1125/xaf-winforms-how-to-use-tree-list-editors-to-display-list-views.

DxTreeListEditor ships with the base DevExpress.ExpressApp.Blazor module. You do not need to add a module to an existing XAF ASP.NET Core Blazor application to start using it.

Follow instructions in this example to implement the following tree structure:

CategoryTreeList

CategoryTreeList Legend

You need the following classes:

  • ProjectGroup
  • Project
  • ProjectArea

Derive these classes from an abstract class that implements the ITreeNode interface. To display a tree of these objects, add an item that corresponds to that abstract class to the navigation control. When you click this item, XAF organizes all objects of the types derived from the abstract class in a tree via the Tree List editors (you need to add the TreeList Editors module to the application).

Note

Business classes you implement in this example must meet the following requirements:

  • The parent and child classes corresponding to tree list nodes should be derived from the same persistent class that implements the ITreeNode interface. In this example, the ProjectGroup, Project, and ProjectArea classes are derived from the Category class.
  • It is impossible to have more than one root class in a Tree List. In this example, the ProjectGroup is the root class.
  1. Implement an abstract Category class as the following code snippet demonstrates:

    using DevExpress.Persistent.Base;
    using DevExpress.Persistent.Base.General;
    using DevExpress.Persistent.BaseImpl.EF;
    using System.ComponentModel;
    
    namespace MySolution.Module.BusinessObjects;
    [NavigationItem("Category")]
    public abstract class Category : BaseObject, ITreeNode {
        protected abstract ITreeNode Parent {
            get;
        }
        protected abstract IBindingList Children {
            get;
        }
        public virtual string Name { get; set; }
        #region ITreeNode
        IBindingList ITreeNode.Children {
            get {
                return Children;
            }
        }
        string ITreeNode.Name {
            get {
                return Name;
            }
        }
        ITreeNode ITreeNode.Parent {
            get {
                return Parent;
            }
        }
        #endregion
    }
    // Make sure that you use options.UseChangeTrackingProxies() in your DbContext settings.
    

    Apply the NavigationItemAttribute to add the corresponding navigation item to the navigation control. This overrides the Parent and Children properties in the descendant classes to return objects of the required types.

  2. Implement the ProjectGroup, Project, and ProjectArea classes. Derive these classes from the Category class.

    Entity Framework Core classes:

    using DevExpress.Persistent.Base;
    using DevExpress.Persistent.Base.General;
    using System.Collections.ObjectModel;
    using System.ComponentModel;
    
    namespace MySolution.Module.BusinessObjects;
    [DefaultClassOptions]
    public class ProjectGroup : Category {
        public ProjectGroup() {
            projects.CollectionChanged += (s, e) => children?.ResetBindings();
        }
        ObservableCollection<Project> projects = new ObservableCollection<Project>();
        protected override ITreeNode Parent {
            get {
                return null;
            }
        }
        protected override IBindingList Children {
            get {
                if(children == null) {
                    children = new BindingList<Project>(Projects);
                }
                return children;
            }
        }
        BindingList<Project> children;
        [DevExpress.ExpressApp.DC.Aggregated]
        public virtual ObservableCollection<Project> Projects { get => projects; }
    }
    
    // Make sure that you use options.UseChangeTrackingProxies() in your DbContext settings.
    

    XPO classes:

    using DevExpress.Persistent.Base.General;
    using DevExpress.Xpo;
    using System.ComponentModel;
    
    namespace MySolution.Module.BusinessObjects;
    [System.Diagnostics.CodeAnalysis.SuppressMessage("Design",
    "XAF0002:XPO business class properties should not be overriden",
    Justification = "")]
    public class ProjectGroup : Category {
        protected override ITreeNode Parent {
            get {
                return null;
            }
        }
        protected override IBindingList Children {
            get {
                return Projects;
            }
        }
        public ProjectGroup(Session session) : base(session) { }
        public ProjectGroup(Session session, string name) : base(session) {
            this.Name = name;
        }
        [Association("ProjectGroup-Projects"), Aggregated]
        public XPCollection<Project> Projects {
            get {
                return GetCollection<Project>(nameof(Projects));
            }
        }
    }
    

    Tip

    If your application uses Entity Framework Core, register new classes in your application’s DBContext:

    C#
    using MySolution.Module.BusinessObjects;
    
    namespace  MySolution.Module.BusinessObjects;
    public class MySolutionEFCoreDbContext : DbContext {
        //...
        public DbSet<Category> Categories { get; set; }
        public DbSet<Project> Projects { get; set; }
        public DbSet<ProjectArea> ProjectAreas { get; set; }
        public DbSet<ProjectGroup> ProjectGroups { get; set; }
    }
    
  3. In a Windows Forms or an ASP.NET Web Forms project, add the TreeList Editors module as described in the following topic: TreeList Editors Module Overview.

  4. Run the application. Click the Category item in the navigation control and use the New Action to create ProjectGroup, Project, and ProjectArea objects. The Category List View displays these objects as a hierarchical tree:
ASP.NET Core Blazor
XAF ASP.NET Core Blazor Tree List for ITreeNode, DevExpress
Windows Forms
XAF Windows Forms Tree List for ITreeNode, DevExpress

Note

In Windows Forms applications, Tree List nodes can have associated images. Tree List Editors display these images within the nodes. For more information, refer to the following topic: Node Images in a Tree List. In Windows Forms applications, you can also display a collection of items for each category node to the right of the Tree List. For more information, refer to the following topic: Categorized List (Windows Forms).

If you do not require the hard-coded structure as demonstrated in this example, use the HCategory class that implements the ITreeNode interface out of the box. For more information, refer to the following topic: Display a Tree List using the HCategory Class.