-3

C#, WPF. I am using a Datagrid with binding. My understanding is that with INotifyPropertyChanged implemented, object properties should update in the Datagrid if they are changed.

Currently this is not happening, although I I have implemented INotifyPropertyChanged and I know from testing that the PropertyChanged event is firing. My guess is that binding is not two-way(?) If that is the case I'm not sure how to set it to two-way. The binding is set in XAML, and the ItemsSource is set later in code-behind:

<DataGrid Name="dataGridxyz" ItemsSource="{Binding}">

dataGridxyz.ItemsSource = foo;

Adding two-way binding in XAML using this syntax causes an error:

<DataGrid Name="dataGridxyz" ItemsSource="{Binding, Mode=TwoWay}">

So I was looking for something like this:

dataGridxyz.ItemsSource = foo;
dataGridxyz.Binding.Mode = TwoWay;

It may be that I could set it to two-way binding either in XAML or code-behind... but I can't see how to do either.

EDIT:

The following is minimal functional example to show the problem. It is a much-simplified version of the real thing which is part of a much bigger project.

When the button is clicked, the Name property is changed but it does not update in the PropertyGrid.

<Window x:Class="testBinding.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d"
        Title="MainWindow">
    <Grid>
        <StackPanel Orientation="Vertical">
            <DataGrid Name="dg" ItemsSource="{Binding}" AutoGenerateColumns="True"/>
            <Button Name="btn" Width="100" Height="20" Content="Test" Click="btn_Click"/>
        </StackPanel>
    </Grid>

namespace testBinding
{
    public partial class MainWindow : Window
    {
        BindingList<foo> bar = new BindingList<foo>() { new foo() };
        public MainWindow()
        {
            InitializeComponent();
            dg.ItemsSource = bar;
        }

        private void btn_Click(object sender, RoutedEventArgs e)
        {
            bar[0].Name = "Paul";
        }
    }

    class foo : genericClass, INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
    }

    class genericClass : INotifyPropertyChanged
    {
        private string _name = "John";
        public string EyeColor = "Blue";
        public bool Child = false;

        public event PropertyChangedEventHandler PropertyChanged;
        protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }

        public string Name
        {
            get { return _name; }
            set
            {
                _name = value;
                MessageBox.Show("Name changed!"); // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
                OnPropertyChanged("Name");
            }
        }
    }
}
wotnot
  • 261
  • 1
  • 12
  • 2
    The error is most probably because you have `Mode-TwoWay` instead you should have `Mode=TwoWay`. – MindSwipe Oct 16 '20 at 13:09
  • 2
    Besides that, it is totally pointless to set TwoWay on a Binding of the ItemsSource property. It has no effect at all. – Clemens Oct 16 '20 at 13:11
  • `{Binding, Mode-TwoWay}` is also invalid due to the comma. Syntactically correct would be `{Binding Mode=TwoWay}`, but that would still make no sense because a TwoWay binding only works in conjunction with a property `Path`. Even with that, it would still not have any effect here. – Clemens Oct 16 '20 at 13:14
  • You must however make sure that `foo` is an IEnumerable that holds a collection of objects with public properties. – Clemens Oct 16 '20 at 13:16
  • The hyphen instead of an equals was a typo above, but was correct in the code (entered using IntelliSense.) I have now corrected it above. – wotnot Oct 16 '20 at 13:16
  • So what is `foo`? – Klaus Gütter Oct 16 '20 at 13:18
  • There is still the invalid comma... But as said, all that is pointless anyway. – Clemens Oct 16 '20 at 13:18
  • I know that it is not valid (either with or without the comma). – wotnot Oct 16 '20 at 13:23
  • @KlausGütter, ```foo``` is a class instance with various properties which is being displayed in the ```Datagrid```. If I change e.g. its ```Name```, that change is not reflected in the ```Datagrid```. – wotnot Oct 16 '20 at 13:26
  • 2
    As alread said, `foo` must be a collection of objects (at least an IEnumerable). Use e.g. `List`. A DataGrid is supposed to show *multiple* objects, one per row. – Clemens Oct 16 '20 at 13:29
  • Try `dataGridxyz.ItemsSource = new List { foo };` – Clemens Oct 16 '20 at 13:33
  • Sorry - I was not correct to suggest that ```foo``` is a single object. ```foo``` is a ```BindingList``` of said objects. I have got the same issue in a ```Datagrid``` and an xceed ```PropertyGrid```. Properties display OK, but they do not update when changed elsewhere (e.g. if I change in the ```PropertyGrid``` I would expect to see that change reflected in the ```DataGrid```). – wotnot Oct 16 '20 at 13:38
  • Then show us the relevant parts of that class... – Clemens Oct 16 '20 at 13:43
  • It is fairly big. I will create a stand-alone project as an example. – wotnot Oct 16 '20 at 13:44
  • 1
    BindingList in WPF? See https://stackoverflow.com/questions/4284663/difference-between-observablecollection-and-bindinglist – Klaus Gütter Oct 16 '20 at 13:52
  • I have created a separate test project and it works there, without any need to set two-way binding. If I use a button to change the Name property, it does update in the ```PropertyGrid``` as I would expect. I guess the simple answer to my question then is that ```DataGrid``` binding is two-way by default and does not have to be explicitly set. – wotnot Oct 16 '20 at 14:12
  • There must be something else going on in my real application which prevents it from working (although the implementation of ```INotifyPropertyChanged``` etc. is the same as far as I can see ; I transferred most of it using copy and paste.) I will go back to it and try to work out what is going on. Thanks for the comments. – wotnot Oct 16 '20 at 14:16
  • No idea, but you still seem not to understand that setting the ItemsSource Binding to TwoWay would not have an effect anyway. Besides that, consider deleting the question. It is of no use for anybody. – Clemens Oct 16 '20 at 14:16
  • I thought that one-way binding would explain the behavior that I was seeing i.e. ```DataGrid``` / ```PropertyGrid``` not updating when properties changed. – wotnot Oct 16 '20 at 14:19

1 Answers1

-1

I figured out what was happening here through a combination of guesswork and trial and error. Thanks to those who commented.

It was not caused by one-way binding as I had originally surmised.

This problem was caused by the fact that the foo object in the example above inherits from another class (genericClass) and both implement INotifyPropertyChanged. It seems clear that the existence of the PropertyChanged event in the foo class prevents the DataGrid from updating. I had not expected this behavior since I know that the PropertyChanged event in the inherited class does fire and does update the Name property.

If I remove the PropertyChanged event from foo, then the name updates in the PropertyGrid as expected.

class foo : genericClass, INotifyPropertyChanged
    {
        //public event PropertyChangedEventHandler PropertyChanged;
    }

It leaves me with the problem of how to handle property changes at more than one level of inheritance (i.e. both in a class and in one it inherits from, which seems a valid thing to do) ... but that is perhaps a different question.

wotnot
  • 261
  • 1
  • 12
  • 1
    While two-way binding might not be the underlying cause, you seem singularly unable to grasp that it is conceptually incorrect to use `Mode=TwoWay` when binding to `ItemsSource`. Also, please post a separate question for the new issue you are facing. – Tanveer Badar Oct 16 '20 at 16:25
  • I don't dispute that it is conceptually incorrect. As to whether I grasp it - no, not particularly ; nobody has explained it. There are plenty of references to one-way vs two-way binding in relation to DataGrids and this one says "can't you just set the binding Mode on the DataGrid's ItemsSource property to OneWay" and has got two up-votes :-) https://stackoverflow.com/questions/2365865/wpf-one-way-binding-on-entire-datagrid. But anyway, it does not seem very relevant since I have located the cause of the problem. I stated above that "It was not caused by one-way binding", which seems clear. – wotnot Oct 16 '20 at 17:12
  • 2
    This does in no way answer the original question. Both the question and the answer should be removed, as they do not provide any benefit for other users of the site. Seriously. – Clemens Oct 16 '20 at 17:13
  • "*nobody has explained it*" - that must be a joke. We told you a couple of times. The problem is you aren't listening. – Clemens Oct 16 '20 at 17:14
  • And if you would do just a bit of research on StackOverflow, you would quickly find the typical INotifyPropertyChanged base class implementation with a protected NotifyPropertyChanged method that could be called from derived classes. – Clemens Oct 16 '20 at 17:17
  • @wotnot If you had [looked](https://learn.microsoft.com/en-us/dotnet/api/system.windows.controls.itemscontrol.itemssource?view=netcore-3.1#System_Windows_Controls_ItemsControl_ItemsSource) deeper, you would [know](https://learn.microsoft.com/en-us/dotnet/api/system.windows.frameworkpropertymetadata.bindstwowaybydefault?view=netcore-3.1#System_Windows_FrameworkPropertyMetadata_BindsTwoWayByDefault) it does not [support](https://github.com/dotnet/wpf/blob/master/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Controls/ItemsControl.cs#L185) two-way binding. – Tanveer Badar Oct 16 '20 at 18:40