wpf-9567-controls-and-libraries-data-grid-display-hierarchical-data-unbound-mode.md
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).
This example creates a TreeListView that operates in unbound mode without a data source.
View Example: Create an Unbound Tree
<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; }
}
Public Class ProjectObject
Public Property Name As String
Public Property Executor As String
End Class
Public Class StageObject
Inherits ProjectObject
End Class
Public Class TaskObject
Inherits ProjectObject
Public Property State As String
End Class
<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;
}
}
Public Partial Class MainWindow
Inherits Window
Public Sub New()
' ...
BuildTree();
End Sub
Private Sub BuildTree()
Dim rootNode As TreeListNode = CreateRootNode(New ProjectObject() With {.Name = "Project: Stanton", .Executor = "Nicholas Llams"})
Dim childNode As TreeListNode = CreateChildNode(rootNode, New StageObject() With {.Name = "Information Gathering", .Executor = "Ankie Galva"})
CreateChildNode(childNode, New TaskObject() With {.Name = "Design", .Executor = "Reardon Felton", .State = "In progress"})
End Sub
Private Function CreateRootNode(ByVal dataObject As Object) As TreeListNode
Dim rootNode As TreeListNode = New TreeListNode(dataObject)
Me.view.Nodes.Add(rootNode)
Return rootNode
End Function
Private Function CreateChildNode(ByVal parentNode As TreeListNode, ByVal dataObject As Object) As TreeListNode
Dim childNode As TreeListNode = New TreeListNode(dataObject)
parentNode.Nodes.Add(childNode)
Return childNode
End Function
End Class
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.
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;
}
}
Public Partial Class MainWindow
Inherits Window
Private Property Helper As FileSystemDataProvider
Public Sub New()
Me.InitializeComponent()
Helper = New FileSystemHelper()
InitDrives()
End Sub
Public Sub InitDrives()
Me.grid.BeginDataUpdate()
Try
Dim root As String() = Helper.GetLogicalDrives()
For Each s As String In root
Dim node As TreeListNode = New TreeListNode() With {.Content = New FileSystemItem(s, "Drive", "<Drive>", s)}
Me.view.Nodes.Add(node)
node.IsExpandButtonVisible = DefaultBoolean.True
Next
Catch
End Try
Me.grid.EndDataUpdate()
End Sub
Private Sub OnNodeExpanding(ByVal sender As Object, ByVal e As TreeList.TreeListNodeAllowEventArgs)
Dim node As TreeListNode = e.Node
If node.Tag Is Nothing OrElse CBool(node.Tag) = False Then
InitFolder(node)
node.Tag = True
End If
End Sub
Private Sub InitFolder(ByVal treeListNode As TreeListNode)
Me.grid.BeginDataUpdate()
InitFolders(treeListNode)
InitFiles(treeListNode)
Me.grid.EndDataUpdate()
End Sub
Private Sub InitFolders(ByVal treeListNode As TreeListNode)
Dim item As FileSystemItem = TryCast(treeListNode.Content, FileSystemItem)
If item Is Nothing Then Return
Try
Dim root As String() = Helper.GetDirectories(item.FullName)
For Each s As String In root
Try
Dim node As TreeListNode = New TreeListNode() With {.Content = New FileSystemItem(Helper.GetDirectoryName(s), "Folder", "<Folder>", s)}
treeListNode.Nodes.Add(node)
node.IsExpandButtonVisible = If(HasFiles(s), DefaultBoolean.True, DefaultBoolean.False)
Catch
End Try
Next
Catch
End Try
End Sub
Private Sub InitFiles(ByVal treeListNode As TreeListNode)
Dim item As FileSystemItem = TryCast(treeListNode.Content, FileSystemItem)
If item Is Nothing Then Return
Dim node As TreeListNode
Try
Dim root As String() = Helper.GetFiles(item.FullName)
For Each s As String In root
node = New TreeListNode() With {.Content = New FileSystemItem(Helper.GetFileName(s), "File", Helper.GetFileSize(s).ToString(), s)}
node.IsExpandButtonVisible = DefaultBoolean.False
treeListNode.Nodes.Add(node)
Next
Catch
End Try
End Sub
Private Function HasFiles(ByVal path As String) As Boolean
Dim root As String() = Helper.GetFiles(path)
If root.Length > 0 Then Return True
root = Helper.GetDirectories(path)
If root.Length > 0 Then Return True
Return False
End Function
End Class