pfthroaway
pfthroaway

Reputation: 85

Data Binding doesn't work when I assign a new object instance to the bound variable

I have an object that I have bound to a control on a form using C# WinForms (targetting .NET 4.5.2). I have implemented INotifyPropertyChanged, and when I modify a Property of this object, it updates on the Form's control as expected. However, when I change this object's instance to a new instance, it will no longer update the control, even if I try to modify a specific Property.

class User : INotifyPropertyChanged
{
    string _name = "";

    public string Name
    {
        get { return _name; }
        set { _name = value; OnPropertyChanged("Name"); }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    protected void OnPropertyChanged(string property)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));
    }

    public User() { }

    public User(string name)
    {
        Name = name;
    }

    public User(User otherUser)
    {
        Name = otherUser.Name;
    }
}

and on the Form I have

User currentUser = new User("Example Name");
lblName.DataBindings.Add("Text", currentUser, "Name");

which updates properly. I can change name using currentUser.Name = "Blahblah"; and it works fine. When I try to call currentUser = new User("New Name");, it will no longer update no matter what I do.

It is my understanding that the specific instance of the object is what the controls are bound to. My primary goal is to not have to manually go through each Property of larger objects and manually change everything over every single time I want to change instances.

My question: is there a way to change instances of an object without removing the binding to the control?

Upvotes: 7

Views: 3027

Answers (2)

Reza Aghaei
Reza Aghaei

Reputation: 125227

Ivan showed you how to solve the problem. Here in this answer I'll try to show you what the problem is and why it works this way.

Short answer

Your label is bound to an object not the variable. The variable is just like a pointer to the object. So replacing the variable doesn't have any impact on the object which your label is using as data source. Just the variable points to the new object. But your label is using the previous object. It has its own pointer to the previous object.

Consider these facts to know what is happening:

  1. User currentUser = new User("Example Name");

    When you assign an object to a variable, in fact the variable points to the object.

  1. lblName.DataBindings.Add("Text", currentUser, "Name");
    You performed data-binding to the object. The control will be bound to the object not the variable.
  1. currentUser.Name = "Blahblah";

    You changed the value of Name property and since you implemented INotifyPropertyChanged the control will be notified of changes and will refresh its Text.

But the main statement which didn't work as you expected:

  1. currentUser = new User("New Name");

    Here you made the currentUser variable point to another object. It doesn't have anything to do with the previous object which it was pointing to. It just points to another object.

    The Binding which you added to the label, still uses the previous object.

    So it's normal to not have any notification, because the object which is in use by the label didn't changed.

How the BindingSource solved the problem?

The BindingSource raises ListChanged event when you assign a new object to its DataSource and it makes the BindingObject fetch new value from DataSource and refresh Text property. You can take a look at this post: How to make a binding source aware of changes in its data source?

Upvotes: 3

Ivan Stoev
Ivan Stoev

Reputation: 205649

To get the desired behavior, you should not bind directly to the concrete instance like you do currently:

lblName.DataBindings.Add("Text", currentUser, "Name");

Instead, you need some intermediary. The easiest is to use the BindingSource component for that purpose.

Add a BindingSource field to the form:

private BindingSource dataSource;

Then initialize it and bind the controls to it one time (usually in form Load event):

dataSource = new BindingSource { DataSource = typeof(User) };
lblName.DataBindings.Add("Text", dataSource, "Name");
// ...

Now anytime you want to bind to a User instance, you simply assign it to the DataSource property of the BindingSource:

initial:

dataSource.DataSource = new User("Example Name");

later:

dataSource.DataSource = new User("New Name"); 

Upvotes: 6

Related Questions