Skip to main content

Unbound Mode

  • 6 minutes to read

Nodes are stored as nested collections because the TreeListView displays data in a tree. The collection of root level nodes can be accessed via the TreeListView.Nodes property. Each node has its own collection of child nodes available via the TreeListNode.Nodes property. These child nodes have their own children, etc.

In an unbound mode, you should manually build a TREE by creating nodes (TreeListNode) and adding them to the corresponding node collections.

Note

Nodes can be represented by objects of different types. The only requirement is that these data objects should have common fields (columns).

Build a Tree in Unbound Mode

This example creates a TreeListView that operates in unbound mode without a data source.

Tree List - Unbound Mode

View Example: Create an Unbound Tree

In XAML

<dxg:GridControl>
    <dxg:GridColumn FieldName="Name"/>
    <dxg:GridColumn FieldName="Executor"/>
    <dxg:GridColumn FieldName="State"/>
    <dxg:GridControl.View>
        <dxg:TreeListView x:Name="view" AutoWidth="True" AutoExpandAllNodes="True">
            <dxg:TreeListView.Nodes>
                <dxg:TreeListNode>
                    <dxg:TreeListNode.Content>
                        <local:ProjectObject Name="Project: Betaron" Executor="Destiny Tabisola"/>
                    </dxg:TreeListNode.Content>
                    <dxg:TreeListNode.Nodes>
                        <dxg:TreeListNode>
                            <dxg:TreeListNode.Content>
                                <local:StageObject Name="Development" Executor="Kairra Hogg"/>
                            </dxg:TreeListNode.Content>
                            <dxg:TreeListNode.Nodes>
                                <dxg:TreeListNode>
                                    <dxg:TreeListNode.Content>
                                        <local:TaskObject Name="Coding" Executor="Sabato Durley" State="Not Started"/>
                                    </dxg:TreeListNode.Content>
                                </dxg:TreeListNode>
                            </dxg:TreeListNode.Nodes>
                        </dxg:TreeListNode>
                    </dxg:TreeListNode.Nodes>
                </dxg:TreeListNode>
            </dxg:TreeListView.Nodes>
        </dxg:TreeListView>
    </dxg:GridControl.View>
</dxg:GridControl>
public class ProjectObject {
    public string Name { get; set; }
    public string Executor { get; set; }
}

public class StageObject : ProjectObject { }

public class TaskObject : ProjectObject {
    public string State { get; set; }
}

In Code

<dxg:GridControl>
    <dxg:GridColumn FieldName="Name"/>
    <dxg:GridColumn FieldName="Executor"/>
    <dxg:GridColumn FieldName="State"/>
    <dxg:GridControl.View>
        <dxg:TreeListView x:Name="view" AutoWidth="True" AutoExpandAllNodes="True"/>
    </dxg:GridControl.View>
</dxg:GridControl>
public partial class MainWindow : Window {
    public MainWindow() {
        // ...
        BuildTree();
    }

    private void BuildTree() {
        TreeListNode rootNode = CreateRootNode(new ProjectObject() { Name = "Project: Stanton", Executor = "Nicholas Llams" });
        TreeListNode childNode = CreateChildNode(rootNode, new StageObject() { Name = "Information Gathering", Executor = "Ankie Galva" });
        CreateChildNode(childNode, new TaskObject() { Name = "Design", Executor = "Reardon Felton", State = "In progress" });
    }

    private TreeListNode CreateRootNode(object dataObject) {
        TreeListNode rootNode = new TreeListNode(dataObject);
        view.Nodes.Add(rootNode);
        return rootNode;
    }

    private TreeListNode CreateChildNode(TreeListNode parentNode, object dataObject) {
        TreeListNode childNode = new TreeListNode(dataObject);
        parentNode.Nodes.Add(childNode);
        return childNode;
    }
}

Dynamic Data Loading

In an unbound mode, you need to manually create a tree (in code or XAML). A tree can be created on demand. Child nodes are dynamically created and initialized when their parent node is expanded.

To implement on demand node loading, handle the TreeListView.NodeExpanding event. This event occurs before a node is expanded, allowing you to dynamically populate its collection of child nodes. The processed node is returned by the event parameter’s TreeListNodeEventArgs.Node property.

Note

When expanding a node, you do not know whether it has child nodes or not. If the node has no child nodes, hide the expand button by setting the TreeListNode.IsExpandButtonVisible property to false.

Load Data On-Demand

In this example, the TreeListView displays the file/folder tree. The TreeListView.NodeExpanding event allows you to create child nodes dynamically when a user expands a parent node.

View Example: Load Nodes Dynamically

<dxg:GridControl x:Name="grid">
    <dxg:GridControl.Columns>
        <dxg:GridColumn FieldName="Name"/>
        <dxg:GridColumn FieldName="ItemType"/>
        <dxg:GridColumn FieldName="Size">
            <dxg:GridColumn.EditSettings>
                <dxe:TextEditSettings HorizontalContentAlignment="Right"/>
            </dxg:GridColumn.EditSettings>
        </dxg:GridColumn>
        <dxg:GridColumn FieldName="FullName"/>
    </dxg:GridControl.Columns>
    <dxg:GridControl.View>
        <dxg:TreeListView x:Name="view" 
                          AllowEditing="False"
                          AutoWidth="True"
                          NodeExpanding="OnNodeExpanding"/>
    </dxg:GridControl.View>
</dxg:GridControl>
public partial class MainWindow : Window {
    FileSystemDataProvider Helper { get; set; }
    public MainWindow() {
        InitializeComponent();
        Helper = new FileSystemHelper();
        InitDrives();
    }
    public void InitDrives() {
        grid.BeginDataUpdate();
        try {
            string[] root = Helper.GetLogicalDrives();

            foreach (string s in root) {
                TreeListNode node = new TreeListNode() { Content = new FileSystemItem(s, "Drive", "<Drive>", s) };
                view.Nodes.Add(node);
                node.IsExpandButtonVisible = DefaultBoolean.True;
            }
        }
        catch { }
        grid.EndDataUpdate();
    }

    private void OnNodeExpanding(object sender, DevExpress.Xpf.Grid.TreeList.TreeListNodeAllowEventArgs e) {
        TreeListNode node = e.Node;
        if (node.Tag == null || (bool)node.Tag == false) {
            InitFolder(node);
            node.Tag = true;
        }
    }

    private void InitFolder(TreeListNode treeListNode) {
        grid.BeginDataUpdate();
        InitFolders(treeListNode);
        InitFiles(treeListNode);
        grid.EndDataUpdate();
    }

    private void InitFolders(TreeListNode treeListNode) {
        FileSystemItem item = treeListNode.Content as FileSystemItem;
        if (item == null) return;

        try {
            string[] root = Helper.GetDirectories(item.FullName);
            foreach (string s in root) {
                try {
                    TreeListNode node = new TreeListNode() { Content = new FileSystemItem(Helper.GetDirectoryName(s), "Folder", "<Folder>", s) };
                    treeListNode.Nodes.Add(node);

                    node.IsExpandButtonVisible = HasFiles(s) ? DefaultBoolean.True : DefaultBoolean.False;
                }
                catch { }
            }
        }
        catch { }
    }

    private void InitFiles(TreeListNode treeListNode) {
        FileSystemItem item = treeListNode.Content as FileSystemItem;
        if (item == null) return;
        TreeListNode node;
        try {
            string[] root = Helper.GetFiles(item.FullName);
            foreach (string s in root) {
                node = new TreeListNode() { Content = new FileSystemItem(Helper.GetFileName(s), "File", Helper.GetFileSize(s).ToString(), s) };
                node.IsExpandButtonVisible = DefaultBoolean.False;
                treeListNode.Nodes.Add(node);
            }
        }
        catch { }
    }

    private bool HasFiles(string path) {
        string[] root = Helper.GetFiles(path);
        if (root.Length > 0) return true;
        root = Helper.GetDirectories(path);
        if (root.Length > 0) return true;
        return false;
    }
}