Answered DataView master\detail binding

  • Thursday, April 17, 2008 5:00 AM
     
     

    This question has been asked two weeks ago and there is no reaction. So, I've decided to repost it.

     

    Sorry for misformatting, there is a bug with my ie - formatting xaml as code block erases content of any other xaml code blocks (

     

    Please, any comments, any ideas, anything - post it here. I've spent these two weeks trying to find any possible solution... I could not.

     

    Help, please! )

     

    The problem core:

    There are two DataTables ("Master" and "Details") connected with DataRelation (MDRelation) referencing to the primary columns of both DataTables. So, there is 1-to-1 binding scenario.

     

    I declare two CollectionViewSource and binding between them (In real life it is located in separate ResourceDictionary):

      <Window.Resources>
        <CollectionViewSource x:Key="masterSource" />
        <CollectionViewSource x:Key="detailsSource" Source="{Binding Source={StaticResource masterSource}, Path=MDRelation}" />
      </Window.Resources> 

     

    I init them with new DataView. There are a lot of reasons not to use default DataView. I skip them.


    DataView v = new DataView(dataSet.Tables[0]);

    ((CollectionViewSource)Resources["masterSource"]).Source = v;

     

     

    Now, I have ListBox binded to the masterSource and displaying details data (accesed via the MDRelation):

    <ListBox DataContext="{DynamicResource masterSource}" ItemsSource="{Binding}" DisplayMemberPath="MDRelation/Name"/>


    And there is TextBox binded to the detailsSource:

    <TextBox DockPanel.Dock="Top" DataContext="{DynamicResource detailsSource}" Text="{Binding Name, UpdateSourceTrigger = PropertyChanged}"/>

     

    At first time, all works fine, and the data being edited in the text box is synchronized with the data being displayed in the ListBox.

     

    But, if i change masterSource's dataView, the binding is broken and the data is out of sync.

    DataView v = new DataView(dataSet.Tables[0]);

    ((CollectionViewSource)Resources["masterSource"]).Source = v;

     

    Any solutions?

     

    The code - Window1.xaml:

    <Window x:Class="MDBinding.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="BindingSample" Width="618" Height="382">
      <Window.Resources>
        <Style x:Key="SomeSpacing">
          <Setter Property="FrameworkElement.Margin" Value="6" />
        </Style>
        <CollectionViewSource x:Key="masterSource" />
        <CollectionViewSource x:Key="detailsSource" Source="{Binding Source={StaticResource masterSource}, Path=MDRelation}" />
      </Window.Resources>
      <DockPanel LastChildFill="True" Style="{DynamicResource SomeSpacing}">

        <TextBlock DockPanel.Dock="Top" Text="Read me!" />

        <Border DockPanel.Dock="Top" BorderBrush="Black" BorderThickness="0.5" Padding="3" Style="{DynamicResource SomeSpacing}">
          <TextBlock TextWrapping="Wrap">
            <TextBlock.Inlines>
              <Bold>Insructions:</Bold>
              <LineBreak />
              <LineBreak />
              1: Click <Hyperlink Click="ResetClick"><Bold>here</Bold></Hyperlink> to replace data.
              Edit text below. Bining is correct - all works OK.
              <LineBreak />
              <LineBreak />
              2: Click <Hyperlink Click="SetSourceClick">here</Hyperlink> to replace dataview <Bold>without</Bold> repacing data.
              Edit text below. Data is <Bold>out of sync</Bold> now.
              <LineBreak />
              <LineBreak />
              3: GOTO 1: )
            </TextBlock.Inlines>
          </TextBlock>
        </Border>

        <TextBlock DockPanel.Dock="Top" Text="Edit me!" />
        <TextBox DockPanel.Dock="Top" DataContext="{DynamicResource detailsSource}" Text="{Binding Name, UpdateSourceTrigger = PropertyChanged}" Style="{DynamicResource SomeSpacing}" />

        <TextBlock DockPanel.Dock="Top" Text="Select me!" />
        <ListBox DataContext="{DynamicResource masterSource}" ItemsSource="{Binding}" DisplayMemberPath="MDRelation/Name" Style="{DynamicResource SomeSpacing}" />
      </DockPanel>
    </Window>

     

    The code - Window1.xaml.cs

    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 MDBinding
    {
      public partial class Window1: Window
      {
        private DataSet dataSet;

        public Window1()
        {
          InitializeComponent();
        }

        private void ResetDataSet()
        {
          dataSet = new DataSet();

          DataTable masterTable = new DataTable("Master");
          masterTable.Columns.Add("ID", typeof(int));
          masterTable.Columns[0].Unique = true;
          masterTable.Columns.Add("Name", typeof(string));

          DataTable detailsTable = new DataTable("Details");
          detailsTable.Columns.Add("ID", typeof(int));
          detailsTable.Columns[0].Unique = true;
          detailsTable.Columns.Add("Name", typeof(string));

          dataSet.Tables.Add(masterTable);
          dataSet.Tables.Add(detailsTable);

          dataSet.Relations.Add("MDRelation", masterTable.Columns[0], detailsTable.Columns[0], true);
        }

        private void RefillDataSet()
        {
          dataSet.Clear();
          for (int i = 0; i < 5; i++)
          {
            dataSet.Tables[0].Rows.Add(i, "Master #" + i);
            dataSet.Tables[1].Rows.Add(i, "Detail #" + i);
          }
        }
       
        private void SetSource()
        {
          DataView v = new DataView(dataSet.Tables[0]);

          ((CollectionViewSource)Resources["masterSource"]).Source = v;
        }

        private void SetSourceClick(object sender, RoutedEventArgs e)
        {
          SetSource();
        }
        private void ResetClick(object sender, RoutedEventArgs e)
        {
          ResetDataSet();

          RefillDataSet();

          SetSource();
        }
      }
    }

All Replies

  • Wednesday, April 23, 2008 10:34 AM
     
     Answered
    This should be a bug, I've simplified the repro code as follows:

    Code Snippet

    <Window
            x:Class="MDBinding.Window1"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            Height="150" Width="300">
      <DockPanel>
        <StackPanel DockPanel.Dock="Top" Orientation="Horizontal">
        <Button
                Width="80" Height="30"
                Content="Reset DataContext" Click="ResetClick"/>
          <Button
                Width="80" Height="30"
                Content="Update Data" Click="UpdateClick"/>
        </StackPanel>
        <TextBlock
          x:Name="textBox2"
          Text="{Binding Path=Name}"/>
      </DockPanel>
    </Window>


    public partial class Window1 : Window
    {
        DataView dataView = null;
        DataTable masterTable = null;
        public Window1()
        {
            InitializeComponent();
            SetupData();
            this.Loaded += delegate
            {
                SetSource();
            };
        }

        private void SetupData()
        {
            masterTable = new DataTable("Master");
            masterTable.Columns.Add("ID", typeof(int));
            masterTable.Columns[0].Unique = true;
            masterTable.Columns.Add("Name", typeof(string));
            masterTable.Rows.Add(0, "Master #" + 0);
        }

        private void SetSource()
        {
            dataView = new DataView(masterTable);
            textBox2.DataContext = null;
            textBox2.DataContext = dataView;
        }

        private void ResetClick(object sender, RoutedEventArgs e)
        {
            SetSource();
        }

        int i = 0;
        private void UpdateClick(object sender, RoutedEventArgs e)
        {
            if (masterTable != null)
                dataView[0][1] += (++i).ToString();
        }
    }


    After you reset the DataContext, clicking "Update Data" button does update the display of TextBlock, I've found that in this circumstance, the binding engine doesn't subscribe to DataRowView.PropertyChanged event, that's why the target cannot be updated. The strange thing here is that I can make sure that the data binding does have subscribed to PropertyChanged event through PropertyChangedEventManager, but when I update the data, for the reason I still doesn't know, the subscription gets lost.

    I would greatly appreciate it if you could file a bug on this at the following site along with the repro code I pasted above:

    https://connect.microsoft.com/feedback/default.aspx?SiteID=212&wa=wsignin1.0


    To workaround this issue, please use the default DataView of the DataTable instead aka DataTable.DefaultView.

    Hope this helps

  • Thursday, April 24, 2008 4:57 AM
     
     

    Big thanks!

     

    I've feedbacked it at https://connect.microsoft.com/feedback/ViewFeedback.aspx?FeedbackID=339740&SiteID=212

    Please, vote for it!

  • Monday, April 28, 2008 4:32 PM
     
     

    This appears to be an ADO bug, not WPF.  After the app creates a new DataView, WPF subscribes to change events from the DataView's only member (a DataRowView), and unsubscribes to change events from the old DataRowView.  Then somehow ADO changes the new DataView without any notification, replacing the new DataRowView with the old one.  Now changes to the Name column affect the old DataRowView, and WPF never hears about them because it's listening to the new DataRowView.

     

    I've sent the bug to the ADO team.

     

  • Monday, April 16, 2012 5:06 AM
     
     

    This appears to be an ADO bug, not WPF.  After the app creates a new DataView, WPF subscribes to change events from the DataView's only member (a DataRowView), and unsubscribes to change events from the old DataRowView.  Then somehow ADO changes the new DataView without any notification, replacing the new DataRowView with the old one.  Now changes to the Name column affect the old DataRowView, and WPF never hears about them because it's listening to the new DataRowView.

    I've sent the bug to the ADO team.

    ----------------------------------------------------------------------------------------

    Have anyone Got solution for above issue ? it seems still this issue there. i am also facing same issue when i am creating more than one view for datatable.


  • Thursday, May 31, 2012 8:48 AM
     
     Proposed
    it seems this bug  is  fixed in 4.0

    siva

    • Proposed As Answer by Siva Kumar J Thursday, May 31, 2012 8:49 AM
    •