views:

45

answers:

2

Hi everyone! I'm new to data binding and encountered the following oddity today which I fail to understand:

I have a form with two labels (labelA and labelB) and two buttons (buttonA and buttonB).

The form hosts an object (called "formState") with two properties (CountA and CountB). labelA.Text is data-bound to formState.CountA, labelB.Text is data-bound to formState.CountB.

When buttonA is pressed, it makes formState raise the PropertyChange event with "CountA" as property name. When buttonB is pressed, it raises formState.PropertyChange with "CountB" as property name.

I would expect that only labelA gets updated when buttonA is pressed, and only labelB gets updated when buttonB is pressed. However, both labels are updated when I press any button. I can see this because the CountA and CountB properties increment and return a counter each time their values are read. Code below.

Does anyone have an explanation for this behavior?

public partial class Form1 : Form
{
    private FormState formState = new FormState();

    public Form1()
    {
        InitializeComponent();

        labelA.DataBindings.Add(
            new Binding("Text", formState, "CountA"));

        labelB.DataBindings.Add(
            new Binding("Text", formState, "CountB"));
    }

    private void buttonA_Click(object sender, EventArgs e)
    {
        formState.raisePropertyChangedEvent("CountA");
        // both labelA and labelB get updated - why not just labelA?
    }

    private void buttonB_Click(object sender, EventArgs e)
    {
        formState.raisePropertyChangedEvent("CountB");
        // both labelA and labelB get updated - why not just labelB?
    }

    public class FormState : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        private int countA = 0;
        private int countB = 0;

        public int CountA
        {
            get { return ++countA; }
        }

        public int CountB
        {
            get { return ++countB; }
        }

        public void raisePropertyChangedEvent(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }
+2  A: 

It may be a naive consumer of observable objects. Naive consumers of INotifyPropertyChangedObjects may ignore the property name and just reevaluate the whole thing. One might imagine the class looking like:

class NaiveConsumer
{
   void Foo(INotifyPropertyChanged observable)
   {
       observable.PropertyChanged += PropertyChangedHandler;
   }

   void PropertyChangedHandler(object sender, PropertyChangedEventArgs e)
   {
      // Evaluate all properties, even though only 1 prop changed.
      this.NameTextBox.Text = observable.Name;
      this.AgeTextBox.Text = observable.Age;
   }
}
Judah Himango
It doesn't seem to be completely the case like that - I tested raising the PropertyChanged event with an invalid property name, and none of my labels were updated. However I also tested raising it with 'null' as property name - which again updated both labels. In any case, unless there is a problem with my code, all this means that data-bound .NET controls don't react to PropertyChanged events as I would expect: In the case of a complex data-bound user interface, every data change will cause the entire user interface to update. Sounds quite inefficient.
Johannes Petzold
+1  A: 

Your class's responsibility is to provide change notification - its the consumer's responsibility to decide how to deal with it. If it choses to check every property on any property changed, it can do that.

vanja.