简体   繁体   中英

Why does raising the PropertyChanged event cause other controls to update

I have a view model that has several properties that are databound to several controls.

When I raise PropertyChanged on one of them, the controls unexpectedly all update. I would expect only the one I am raising the event on to update.

For my form, I have this:

    public partial class MainForm : Form
    {
        AmountCalculatorVM amountCalculatorVM;
        public MainForm()
        {
            InitializeComponent();
        }

        private void setBindings()
        {
            textBoxTotalAmount.DataBindings.Add("Text", amountCalculatorVM, "TotalAmount");
            textBoxAverage.DataBindings.Add("Text", amountCalculatorVM, "Average",true, DataSourceUpdateMode.Never,null, "#.00");
            textBoxCount.DataBindings.Add("Text", amountCalculatorVM, "Count");
            listBoxLineAmounts.DataSource = amountCalculatorVM.Amounts;
        }

        private void MainForm_Load(object sender, EventArgs e)
        {
            amountCalculatorVM = new AmountCalculatorVM();
            setBindings();
        }

        private void buttonAddAmount_Click(object sender, EventArgs e)
        {
            if (int.TryParse(textBoxLineAmount.Text.Replace(",", ""), out int amount))
            {
                amountCalculatorVM.Amounts.Add(amount);
                textBoxLineAmount.Text = "";
                textBoxLineAmount.Focus();
            }
        }


        private void buttonClear_Click(object sender, EventArgs e)
        {
            textBoxLineAmount.Text = "";
            amountCalculatorVM.Amounts.Clear();
            textBoxLineAmount.Focus();
        }

    }

Then, for my view model, I have this:

    class AmountCalculatorVM : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        private void OnPropertyChanged([CallerMemberName] string propertyName = "")
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }

        private readonly AmountList amounts;
        public BindingSource Amounts { get; }
        public int TotalAmount => amounts.TotalAmount;
        public int Count => amounts.Count;
        public decimal Average => amounts.Average;
        public AmountCalculatorVM()
        {
            amounts = new AmountList();
            Amounts = new BindingSource();
            Amounts.DataSource = amounts;
            Amounts.ListChanged += Amounts_ListChanged;
        }

        private void Amounts_ListChanged(object sender, ListChangedEventArgs e)
        {

            //Any one of these will cause all three textboxes to update in the form
            //I would think that with Count and Average commented out, the Count and 
            //Average textboxes would not update.
            OnPropertyChanged("TotalAmount"); 
            //OnPropertyChanged("Count"); 
            //OnPropertyChanged("Average");

            //Using any other word will not
            //OnPropertyChanged("SomeOtherRandomWord");

        }
    }

Here is the AmountList class for reference:

 class AmountList : List<int>
    {
        public int TotalAmount
        {
            get
            {
                int total = 0;
                foreach (int amount in this)
                {
                    total += amount;
                }
                return total;
            }
        }

Now, unexpectedly, all three textboxes update if an item is added to the amounts list, which fires ListChanged, and then in turn, the PropertyChanged event.

It doesn't matter which of the three properties I fire PropertyChanged on, but it won't work if I use a different value - it needs to be either TotalAmount, Count, or Average.

I can't understand this behaviour. I would have expected only the text box bound to TotalAmount to be updated, and not the other two, since nothing seems to be notifying them that an update has occurred.

Any ideas?

Why don't you implement the propertychanged like this:

public class Data : INotifyPropertyChanged
{
    // boiler-plate
    public event PropertyChangedEventHandler PropertyChanged;
    protected virtual void OnPropertyChanged(string propertyName)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
    }
    protected bool SetField<T>(ref T field, T value, string propertyName)
    {
        if (EqualityComparer<T>.Default.Equals(field, value)) return false;
        field = value;
        OnPropertyChanged(propertyName);
        return true;
    }

    // props
    private string name;
    public string Name
    {
        get { return name; }
        set { SetField(ref name, value, "Name"); }
    }
}

You can control now, in the setter, which property fires the event:

private string name;
public string Name
{
    get { return name; }
    set { SetField(ref name, value, "Name"); }
}

you know what I mean?

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM