none
Binding on DataGrid with ObservableCollection

    Question

  • Hello,

    I'm working on a WPF project where I have a datagrid bound to an ObservableCollection. The collection itens implement INotifyPropertyChanged.

    The strange behavior I'm experiencing is that when I handle the click event of a Button located in a Toolbar, the bound collection is still not updated. However, If I handle the click event of a button directly on the windows, then the collection is updated correctly at that moment.

    How can I make my collection update when at the moment I handle the click event of a button inside a toolbar. Here is the code of my simple test program:

    <Grid>
        <Grid.RowDefinitions>
          <RowDefinition Height="78*" />
          <RowDefinition Height="233*" />
        </Grid.RowDefinitions>
        <DataGrid AutoGenerateColumns="True" Grid.Row="1" Name="dataGrid1" ItemsSource="{Binding}" />
        <DockPanel x:Name="MainPanel">
          <ToolBar DockPanel.Dock="Top" VirtualizingStackPanel.VirtualizationMode="Standard">
            <Button Click="Button_Click" x:Name="ProcessButton">Process
            </Button>
          </ToolBar>
          <Button DockPanel.Dock="Right" Click="Button_Click">Process 2</Button>
        </DockPanel>
      </Grid>
    

    The code behind is:

        }
      }
    
      public class MyCollection : ObservableCollection<MyObject>
      {
    
      }
    
      public class MyObject : INotifyPropertyChanged
      {
        private bool _use;
        public bool Use
        {
          get{
            return _use;
          }
          set
          {
            if (_use != value && this.PropertyChanged != null)
            {
              _use = value;
              this.PropertyChanged(this, new PropertyChangedEventArgs("Use"));
            }
            else
              _use = value;
          }
        }
        public string Name { get; set; }
    
        public MyObject(bool use, string name)
        {
          _use = use;
          Name = name;
        }
    
        public event PropertyChangedEventHandler PropertyChanged;
    
    
      }
    

    Note that I'm not using the Name property at this moment, so it does not notify PropertyChanged.

    If you put a breakpoint at the Click event handler you will see that the collection is update only when the regular Button is clicked.

    What might be missing?

    Regards,

    Igor.


    Software Developer for Business Applications www.twitter.com/ikondrasovas
    Friday, December 17, 2010 7:35 PM

Answers

  • Hi,

    When you want some changes occurs in UI, make sure that property binded to control, as in your case it is auto generated and you bind it thru codebehind

    your case 

    <DataGrid AutoGenerateColumns="True" Grid.Row="1" Name="dataGrid1" ItemsSource="{Binding}" />
    My case

    <DataGrid Grid.Row="1" AutoGenerateColumns="False"

                      Name="dataGrid1"

                      ItemsSource="{Binding Col,Mode=TwoWay}">

                <DataGrid.Columns>

                    <DataGridCheckBoxColumn Binding="{Binding Use,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" />

                    <DataGridTextColumn Binding="{Binding Name,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" />

                </DataGrid.Columns>

            </DataGrid>

     

    Bold Things make the difference.

    If it really helps you, please make it as answer.

    Thanks,

    Rajnikant

    Monday, December 20, 2010 12:48 PM
  • Hi,

    Use this way

    <DataGrid Grid.Row="1" AutoGenerateColumns="False"

                      Name="dataGrid1"

                      ItemsSource="{Binding Col,Mode=TwoWay}">

                <DataGrid.Columns>

                    <DataGridCheckBoxColumn Binding="{Binding Use,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" />

                    <DataGridTextColumn Binding="{Binding Name,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" />

                </DataGrid.Columns>

            </DataGrid>

    and code behind

     public partial class Window1 : Window, INotifyPropertyChanged

        {

            #region INotifyPropertyChanged

     

            /// <summary>

            /// Occurs when a property value changes

            /// </summary>

            public event PropertyChangedEventHandler PropertyChanged;

     

            /// <summary>

            /// Raise the  <see cref="PropertyChanged"/> event.

            /// </summary>

            /// <param name="propertyName"></param>

            protected void NotifyPropertyChange(string propertyName)

            {

                if (PropertyChanged != null)

                    PropertyChanged(this, new PropertyChangedEventArgs(propertyName));

            }

     

            #endregion

     

            private ObservableCollection<MyObject> _col = new ObservableCollection<MyObject>();

            public ObservableCollection<MyObject> Col

            {

                get { return _col; }

                set

                {

                    _col = value;

                    NotifyPropertyChange("Col");

                }

            }

            public Window1()

            {

                InitializeComponent();

                this.DataContext = this;

                Col.Add(new MyObject() {Use=true,Name="Raj" });

     

                //dataGrid1.ItemsSource = Col;

            }

     

            private void Button_Click(object sender, RoutedEventArgs e)

            {

                MessageBox.Show(Col[0].Use.ToString());

            }

     

        }

        public class MyObject 

        {

            public bool Use { get; set; }

            public string Name { get; set; }

        }

     

    Thanks,

    Rajnikant

    Monday, December 20, 2010 11:50 AM
  • Hi Igor,

    We can use the DataGrid in WPF Toolkit for WPF 3.5 instead of the DataGrid in WPF 4, and they are 99% same.

    Regarding to the error on the DataBinding, it is occurred by the last new empty row in the DataGrid, it is a new pending to add row so there is no the binding source object in the collection, the BindingExpression on this row returns error. We can ignore it in WPF Toolkit or turn off it by setting CanUserAddRows="False".

    And regarding to the original problem, when we click on the Button in the toolbar, the focus till on the DataGrid, so the DataGrid does not CommitEdit to update the data source value. But click on the button out of the toolbar, application move the focus to the button, the DataGrid do CommitEdit to update the value. So the simple solution is to invoke the CommitEdit method in the Button_Click event handler:

     private void Button_Click(object sender, RoutedEventArgs e)
     {
      dataGrid1.CommitEdit();
      MessageBox.Show(_col[0].Use.ToString());
     }
    

    Of course, Rajnikant Rajwadi 's reply is a standard solution for developing the data by DataGrid, code the DataGridColumn manually, set the bidnings one by one, and specify the UpdateSourceTrigger=PropertyChanged.

    Hope this helps.

    Sincerely,


    Bob Bao [MSFT]
    MSDN Community Support | Feedback to us
    Get or Request Code Sample from Microsoft
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    Tuesday, December 21, 2010 8:18 AM

All replies

  • Hi Igor,

    How do you handle the changes in the Button_Click event handler, do you change the item of the collection in the event handler?

    I tested the code, the click event handler can be fired and any changes in the ObservableCollection can be notified and updated to the DataGrid. I just have one concern, the constructor of the MyObject class, it set the _use property value bypassed the setter of the property, so you may change it to "Use = use;".

    On the other hand, could you please share more code about the event handler? If it is possible, you could share a complete sample about it, then we can help you to troubleshoot it, Thanks.

    Sincerely,
    Bob Bao

    MSDN Subscriber Support in Forum 

    If you have any feedback on our support, please contact msdnmg@microsoft.com


    Bob Bao [MSFT]
    MSDN Community Support | Feedback to us
    Get or Request Code Sample from Microsoft
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    Monday, December 20, 2010 2:11 AM
  • Hello Bob,

    Thank you for the reply back.

    This is a simple testing I did to reproduce the problem I'm having in my project.

    This is what the application does:

    public partial class MainWindow : Window
      {
        private MyCollection _col = new MyCollection();
    
        public MainWindow()
        {
          InitializeComponent();
    
          _col.Add(new MyObject(true, "igor"));
          dataGrid1.ItemsSource = _col;
        }
    
        private void Button_Click(object sender, RoutedEventArgs e)
        {
          MessageBox.Show(_col[0].Use.ToString());
        }
      }
    

    It is a silly sample, but it does reproduces the problem. When both of the button are clicked, the event handler will show the Use property value of the first row on the grid.

    As you can see, when the Button located in the toolbar is pushed, the Use value is not correct. It seems it does not get the changes performed on the DataGrid.

    So my problem is that the changes on the UI are not reflected in the collection.

    Can you reproduce the problem now?

    Thank you,

    Igor.


    Software Developer for Business Applications www.twitter.com/ikondrasovas
    Monday, December 20, 2010 10:56 AM
  • Hi,

    Use this way

    <DataGrid Grid.Row="1" AutoGenerateColumns="False"

                      Name="dataGrid1"

                      ItemsSource="{Binding Col,Mode=TwoWay}">

                <DataGrid.Columns>

                    <DataGridCheckBoxColumn Binding="{Binding Use,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" />

                    <DataGridTextColumn Binding="{Binding Name,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" />

                </DataGrid.Columns>

            </DataGrid>

    and code behind

     public partial class Window1 : Window, INotifyPropertyChanged

        {

            #region INotifyPropertyChanged

     

            /// <summary>

            /// Occurs when a property value changes

            /// </summary>

            public event PropertyChangedEventHandler PropertyChanged;

     

            /// <summary>

            /// Raise the  <see cref="PropertyChanged"/> event.

            /// </summary>

            /// <param name="propertyName"></param>

            protected void NotifyPropertyChange(string propertyName)

            {

                if (PropertyChanged != null)

                    PropertyChanged(this, new PropertyChangedEventArgs(propertyName));

            }

     

            #endregion

     

            private ObservableCollection<MyObject> _col = new ObservableCollection<MyObject>();

            public ObservableCollection<MyObject> Col

            {

                get { return _col; }

                set

                {

                    _col = value;

                    NotifyPropertyChange("Col");

                }

            }

            public Window1()

            {

                InitializeComponent();

                this.DataContext = this;

                Col.Add(new MyObject() {Use=true,Name="Raj" });

     

                //dataGrid1.ItemsSource = Col;

            }

     

            private void Button_Click(object sender, RoutedEventArgs e)

            {

                MessageBox.Show(Col[0].Use.ToString());

            }

     

        }

        public class MyObject 

        {

            public bool Use { get; set; }

            public string Name { get; set; }

        }

     

    Thanks,

    Rajnikant

    Monday, December 20, 2010 11:50 AM
  • Hello Rajnikan,

    I did the changes and it worked !

    Could you please tell me what was wrong with my code and what did you do to fix it? So I will not come into this error again...

    Thank you,

    Igor.


    Software Developer for Business Applications www.twitter.com/ikondrasovas
    Monday, December 20, 2010 12:05 PM
  • Hi,

    When you want some changes occurs in UI, make sure that property binded to control, as in your case it is auto generated and you bind it thru codebehind

    your case 

    <DataGrid AutoGenerateColumns="True" Grid.Row="1" Name="dataGrid1" ItemsSource="{Binding}" />
    My case

    <DataGrid Grid.Row="1" AutoGenerateColumns="False"

                      Name="dataGrid1"

                      ItemsSource="{Binding Col,Mode=TwoWay}">

                <DataGrid.Columns>

                    <DataGridCheckBoxColumn Binding="{Binding Use,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" />

                    <DataGridTextColumn Binding="{Binding Name,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" />

                </DataGrid.Columns>

            </DataGrid>

     

    Bold Things make the difference.

    If it really helps you, please make it as answer.

    Thanks,

    Rajnikant

    Monday, December 20, 2010 12:48 PM
  • Hello Rajnikant,

    When I decided to apply these changes to my real project, they did not work. So after wondering why I noticed that my test application was targeting .NET 4.0 Client Profile and my real project target .NET 3.5.

    So I decided to change the target platform on the test app and the problem came back !!

    I cannot change my project target platform to .NET 4 at this moment.

    Could you please change you target platform on you test app to .NET 3.5 and see if the problem appears?

    Thank you,

    Igor.


    Software Developer for Business Applications www.twitter.com/ikondrasovas
    Monday, December 20, 2010 12:51 PM
  • Hello Rajnikant,

    When I decided to apply these changes to my real project, they did not work. So after wondering why I noticed that my test application was targeting .NET 4.0 Client Profile and my real project target .NET 3.5.

    So I decided to change the target platform on the test app and the problem came back !!

    I cannot change my project target platform to .NET 4 at this moment.

    Could you please change you target platform on you test app to .NET 3.5 and see if the problem appears?

    Thank you,

    Igor.


    Software Developer for Business Applications www.twitter.com/ikondrasovas

    One Important thing I noticed is while using .NET 3.5, I get the following binding errors on the output window:

    System.Windows.Data Error: 39 : BindingExpression path error: 'Use' property not found on 'object' ''Object' (HashCode=65192075)'. BindingExpression:Path=Use; DataItem='Object' (HashCode=65192075); target element is 'CheckBox' (Name=''); target property is 'IsChecked' (type 'Nullable`1')

    System.Windows.Data Error: 39 : BindingExpression path error: 'Name' property not found on 'object' ''Object' (HashCode=65192075)'. BindingExpression:Path=Name; DataItem='Object' (HashCode=65192075); target element is 'TextBlock' (Name=''); target property is 'Text' (type 'String')

     


    Software Developer for Business Applications www.twitter.com/ikondrasovas
    Monday, December 20, 2010 12:58 PM
  • Hi,

    Thats been fine issue, will come back with solution.

    Thanks,

    Rajnikant

    Monday, December 20, 2010 1:18 PM
  • Hi Igor,

    We can use the DataGrid in WPF Toolkit for WPF 3.5 instead of the DataGrid in WPF 4, and they are 99% same.

    Regarding to the error on the DataBinding, it is occurred by the last new empty row in the DataGrid, it is a new pending to add row so there is no the binding source object in the collection, the BindingExpression on this row returns error. We can ignore it in WPF Toolkit or turn off it by setting CanUserAddRows="False".

    And regarding to the original problem, when we click on the Button in the toolbar, the focus till on the DataGrid, so the DataGrid does not CommitEdit to update the data source value. But click on the button out of the toolbar, application move the focus to the button, the DataGrid do CommitEdit to update the value. So the simple solution is to invoke the CommitEdit method in the Button_Click event handler:

     private void Button_Click(object sender, RoutedEventArgs e)
     {
      dataGrid1.CommitEdit();
      MessageBox.Show(_col[0].Use.ToString());
     }
    

    Of course, Rajnikant Rajwadi 's reply is a standard solution for developing the data by DataGrid, code the DataGridColumn manually, set the bidnings one by one, and specify the UpdateSourceTrigger=PropertyChanged.

    Hope this helps.

    Sincerely,


    Bob Bao [MSFT]
    MSDN Community Support | Feedback to us
    Get or Request Code Sample from Microsoft
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    Tuesday, December 21, 2010 8:18 AM