none
Self Referential Table in TreeView RRS feed

  • Question

  • Ok...I finally have a tree working on a self referential table in WPF.  Problem:  it is a massive kluge.  Where is the elegance? 

    The problem is to have a flat table containing a id and parent id columns and a relation to self working properly in a tree.  Here is the XAML...

    <Window x:Class="WpfBench.SelfReferentialDataTableTree"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:WpfBench" 
        Title="SelfReferentialDataTableTree">
        <Window.Resources>
            <local:GetChildRowConverter x:Key="GetChildRows"/>
        </Window.Resources>
        <Grid>
            <TreeView Margin="0" Name="treeView1">
                <TreeView.ItemTemplate>
                    <HierarchicalDataTemplate>
                        <HierarchicalDataTemplate.ItemsSource>
                            <Binding Converter="{StaticResource GetChildRows}"/>
                        </HierarchicalDataTemplate.ItemsSource>
                        <TextBlock Text="{Binding Path=[2]}"/>
                    </HierarchicalDataTemplate>
                </TreeView.ItemTemplate>
            </TreeView>
        </Grid>
    </Window>
    

    Here is the code behind...

    namespace WpfBench
    {
        /// <summary>
        /// Interaction logic for SelfReferentialDataTableTree.xaml
        /// </summary>
        public partial class SelfReferentialDataTableTree : Window
        {
            DataSet m_dataSet = new DataSet();
            public SelfReferentialDataTableTree()
            {
                InitializeComponent();
                InitializeDataSet();
                PrintStructure();
                treeView1.ItemsSource = m_dataSet.Tables["SelfReference"].Select("ParentID=0");
            }
    
            private void PrintStructure()
            {
                foreach (DataRow row in m_dataSet.Tables["SelfReference"].Select("ParentID=0"))
                {
                    PrintRow(0, row);
                }
            }
    
            private void PrintRow(int p, DataRow row)
            {
                string leader = new string(' ', p * 5);
                Trace.WriteLine(leader + "Level " + p + ": " + (string)row["Name"]);
                foreach (DataRow child in row.GetChildRows("me2me"))
                {
                    PrintRow(p + 1, child);
                }
            }
    
            private void InitializeDataSet()
            {
                DataTable table = m_dataSet.Tables.Add("SelfReference");
                DataColumn id = table.Columns.Add("ID", typeof(int));
                DataColumn parentId = table.Columns.Add("ParentID", typeof(int));
                DataColumn name = table.Columns.Add("Name", typeof(string));
                DataRelation relation = table.ChildRelations.Add("me2me",id, parentId, false);
                for (int i = 0; i < 40; ++i)
                {
                    DataRow row = table.NewRow();
                    row[id] = i + 1;
                    row[parentId] = i % 10;
                    row[name] = "Row " + (i + 1);
                    table.Rows.Add(row);
                }
            }
    
        }
    }
    

    Finally here is the value converter...

    namespace WpfBench
    {
        public class GetChildRowConverter : IValueConverter
        {
            #region IValueConverter Members
    
            public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
            {
                if (value is DataRow)
                {
                    DataRow row = value as DataRow;
                    return row.GetChildRows("me2me");
                }
                else
                {
                    return value;
                }
            }
    
            public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
            {
                throw new NotImplementedException();
            }
    
            #endregion
        }
    }
    

    The tree should end up structured like this...

    Level 0: Row 1
         Level 1: Row 2
              Level 2: Row 3
                   Level 3: Row 4
                        Level 4: Row 5
                             Level 5: Row 6
                                  Level 6: Row 7
                                       Level 7: Row 8
                                            Level 8: Row 9
                                                 Level 9: Row 10
                                                 Level 9: Row 20
                                                 Level 9: Row 30
                                                 Level 9: Row 40
                                            Level 8: Row 19
                                            Level 8: Row 29
                                            Level 8: Row 39
                                       Level 7: Row 18
                                       Level 7: Row 28
                                       Level 7: Row 38
                                  Level 6: Row 17
                                  Level 6: Row 27
                                  Level 6: Row 37
                             Level 5: Row 16
                             Level 5: Row 26
                             Level 5: Row 36
                        Level 4: Row 15
                        Level 4: Row 25
                        Level 4: Row 35
                   Level 3: Row 14
                   Level 3: Row 24
                   Level 3: Row 34
              Level 2: Row 13
              Level 2: Row 23
              Level 2: Row 33
         Level 1: Row 12
         Level 1: Row 22
         Level 1: Row 32
    Level 0: Row 11
    Level 0: Row 21
    Level 0: Row 31
    


    So I am using a value converter to "convert" a DataRow to an array of child DataRows.  This cannot be the way that this should work!  I have tried mightily to get some kind of ObjectDataProvider working...no luck.  I have looked at all the master/detail bindings for WPF on the forums...simply does not work for self referencing table.  So show me the elegance!  I have been working on WPF solid for a couple of months now and it seems to be one massive kluge after another to get real work done.


    Wednesday, December 16, 2009 4:38 PM

Answers

  • Hi Bob,

    You can add the TreeViewItems programmatically using recursion without data binding, like follows:

    <TreeView Margin="14,10,18,10" Name="treeView1"/>

    =======  C# =======

    public partial class Window1 : Window
        {
            public Window1()
            {
                InitializeComponent();
            }

            DataSet m_dataSet = new DataSet();

            private void InitializeDataSet()
            {
                DataTable table = m_dataSet.Tables.Add("SelfReference");
                DataColumn id = table.Columns.Add("ID", typeof(int));
                DataColumn parentId = table.Columns.Add("ParentID", typeof(int));
                DataColumn name = table.Columns.Add("Name", typeof(string));
                DataRelation relation = table.ChildRelations.Add("me2me", id, parentId, false);
                for (int i = 0; i < 40; ++i)
                {
                    DataRow row = table.NewRow();
                    row[id] = i + 1;
                    row[parentId] = i % 10;
                    row[name] = "Row " + (i + 1);
                    table.Rows.Add(row);
                }
            }

            void Window1_Loaded (object sender, RoutedEventArgs e)
            {
                InitializeDataSet();

                foreach (DataRow dr in m_dataSet.Tables["SelfReference"].Rows)
                {
                    if (Convert.ToInt32(dr["ParentId"]) == 0)
                    {
                        TreeViewItem root = new TreeViewItem();
                        root.Header = dr["Name"].ToString();
                        treeView1.Items.Add(root);
                        SetupTreeView(dr, root);
                    }

                }
            }
         
            public void SetupTreeView (DataRow dr, TreeViewItem pNode)
            {
                foreach (DataRow row in dr.GetChildRows("me2me"))
                {
                    TreeViewItem cChild = new TreeViewItem();
                    cChild.Header = row["Name"].ToString();
                    pNode.Items.Add(cChild);
                    //Recursively build the tree
                    SetupTreeView(row, cChild);

                }
            }
        }



    If you like to use data binding, you can check Macro's sample on this thread:

    http://social.msdn.microsoft.com/forums/en-US/wpf/thread/8ec80b7d-17e8-4d66-b8b0-07c8bfea6213/

    Best Regards,
    Zhi-Xin Ye
    MSDN Subscriber Support in Forum
    If you have any feedback on our support, please contact msdnmg@microsoft.com





    Please remember to mark the replies as answers if they help and unmark them if they provide no help.
    Welcome to the All-In-One Code Framework!
    • Marked as answer by Zhi-Xin Ye Tuesday, December 22, 2009 8:58 AM
    Thursday, December 17, 2009 8:52 AM