50

What is the best way to bind a property to a control so that when the property value is changed, the control's bound property changes with it.

So if I have a property FirstName which I want to bind to a textbox's txtFirstName text value. So if I change FirstName to value "Stack" then the property txtFirstName.Text also changes to value "Stack".

I know this may sound a stupid question but I'll appreciate the help.

David Silva-Barrera
  • 1,006
  • 8
  • 12
cubski
  • 3,218
  • 1
  • 31
  • 33

2 Answers2

59

You must implement INotifyPropertyChanged And add binding to textbox.

I will provide C# code snippet. Hope it helps

class Sample : INotifyPropertyChanged
{
    private string firstName;
    public string FirstName
    {
        get { return firstName; }
        set
        {
            firstName = value;
            InvokePropertyChanged(new PropertyChangedEventArgs("FirstName"));
        }
    }

    #region Implementation of INotifyPropertyChanged

    public event PropertyChangedEventHandler PropertyChanged;

    public void InvokePropertyChanged(PropertyChangedEventArgs e)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null) handler(this, e);
    }

    #endregion
}

Usage :

 Sample sourceObject = new Sample();
 textbox.DataBindings.Add("Text",sourceObject,"FirstName");
 sourceObject.FirstName = "Stack"; 
Stecya
  • 22,896
  • 10
  • 72
  • 102
  • the post is tagged winforms - is this not for wpf? – Marino Šimić May 04 '11 at 12:30
  • 3
    This will update `sourceObject` when `textBox.Text` changes. You also need to subscribe to the `PropertyChanged` event to update the text box contents when `sourceObject.FirstName` changes (which was the original question). – David May 04 '11 at 12:51
  • thanks... just a quick question, would this work as two-way binding? when I change the textbox text property, would the firstname property change as well? – cubski May 04 '11 at 12:56
  • 2
    @David - this is working two ways. Just created app and tested it – Stecya May 04 '11 at 13:14
  • 1
    @Stecya - OK, sorry (I remember having to do both but that was some time ago). – David May 04 '11 at 13:41
  • 3
    @Stecya This is not entirely correct. Given the above example, it will work when I change `sourceObject.FirstName` and it will work when I change the text in the textbox in the UI and leave the textbox. However, when I update the text of the textbox in code-behind by doing `textbox.Text = "Anything"` it will not update the property in the `sourceObject`. So I think the binding update gets triggered by the .NET on the internal leave event of any control, it's not? Please correct me, when I'm wrong. For me this case is not enough to go with. – Martin Braun Sep 04 '15 at 14:14
  • 4
    To bind changes in control to the property, use this `textbox.DataBindings.Add("Text",sourceObject,"FirstName", false, DataSourceUpdateMode.OnPropertyChanged);` – yatskovsky May 10 '16 at 19:47
  • 3
    That's nice. Though I suggest using method that automatically gets property name for your convenience: ` protected void OnPropertyChanged() { if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(new System.Diagnostics.StackFrame(1).GetMethod().Name.Replace("set_", ""))); }` – Szybki Jun 28 '16 at 00:35
  • +1: A note I was not aware of but is obvious after the fact: this will not work with properties not marked as `public`. It will throw a binding error regarding the dataMember. – Joel Etherton Dec 01 '16 at 23:02
13

A simplified version of the accepted answer that does NOT require you to type names of properties manually in every property setter like OnPropertyChanged("some-property-name"). Instead you just call OnPropertyChanged() without parameters:

You need .NET 4.5 minimum. CallerMemberName is in the System.Runtime.CompilerServices namespace

public class Sample : INotifyPropertyChanged
{
    private string _propString;
    private int _propInt;


    //======================================
    // Actual implementation
    //======================================
    public event PropertyChangedEventHandler PropertyChanged;

    [NotifyPropertyChangedInvocator]
    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        var handler = PropertyChanged;
        if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
    }
    //======================================
    // END: actual implementation
    //======================================


    public string PropString
    {
        get { return _propString; }
        set
        {
            // do not trigger change event if values are the same
            if (Equals(value, _propString)) return;
            _propString = value;

            //===================
            // Usage in the Source
            //===================
            OnPropertyChanged();

        }
    }

    public int PropInt
    {
        get { return _propInt; }
        set
        {
            // do not allow negative numbers, but always trigger a change event
            _propInt = value < 0 ? 0 : value;
            OnPropertyChanged();
        }
    }
}

Usage stays the same:

var source = new Sample();
textbox.DataBindings.Add("Text", source, "PropString");
source.PropString = "Some new string";

Hope this helps someone.

Dmitry Avtonomov
  • 8,747
  • 4
  • 32
  • 45
  • I'm getting na error on "CallerMemberName". Does it has to be deffined in some place? – Antonio Mano Mar 21 '18 at 13:22
  • 1
    You need .NET 4.5 minimum. [CallerMemberName](https://msdn.microsoft.com/en-us/library/system.runtime.compilerservices.callermembernameattribute) is in the System.Runtime.CompilerServices namespace – Steven Bone May 16 '18 at 19:45
  • [NotifyPropertyChangedInvocator] does not exists even when using .net 4.6.1 and System.Runtime.CompilerServices namespace. – Shadowblitz16 Jun 18 '19 at 19:35
  • See https://stackoverflow.com/questions/23213146/what-is-notifypropertychangedinvocator-in-c-sharp-when-implements-inotifyprope – Dmitry Avtonomov Jun 22 '19 at 10:49