Skip to main content Skip to footer

Hierarchical C1TreeView from Self-referential DataSource

As it appears, this is among one of the most common discussions on web, so thought of writing something related to C1TreeView. C1TreeView is a powerful control and when nested child-parent relationships are to be represented, it best fits the scenario. Few nested levels(up to 2-3) are easy to implement but what if there are several nested levels, as in windows explorer. The answer is to use self-referential data source and let C1TreeView handle the rest. DataTable appears to be the simplest and easiest self-referential data source, so lets use it as C1TreeView ItemsSource. For WPF, we can use the native DataTable object and set the child-parent relationship on the dataset. The next code snippet creates a basic dataset with parent-child relationship.


internal static int GetData()  
{  
    DataTable dt = new DataTable("data");  
    dt.Columns.Add("Id", typeof(int));  
    dt.Columns.Add("ParentId", typeof(int));  
    dt.Columns.Add("NodeDescription");  
    dt.Rows.Add(1, null, "Africa");  
    dt.Rows.Add(2, null, "Asia");  
    dt.Rows.Add(3, null, "Europe");  
    dt.Rows.Add(4, null, "North America");  
    dt.Rows.Add(5, null, "South America");  
    dt.Rows.Add(6, 1, "China");  
    dt.Rows.Add(7, 1, "Japan");  
    dt.Rows.Add(8, 1, "India");  
    dt.Rows.Add(9, 1, "South Korea");  
    dt.Rows.Add(10, 1, "Russia");  
    dt.Rows.Add(11, 2, "Algeria");  
    dt.Rows.Add(12, 2, "Nigeria");  
    dt.Rows.Add(13, 2, "Egypt");  
    dt.Rows.Add(14, 2, "South Africa");  
    dt.Rows.Add(15, 2, "Kenya");  
    dt.Rows.Add(16, 3, "Austria");  
    dt.Rows.Add(17, 3, "England");  
    dt.Rows.Add(18, 3, "France");  
    dt.Rows.Add(19, 3, "Germay");  
    dt.Rows.Add(20, 3, "Spain");  
    dt.Rows.Add(21, 4, "Cuba");  
    dt.Rows.Add(22, 4, "Canada");  
    dt.Rows.Add(23, 4, "Greenland");  
    dt.Rows.Add(24, 4, "Trinidad and Tobago");  
    dt.Rows.Add(25, 4, "United States");  
    dt.Rows.Add(26, 5, "Argentina");  
    dt.Rows.Add(27, 5, "Brazil");  
    dt.Rows.Add(28, 5, "Chile");  
    dt.Rows.Add(29, 5, "Paraguay");  
    dt.Rows.Add(30, 5, "Venezuela");  

    DataSet ds = new DataSet();  
    ds.Tables.Add(dt);  
    ds.Relations.Add(“rsParentChild” ,ds.Tables["data"].Columns["Id"], ds.Tables["data"].Columns["ParentId"]);  
    return ds;  
}  

Now, the parent nodes in this case will be the nodes which don't have any children, i.e their parent is null. So, the ItemsSource for the C1Treeview will be a DataView with a RowFilter (parent=null). Lets expose a property of type DataView on the window and use it for C1TreeView binding purpose.


private DataView _rootNodes;  
public DataView RootNodes  
{  
    get { return _rootNodes; }  
}  

public MainWindow()  
{  
    InitializeComponent();  
    var dataSet = GetData();  
    _rootNodes = dataSet.Tables["data"].DefaultView;  
    _rootNodes.RowFilter = "ParentId IS NULL";  
    this.DataContext = this;  
}  

The xaml code is below:


<Grid  Background="White">  
    <c1:C1TreeView ItemsSource="{Binding RootNodes}">  
        <c1:C1TreeView.ItemTemplate>  
            <c1:C1HierarchicalDataTemplate ItemsSource="{Binding rsParentChild}">  
                <TextBlock Text="{Binding NodeDescription}" />  
            </c1:C1HierarchicalDataTemplate>  
        </c1:C1TreeView.ItemTemplate>  
    </c1:C1TreeView>  

When you run the application, you will see the following output:- The sample shows only 2 nested levels but this can be easily modified for 'n' number of nested parent-child relationship. You just have to edit the datatable rows. That was it for WPF. Lets talk about Silverlight now. Can we achieve something similar with Silverlight C1TreeView? The answer is a big YES. Thanks to the C1Data class which implements native .NET DataSet, DataTable etc. classes for Silverlight. We can use it for creating a self-referential data source in Silverlight Most for the code would be same and its just the child nodes binding that needs to be changed. You actually have to use a converter which returns all child nodes using DataRelation.


public class NodeConverter : IValueConverter  
{  
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)  
    {  
        var rv = value as DataRowView;  
        if (rv != null)  
        {  
            ObservableCollection<object> rowviews = new ObservableCollection<object>();  
            foreach (var r in rv.GetRow().GetChildRows("rsParentChild").ToList<DataRow>())  
            {  
                rowviews.Add(r.GetRowView());  
            }  
            return rowviews;  
        }  
        return null;  
    }  

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)  
    {  
        throw new NotImplementedException();  
    }  
}  

Here is the modified xaml:


<local:NodeConverter x:Key="nodeconverter"/>  
<Grid x:Name="LayoutRoot" Background="White">  
    <c1:C1TreeView ItemsSource="{Binding RootNodes}" c1:C1NagScreen.Nag="True">  
        <c1:C1TreeView.ItemTemplate>  
            <c1:C1HierarchicalDataTemplate ItemsSource="{Binding Converter={StaticResource nodeconverter}}">  
                <TextBlock Text="{Binding NodeDescription}" />  
            </c1:C1HierarchicalDataTemplate>  
        </c1:C1TreeView.ItemTemplate>  
    </c1:C1TreeView>  
</Grid>  

That's it. Download the attached Silverlight sample.

MESCIUS inc.

comments powered by Disqus