How to have bindable properties of a UserControl which work with OnPropertyChanged

10,005

Solution 1

I think I've got this figured out. I didn't understand how change notifications were sent from control to bound datasource.

Yes, calling OnValidating() is the wrong way.

From what I've pieced together, there are two ways a control can notify the datasource that a property has changed.

One way is for the control to implement INotifyPropertyChanged. I had never done this from the control side before, and I thought only the datasource side of the binding had to implement it.

When I implemented INotifyPropertyChanged on my user control, and raised the PropertyChanged event at the appropriate time, it worked.

The second way is for the control to raise a specific change event for each property. The event must follow the naming convention: <propertyname>Changed

e.g. for my example it would be

public event EventHandler ControlPropertyChanged

If my property was called Foo, it would be FooChanged.

I failed to notice the relavent part of the MSDN documentation, where it says:

For change notification to occur in a binding between a bound client and a data source, your bound type should either:

Implement the INotifyPropertyChanged interface (preferred).

Provide a change event for each property of the bound type.

This second way is how all existing WinForms controls work, so this is how I'm doing it now. I use INotifyPropertyChanged on my datasource, but I raise the Changed events on my control. This seems to be the conventional way.

Solution 2

Implementing the INotifyPropertyChanged interface is very simple. Here is a sample that shows an object with a single public field...

public class Demo : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    private void NotifyPropertyChanged(String info)
    {
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(info));
    }

    private string _demoField;

    public string DemoField
    {
        get {return demoField; }

        set
        {
            if (value != demoField)
            {
                demoField = value;
                NotifyPropertyChanged("DemoField");
            }
        }
    }
}

Then you would create a Binding instance to bind a control property to a property (DemoField) on your source instance (instance of Demo).

Share:
10,005
Igby Largeman
Author by

Igby Largeman

Updated on July 18, 2022

Comments

  • Igby Largeman
    Igby Largeman almost 2 years

    I have a simple usercontrol (WinForms) with some public properties. When I use this control, I want to databind to those properties with the DataSourceUpdateMode set to OnPropertyChanged. The datasource is a class which implements INotifyPropertyChanged.

    I'm aware of the need to create bindings against the properties and I'm doing that.

    I assumed that my usercontrol would have to implement an interface, or the properties would need to be decorated with some attribute, or something along those lines.But my research has come up blank.

    How should this be accomplished? At the moment I'm doing it by calling OnValidating() in my usercontrol whenever a property changes, but that doesn't seem right.

    I can get validation to happen if I set the CausesValidation to true on the usercontrol, but that's not very useful to me. I need to validate each child property as it changes.

    Note this is a WinForms situation.

    EDIT: Evidently I have no talent for explanation so hopefully this will clarify what I'm doing. This is an abbreviated example:

    // I have a user control
    public class MyControl : UserControl
    {
        // I'm binding to this property
        public string ControlProperty { get; set; }
    
        public void DoSomething()
        {
            // when the property value changes, the change should immediately be applied 
            // to the bound datasource
            ControlProperty = "new value";
    
            // This is how I make it work, but it seems wrong
            OnValidating();         
        }
    }
    
    // the class being bound to the usercontrol
    public class MyDataSource : INotifyPropertyChanged
    {
        private string sourceProperty;
        public string SourceProperty
        {
            get { return sourceProperty; }
            set
            {
                if (value != sourceProperty)
                {
                    sourceProperty = value;
                    NotifyPropertyChanged("SourceProperty");
                }
            }
        }
    
        // boilerplate stuff
        public event PropertyChangedEventHandler PropertyChanged;
        protected void NotifyPropertyChanged(string info)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(info));
        }
    }
    
    public class MyForm : Form
    {
        private MyControl myControl;
        public MyForm()
        {
            // create the datasource 
            var dataSource = new MyDataSource() { SourceProperty = "test" };
    
            // bind a property of the datasource to a property of the usercontrol
            myControl.DataBindings.Add("ControlProperty", dataSource, "SourceProperty",
                false, DataSourceUpdateMode.OnPropertyChanged); // note the update mode
        }
    }
    

    (I have tried this using a BindingSource, but the result was the same.)

    Now what I want to happen is that when the value of MyControl.ControlProperty changes, the change is immediately propagated to the datasource (the MyDataSource instance). To achieve this I call OnValidating() in the usercontrol after changing the property. If I don't do that, I have to wait until validation gets triggered by a focus change, which is the equivalent of the "OnValidation" update mode, rather than the desired "OnPropertyUpdate" validation mode. I just don't feel like calling OnValidating() after altering a property value is the right thing to do, even if it (kind of) works.

    Am I right in assuming the calling OnValidating() is not the right way to do this? If so, how do I notify the datasource of the ControlProperty change?

  • Igby Largeman
    Igby Largeman almost 13 years
    I'm already doing that, Phil. As I said, "The datasource is a class which implements INotifyPropertyChanged."
  • Sam
    Sam almost 13 years
    @Charles: no you're not. ControlProperty does not raise the PropertyChanged event.
  • Igby Largeman
    Igby Largeman almost 13 years
    @six: Phil isn't talking about the user control, he's talking about the datasource class. Are you saying both sides of the binding have to implement INotifyPropertyChanged? I thought it was only the datasource side, for reason.
  • Sam
    Sam almost 13 years
    @Charles: well, if ControlProperty doesn't raise an event, how would anyone know it changed?
  • Igby Largeman
    Igby Largeman almost 13 years
    That's what I want to know. It does make sense that both sides of the binding should implement INPC, but what I've read didn't seem to suggest it. Plus, I looked at some exising controls (Button, TextBox etc) and they don't seem to implement INPC, so I'm confused as to how binding works with them.
  • Igby Largeman
    Igby Largeman almost 13 years
    @six: okay, I figured it out. You can implement INPC on the control, but the conventional way is to raise events named <propertyname>Changed.
  • SolutionYogi
    SolutionYogi over 10 years
    I wish I saw this answer sooner. I spent several hours trying to figure out why Data Source was not getting updated from the User Control. +1.