Pierre Pierre - 1 month ago 19
C# Question

WPF/XAML TreeView with Binding to DataSet won't update SubNodes/SubItems if parent DataRow Key Changes

First things first: I am from Germany - so sorry for my possibly bad english :)

My Problem:



I have a TreeView, which is bound to a DataSet with multiple hierarchical DataTables (with required DataRelations of course).

When changing the value of the PrimaryKey Column1 in DataTable1 (see below node 'Test2') the TreeView updates this "root node" but its SubNodes disappear/will be removed.

Interesting fact: When changing the value of the PrimaryKey Column1 in DataTable1 back to the old value everything is fine (like before).

The children DataRows in DataTable2 and DataTable3 of the changed DataRow in DataTable1 got successfully the new key value.
Only the TreeView 'loses' the children.

Sample before (as Tree):



+Test1
-Test2
-Test2-1
Test2-1-1
Test2-1-2
-Test2-2
Test2-2-1
Test2-2-2
+Test3
+Test4
+Test5


Sample after (as Tree):



+Test1
Test2
+Test3
+Test4
+Test5


Code-behind



public MainWindow()
{
InitializeComponent();

DataSet ds = new DataSet("DummyDS");

DataTable dt = new DataTable("DummyT1");
dt.Columns.Add(new DataColumn("DummyT1C1", typeof(string)));
dt.Columns.Add(new DataColumn("DummyT1C2", typeof(string)));
dt.Columns.Add(new DataColumn("DummyT1C3", typeof(string)));
dt.Columns.Add(new DataColumn("DummyT1C4", typeof(string)));
dt.PrimaryKey = new DataColumn[] { dt.Columns[0] };

ds.Tables.Add(dt);

dt = new DataTable("DummyT2");
dt.Columns.Add(new DataColumn("DummyT2C1", typeof(string)));
dt.Columns.Add(new DataColumn("DummyT2C2", typeof(string)));
dt.Columns.Add(new DataColumn("DummyT2C3", typeof(string)));
dt.Columns.Add(new DataColumn("DummyT2C4", typeof(string)));
dt.PrimaryKey = new DataColumn[] { dt.Columns[0], dt.Columns[1] };

ds.Tables.Add(dt);

dt = new DataTable("DummyT3");
dt.Columns.Add(new DataColumn("DummyT3C1", typeof(string)));
dt.Columns.Add(new DataColumn("DummyT3C2", typeof(string)));
dt.Columns.Add(new DataColumn("DummyT3C3", typeof(string)));
dt.Columns.Add(new DataColumn("DummyT3C4", typeof(string)));
dt.PrimaryKey = new DataColumn[] { dt.Columns[0], dt.Columns[1], dt.Columns[2] };

ds.Tables.Add(dt);

ds.Relations.Add(
new DataRelation("DummyT1_DummyT2",
ds.Tables[0].Columns[0],
ds.Tables[1].Columns[0]));
ds.Relations.Add(
new DataRelation("DummyT2_DummyT3",
new DataColumn[] { ds.Tables[1].Columns[0], ds.Tables[1].Columns[1] },
new DataColumn[] { ds.Tables[2].Columns[0], ds.Tables[2].Columns[1] }));

sampleTV.DataContext = ds.Tables[0];

ds.Tables[0].Rows.Add("Test1", "Test11", "Test12", "Test13");
ds.Tables[0].Rows.Add("Test2", "Test21", "Test22", "Test23");
ds.Tables[0].Rows.Add("Test3", "Test31", "Test32", "Test33");
ds.Tables[0].Rows.Add("Test4", "Test41", "Test42", "Test43");
ds.Tables[0].Rows.Add("Test5", "Test51", "Test52", "Test53");
ds.Tables[1].Rows.Add("Test1", "Test1-1", "Test1-11", "Test1-12");
ds.Tables[1].Rows.Add("Test2", "Test2-1", "Test2-11", "Test2-12");
ds.Tables[1].Rows.Add("Test3", "Test3-1", "Test3-11", "Test3-12");
ds.Tables[1].Rows.Add("Test4", "Test4-1", "Test4-11", "Test4-12");
ds.Tables[1].Rows.Add("Test5", "Test5-1", "Test5-11", "Test5-12");
ds.Tables[1].Rows.Add("Test1", "Test1-2", "Test1-21", "Test1-22");
ds.Tables[1].Rows.Add("Test2", "Test2-2", "Test2-21", "Test2-22");
ds.Tables[1].Rows.Add("Test3", "Test3-2", "Test3-21", "Test3-22");
ds.Tables[1].Rows.Add("Test4", "Test4-2", "Test4-21", "Test4-22");
ds.Tables[1].Rows.Add("Test5", "Test5-2", "Test5-21", "Test5-22");
ds.Tables[2].Rows.Add("Test1", "Test1-1", "Test1-1-1", "Test1-1-11");
ds.Tables[2].Rows.Add("Test2", "Test2-1", "Test2-1-1", "Test2-1-11");
ds.Tables[2].Rows.Add("Test3", "Test3-1", "Test3-1-1", "Test3-1-11");
ds.Tables[2].Rows.Add("Test4", "Test4-1", "Test4-1-1", "Test4-1-11");
ds.Tables[2].Rows.Add("Test5", "Test5-1", "Test5-1-1", "Test5-1-11");
ds.Tables[2].Rows.Add("Test1", "Test1-2", "Test1-2-1", "Test1-2-11");
ds.Tables[2].Rows.Add("Test2", "Test2-2", "Test2-2-1", "Test2-2-11");
ds.Tables[2].Rows.Add("Test3", "Test3-2", "Test3-2-1", "Test3-2-11");
ds.Tables[2].Rows.Add("Test4", "Test4-2", "Test4-2-1", "Test4-2-11");
ds.Tables[2].Rows.Add("Test5", "Test5-2", "Test5-2-1", "Test5-2-11");
ds.Tables[2].Rows.Add("Test1", "Test1-1", "Test1-1-2", "Test1-1-21");
ds.Tables[2].Rows.Add("Test2", "Test2-1", "Test2-1-2", "Test2-1-21");
ds.Tables[2].Rows.Add("Test3", "Test3-1", "Test3-1-2", "Test3-1-21");
ds.Tables[2].Rows.Add("Test4", "Test4-1", "Test4-1-2", "Test4-1-21");
ds.Tables[2].Rows.Add("Test5", "Test5-1", "Test5-1-2", "Test5-1-21");
ds.Tables[2].Rows.Add("Test1", "Test1-2", "Test1-2-2", "Test1-2-21");
ds.Tables[2].Rows.Add("Test2", "Test2-2", "Test2-2-2", "Test2-2-21");
ds.Tables[2].Rows.Add("Test3", "Test3-2", "Test3-2-2", "Test3-2-21");
ds.Tables[2].Rows.Add("Test4", "Test4-2", "Test4-2-2", "Test4-2-21");
ds.Tables[2].Rows.Add("Test5", "Test5-2", "Test5-2-2", "Test5-2-21");
ds.AcceptChanges();
}

private void tvTestBtn_Click(object sender, RoutedEventArgs e)
{
DataTable dt = (DataTable) sampleTV.DataContext;
DataRow dr = dt.Rows.Find("Test2");
dr.BeginEdit();
dr.SetField(0, "Test2aaa");
dt.AcceptChanges();
dr.EndEdit();
}


The ButtonClick event is to change the PrimaryKey value while in runtime.

XAML (lies in a Window)



<StackPanel>
<Button x:Name="tvTestBtn" Content="Test" Click="tvTestBtn_Click"/>
<TreeView x:Name="sampleTV" ItemsSource="{Binding DefaultView}">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding DummyT1_DummyT2}">
<HierarchicalDataTemplate.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding DummyT2_DummyT3}">
<HierarchicalDataTemplate.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Converter={local:ItemConverter}}"/>
</DataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
<TextBlock Text="{Binding Converter={local:ItemConverter}}"/>
</HierarchicalDataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
<TextBlock Text="{Binding Converter={local:ItemConverter}}"/>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
</StackPanel>


I have left out the source for 'local:ItemConverter' as i know that there is not the problem (spent many hours of debugging ;) )

What i've already tried



I spent hours (it could also be days maybe) with finding someone, who has also this problem - but did not found anyone/anything.

What i've done:


  1. Invalidate the visual of the TreeView -> did not help.

  2. Removing the DataContext and reassign it -> did not help.

  3. Removing the ItemsSource from the TreeView and reassign it -> did not help.

  4. Removing the ItemsSource from the TreeViewItem and reassign it -> did not help.

  5. Removing the ItemsTemplate from the TreeView and reassign it -> did not help.

  6. Removing the ItemsSource from the TreeView, deleted all items and reassign it -> did not help.



So my last ray of hope was to post my own question here because i have no more ideas.

IMO the TreeView itself still 'plays' with the old Data (in relation to the children of the DataRow), so maybe it only needs to be refreshed - but how?

Any help would be great. :)

Thanks.

Answer

Have you tried something like this:

// You'll have to have the DataSet ds as a field on class MainWindow:
    DataSet ds = new DataSet("DummyDS");

private void tvTestBtn_Click(object sender, RoutedEventArgs e)
{
  DataTable dt = (DataTable)sampleTV.DataContext;
  DataRow dr = dt.Rows.Find("Test2");
  dr.BeginEdit();
  dr.SetField(0, "Test2aaa");

  // Removes the old relation..
  ds.Relations.Remove("DummyT1_DummyT2");
  // and add it again
  ds.Relations.Add(
  new DataRelation("DummyT1_DummyT2",
    ds.Tables[0].Columns[0],
    ds.Tables[1].Columns[0]));


  dt.AcceptChanges();
  dr.EndEdit();
}

I'm not sure it is the right way, but if it works...

I think the relation becomes invalid when you change the value of one of its keys. When the key 'Test2' changes to 'Test2aaa' no child elements in table 2 relates to it anymore??