【问题标题】:Why does raising the PropertyChanged event cause other controls to update为什么引发 PropertyChanged 事件会导致其他控件更新
【发布时间】:2021-02-18 21:27:39
【问题描述】:

我有一个视图模型,它具有多个属性,这些属性数据绑定到多个控件。

当我在其中一个上引发 PropertyChanged 时,控件出乎意料地全部更新。我希望只有我提出事件的那个会更新。

对于我的表单,我有这个:

    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();
        }

    }

然后,对于我的视图模型,我有这个:

    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");

        }
    }

这里是 AmountList 类供参考:

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

现在,出乎意料的是,如果将一个项目添加到金额列表中,所有三个文本框都会更新,这会触发 ListChanged,然后依次触发 PropertyChanged 事件。

我触发 PropertyChanged 的​​三个属性中的哪一个无关紧要,但如果我使用不同的值,它将不起作用 - 它需要是 TotalAmount、Count 或 Average。

我无法理解这种行为。我预计只有绑定到 TotalAmount 的文本框会被更新,而不是其他两个,因为似乎没有通知他们发生了更新。

有什么想法吗?

【问题讨论】:

  • 呃,您似乎是在说您有一个金额列表,以及一个用于 cailvialtobg 的机制,例如列表中所有金额的计数或平均值,而您不期望/想要平均值/count 当您在列表中添加/删除金额时更新。这看起来很奇怪?
  • 并不是我不希望这种情况发生。只是它似乎不应该发生。我想了解,以免将来无意中更新控件。
  • 不应该发生什么?很确定如果我制作了一个将平均值计算为 100 项的机制,如果我添加第 101 项,我希望它重新计算?
  • 由于使用“TotalAmount”作为 eventArgs 参数引发了 PropertyChanged 事件,我看不到计数和平均文本框如何知道要更新。这个问题是关于尝试更多地理解数据绑定机制,而不是在这种特殊情况下的首选结果。
  • 您已将控件绑定到您提到的三个属性。当您更改属性值(引发PropertyChanged 事件的属性)时,BindingSource 会传播PropertyChanged 事件,因此所有绑定控件都会根据Current 对象的值进行更新。 误解是您认为是您的 Amounts_ListChanged 处理程序导致(或应该导致)PropertyChanged 事件:这已经发生,您的处理程序允许您在 ListChanged 事件时采取行动被提升了。

标签: c# .net winforms data-binding


【解决方案1】:

你为什么不这样实现 propertychanged:

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"); }
    }
}

您现在可以在 setter 中控制触发事件的属性:

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

你知道我的意思吗?

【讨论】:

  • 感谢您的回复,但我不确定在这种情况下是否有帮助,因为没有设置任何属性。
猜你喜欢
  • 2016-03-30
  • 1970-01-01
  • 2010-12-07
  • 1970-01-01
  • 1970-01-01
  • 2013-04-25
  • 2023-02-13
  • 2011-12-23
  • 1970-01-01
相关资源
最近更新 更多