none
TreeView bound to ADO.NET DataTable not fully updated RRS feed

  • Question


  • Hi all,

    I have a DataTable “Project” which is self referenced. ProjectId field is the FK of ParentProjectId, and ProjectProject is the corresponding DataRelation. Here is the XAML for a Window which contains a TreeView configured to load and display Projects hierarchically:

    <Window x:Class="QuickNote.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            Title="Quick Note" Height="379" Width="548"
            xmlns:scm="clr-namespace:System.ComponentModel;assembly=WindowsBase"
            xmlns:data="clr-namespace:System.Data;assembly=System.Data"
            xmlns:system="clr-namespace:System;assembly=mscorlib"
            xmlns:local="clr-namespace:QuickNote"
            Loaded="Window_Loaded">
        <Window.Resources>
            <CollectionViewSource x:Key="projectViewSource" Source="{Binding Path=Project, Source={StaticResource mainDataSet}}">
                <CollectionViewSource.SortDescriptions>
            <local:ShortDateConverter x:Key="dateConverter" />
            <HierarchicalDataTemplate x:Key="ProjectTemplate" DataType="{x:Type data:DataRowView}" ItemsSource="{Binding Path=ProjectProject, NotifyOnSourceUpdated=True, NotifyOnTargetUpdated=True, UpdateSourceTrigger=PropertyChanged}">
                <StackPanel>
                    <TextBlock Margin="0,3,0,0">
                        <TextBlock.Text>
                            <MultiBinding StringFormat="{}{0} (ProjectId={1})">
                                <Binding Path="Title" />
                                <Binding Path="ProjectId" />
                            </MultiBinding>
                        </TextBlock.Text>
                    </TextBlock>
                    <TextBlock Margin="0,0,0,3" FontSize="10" Foreground="Blue" Text="{Binding Path=DateOfEntry, Converter={StaticResource dateConverter}, Mode=OneWay}"></TextBlock>
                </StackPanel>
                <HierarchicalDataTemplate.ItemContainerStyle>
                    <Style TargetType="{x:Type TreeViewItem}">
                        <EventSetter Event="TreeViewItem.PreviewMouseRightButtonDown" Handler="TreeViewItem_PreviewMouseRightButtonDown"/>
                    </Style>
                </HierarchicalDataTemplate.ItemContainerStyle>
            </HierarchicalDataTemplate>
        </Window.Resources>
    ...
                <TreeView ItemTemplate="{Binding Source={StaticResource ProjectTemplate}}" ItemsSource="{Binding Source={StaticResource projectViewSource}}" Name="projectsTreeView" SelectedItemChanged="projectsTreeView_SelectedItemChanged" MouseDoubleClick="projectsTreeView_MouseDoubleClick">
                    <TreeView.ContextMenu>
                        <ContextMenu Name="cm" StaysOpen="true">
                            <MenuItem Header="Create Child Project" Click="CreateChildProject_Click"/>
                            <MenuItem Header="Rename" Click="RenameProject_Click"/>
                            <MenuItem Header="Recent Files">
                                <MenuItem Header="ReadMe.txt"/>
                                <MenuItem Header="Schedule.xls"/>
                            </MenuItem>
                        </ContextMenu>
                    </TreeView.ContextMenu>
                    <TreeView.ItemContainerStyle>
                        <Style TargetType="{x:Type TreeViewItem}">
                            <!-- We have to select the item which is right-clicked on -->
                            <EventSetter Event="TreeViewItem.PreviewMouseRightButtonDown" Handler="TreeViewItem_PreviewMouseRightButtonDown"/>
                        </Style>
                    </TreeView.ItemContainerStyle>
                </TreeView>
    


    As you can see, TreeView has ItemsSource bound to projectViewSource and HierarchicalDataTemplate whose ItemsSource bound to ProjectProject DataRelation. All existing items are shown correctly, but the TreeView may not refresh automatically when a child row in “Project” DataTable changed or added:

    1. When a child row changed (for example Title field changed), the TreeView is not updated, while the root items updated normally. Is this probably due to DataRelation delegates change notifications to some wrapper that does not handle them correctly?

    2. If I start my application and add a child row “New Project-1” to an existing row (root or child) using the following C# code:

                MainDataSet.ProjectRow new_row = mainDataSet.Project.NewProjectRow();
    
                new_row.ParentProjectId = ProjectRow.ProjectId;
    
                new_row.DateOfEntry = DateTime.Now;
    
                new_row.Title = "New Project"
    
    
     + ""
    
    
     + new_row.ProjectId;
    
                mainDataSet.Project.AddProjectRow(new_row);
    
    

    Everything works nicely and the TreeView is updated automatically. But if I then add a child row “New Project-2” to newly added row “New Project-1”, the TreeView is not updated and corresponding item does not appear.

    What do I need to do to get this working?

    Thanks in advance.
    Monday, September 14, 2009 7:58 AM

All replies

  • Load your DataSet into and implemented class that implements INotifyPropertyChanged and INotifyCollectionChanged interfaces. Everything will work pronto.
    Bigsby, Lisboa, Portugal - O que for, quando for, é que será o que é... http://bigsby.eu
    Monday, September 14, 2009 11:35 AM
  • I do not think that DataSet generated by VS does not support this interfaces, it mostly looks like that it is an interoperability problem.
    Monday, September 14, 2009 5:29 PM
  • Hello Dmitriano,

      I fear I didn't catch the problem. If there is any misunderstanding, please let me know.

      Here is the XAML for a Window which contains a TreeView configured to load and display Projects hierarchically:

     
    It seems the application shows a project as TreeViewItem and its parent-project as its child node TreeViewItem.

      Is it correct?

       Project
            ----ParentProject
                      --- ParentOfParentProject 


        DataSet is generated by VS, Could you please tell more about the DataRelation?

        Does the following sample code show the correct relation between them

     DataSet myds;
    
            DataTable m_dt;
    
            DataRow m_removed_dr;
    
    
            private void CreateRelation()
            {
    
                myds = new DataSet();
    
                m_dt = new DataTable();
    
                m_dt.Columns.Add("Name", typeof(string));
    
                m_dt.Columns.Add("ProjectId", typeof(int));
    
                m_dt.Columns.Add("ParentProjectId", typeof(int));
    
                myds.Tables.Add(m_dt);
               
                DataColumn parentColumn =
                    m_dt.Columns["ProjectId"];
    
                DataColumn childColumn =
                    m_dt.Columns["ParentProjectId"];
    
                // Create DataRelation.
    
                DataRelation projectprojectRelation;
                projectprojectRelation = new DataRelation("ProjectProject",
                    parentColumn, childColumn);
                // Add the relation to the DataSet.
                myds.Relations.Add(projectprojectRelation);
            }

      
        Thanks.

       
    Please mark the replies as answers if they help and unmark them if they provide no help
    Tuesday, September 15, 2009 7:31 AM
  • Hello Hua, thank you for your answer,

    concerning the DataRelation that is really so, VS generates the following code:
                this.Relations.Add(this.relationProjectResidence);
                this.relationProjectProject = new global::System.Data.DataRelation("ProjectProject", new global::System.Data.DataColumn[] {
                            this.tableProject.ProjectIdColumn}, new global::System.Data.DataColumn[] {
                            this.tableProject.ParentProjectIdColumn}, false);
    
    so ProjectId id a parent column, and ParentProjectId is a child column. There is the sample data:
    ProjectId  ParentProjectId     DateOfEntry  Title Description
    1 <null> 9/11/2009 New Project-1
    2 1 9/11/2009 New Project-2
    3 1 9/11/2009 New Project-3
    4 3 9/11/2009 New Project-4
    5 3 9/11/2009 New Project-5
    6 4 9/11/2009 New Project-6
    7 4 9/11/2009 New Project-7
    8 4 9/11/2009 New Project-8

    row with ProjectId 2 and 3 are child rows of row with ProjectId 1, and so on...

    So, we will see the following TreeView:
     1
      --2
      --3
          --4
          --5
    and so on..
    Tuesday, September 15, 2009 12:06 PM
  • Hello Dmitriano,

      A little difference. It seems to be parent - child - child.

      I have made a simple project and I add the relation manually.

      It works. Hope it helps.
    <Window x:Class="NotifyDataRowChange.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:data="clr-namespace:System.Data;assembly=System.Data"
       Title="Window1" Height="600" Width="800">
    
        <Window.Resources>
            <DataTemplate x:Key="keydataTemplate">
    
                <TextBlock Text="{Binding Path=Name}">
    
                </TextBlock>
            </DataTemplate>
    
            <CollectionViewSource x:Key="projectViewSource" Source="{Binding Path=ProjectTable, Source={StaticResource mainDataSet}}">
                
            </CollectionViewSource>
    
            <HierarchicalDataTemplate x:Key="ProjectTemplate" 
                                      DataType="{x:Type data:DataRowView}" 
                                      ItemsSource="{Binding Path=ProjectProject, 
                                      NotifyOnSourceUpdated=True, 
                                      NotifyOnTargetUpdated=True, 
                                      UpdateSourceTrigger=PropertyChanged}">
                <StackPanel>
                    <TextBlock Margin="0,3,0,0">
                        <TextBlock.Text>
                            <MultiBinding StringFormat="{}{0} (ProjectId={1})">
                                <Binding Path="Name" />
                                <Binding Path="ProjectId" />
                            </MultiBinding>
                        </TextBlock.Text>
                    </TextBlock>
                </StackPanel>
            </HierarchicalDataTemplate>
    
        </Window.Resources>
    
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="50*"></RowDefinition>
                <RowDefinition Height="5*"></RowDefinition>
                <RowDefinition Height="5*"></RowDefinition>
                
            </Grid.RowDefinitions>
            
            <Grid.ColumnDefinitions>
                <ColumnDefinition></ColumnDefinition>
                <ColumnDefinition></ColumnDefinition>
            </Grid.ColumnDefinitions>
            
            <ListBox Grid.Column="1" ItemsSource="{Binding Source={StaticResource projectViewSource}}" >
                <ListBox.ItemTemplate>
                    <DataTemplate>
                        <StackPanel Orientation="Horizontal">
                            <TextBlock Text="{Binding Path=Name}">
                            </TextBlock>
                            
                            <TextBlock Text="{Binding Path=ProjectProject}">
                                
                            </TextBlock>
                        </StackPanel>
                    </DataTemplate>
                </ListBox.ItemTemplate>
            </ListBox>
    
            <TreeView ItemTemplate="{Binding Source={StaticResource ProjectTemplate}}" 
                      ItemsSource="{Binding Source={StaticResource projectViewSource}}" 
                      Name="projectsTreeView">
            </TreeView>
    
            <Button Grid.Row="1" Name="btnAdd" Click="btnAdd_Click">
                Add one row
            </Button>
    
            
        </Grid>
    </Window>
    

       
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Data;
    using System.Windows.Documents;
    using System.Windows.Input;
    using System.Windows.Media;
    using System.Windows.Media.Imaging;
    using System.Windows.Navigation;
    using System.Windows.Shapes;
    using System.Data;
    
    namespace NotifyDataRowChange
    {
        /// <summary>
        /// Interaction logic for Window1.xaml
        /// </summary>
        public partial class Window1 : Window
        {
            DataSet myds;
    
            DataTable m_dt;
    
            DataRow m_removed_dr;
    
    
            private void CreateRelation()
            {
    
                myds = new DataSet();
    
                m_dt = new DataTable("ProjectTable");
    
                m_dt.Columns.Add("Name", typeof(string));
    
                m_dt.Columns.Add("ProjectId", typeof(int));
    
                m_dt.Columns.Add("ParentProjectId", typeof(int));
    
                myds.Tables.Add(m_dt);
               
                DataColumn parentColumn =
                    m_dt.Columns["ProjectId"];
    
                DataColumn childColumn =
                    m_dt.Columns["ParentProjectId"];
    
                // Create DataRelation.
    
                DataRelation projectprojectRelation;
                projectprojectRelation = new DataRelation("ProjectProject",
                    parentColumn, childColumn,false);
                // Add the relation to the DataSet.
                myds.Relations.Add(projectprojectRelation);
            }
    
    
    
            public Window1()
            {
    
                CreateRelation();
                AddDataRow();
    
                this.Resources.Add("mainDataSet", myds);
    
                InitializeComponent();
    
             
            }
    
            private void AddDataRow()
            {
    
                DataRow dr;
    
                dr = m_dt.NewRow();
    
                
    
                dr[0] = "NameOne";
                dr[1] = 1;
                dr[2] = 0;
    
                m_dt.Rows.Add(dr);
    
                //Save the removed dr;
    
                m_removed_dr = dr;
    
                dr = m_dt.NewRow();
    
                dr[0] = "NameTwo";
                dr[1] = 2;
                dr[2] = 1;
    
                m_dt.Rows.Add(dr);
    
                dr = m_dt.NewRow();
    
                dr[0] = "NameThree";
                dr[1] = 3;
                dr[2] = 2;
    
                m_dt.Rows.Add(dr);
    
    
              
            }
    
            private void btnAdd_Click(object sender, RoutedEventArgs e)
            {
    
                //Add a new row has ProjectId = 4, ParentProjectId=2
                DataRow dr;
    
                dr = m_dt.NewRow();
    
                dr[0] = "NameFour";
                dr[1] = 4;
                dr[2] = 2;
    
                m_dt.Rows.Add(dr);
    
            }
    
          
        }
    }
    


     
      Good luck.

    Please mark the replies as answers if they help and unmark them if they provide no help
    Thursday, September 17, 2009 8:58 AM
  • Hello Hua, thank you for your help.

    could you send your project to winappdev_at_gmail_dot_com (remove underscores)?
    Friday, September 18, 2009 7:10 AM
  • Hello Dmitriano,

      Send it. Please check

     Good luck


    Please mark the replies as answers if they help and unmark them if they provide no help
    Friday, September 18, 2009 8:03 AM
  • Hello Hua,

    Built and run your app. Adding new items works for me too, and that is very good, but the problem with renaming still remains.

    To reproduce this problem I added the following code to your app:
            int nClick = 0;
            DataRow theRow;
    
            private void btnAdd_Click(object sender, RoutedEventArgs e)
            {
    
                if (nClick == 2)
                {
                    theRow[0] = "NameFive_Renamed";
                    //theRow[1] = 5;
                    //theRow[2] = 4;
    
                    return;
                }
                
                //Add a new row has ProjectId = 4, ParentProjectId=2
                DataRow dr;
    
                dr = m_dt.NewRow();
    
                if (nClick == 0)
                {
                    dr[0] = "NameFour";
                    dr[1] = 4;
                    dr[2] = 2;
    
                    nClick = 1;
                }
                else
                {
                    if (nClick == 1)
                    {
                        dr[0] = "NameFive";
                        dr[1] = 5;
                        dr[2] = 4;
    
                        theRow = dr;
                        
                        nClick = 2;
                    }
                }
                
                m_dt.Rows.Add(dr);
            }
        }
    
    If you press the button three times and then expand NameFour item, you will see that item NameFive is not updated.

    Sent modified app back to you. Please check.

    Thanks.
    Friday, September 18, 2009 9:05 AM
  • Hello Hua,

    Probably the key difference between the root and the child items of the TreeView is that the root ItemsSource assigned to CollectionViewSource declared in XAML while the child ItemsSource assigned directly to DataRelationView.

    I did further experimentation with you app and added new post:

    http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/a66afb43-d840-4162-b761-ec1104ae56c2
    Sunday, September 20, 2009 8:49 PM