【问题标题】:Update 2 properties at once in MVVM在 MVVM 中一次更新 2 个属性
【发布时间】:2014-12-17 09:03:18
【问题描述】:

查看Windows Power Options,turn off the display中有两个combo-box,一个是Battery,另一个是Plugged in

现在要求Battery 中的值应始终小于Plugged in 中的值。

所以如果Battery 最初设置为25 分钟,然后Plugged in 设置为10 分钟,Battery 的值应自动更改为5 分钟。 更改一个值可能会影响另一个值。

代码是实现INotifyPropertyChanged 的普通ViewModel。

public class MyViewModel : INotifyPropertyChanged 
{
    private uint pluggedIn;
    public uint PluggedIn 
    {
        get { return pluggedIn;}
        set 
        {
            if (pluggedIn != value)
            {
                pluggedIn = value;
                OnPropertyChanged("PluggedIn");
            } 
        }
    } 
    public uint Battery{...} //the same implementation
}

我修改了设置器以应用规则。

        set 
        {
            if (pluggedIn != value)
            {
                pluggedIn = value;
                OnPropertyChanged("PluggedIn");

                if (Battery >= PluggedIn)
                {
                    Battery = FindTheValueLessThanPluggedIn(); //this can potentially modify the value of PluggedIn from the setter of Battery.
                }
            } 
        }

问题。

您可以看到调用 Bettery 的 setter 也可能会影响 PluggedIn 的值,如果我有其他要求,我将不得不编写一些难以阅读/维护的代码来实现这个看似简单的任务。

问题

我可以先计算两个属性的最终值,我需要一个“原子”操作,可以一次更新两个属性(不相互影响),在这个“原子”操作完成后,触发@987654333 @事件为两个属性更新UI。

再想一想

在发布后阅读我的问题后,我想我已经回答了我自己的问题 - 设置支持字段而不是调用 setter,但其他更好的方法?

CalculateNewVaules(out newValue1, out newValue2);
pluggedIn = newValue1;
battery = newValue2;
OnPropertyChanged("PluggedIn");
OnPropertyChanged("Battery");

【问题讨论】:

    标签: c# wpf mvvm inotifypropertychanged


    【解决方案1】:

    这是怎么回事:

       set 
        {
            if (pluggedIn != value)
            {
                pluggedIn = value;
    
                if (Battery >= PluggedIn)
                {
                    _battery = FindTheValueLessThanPluggedIn();
                    OnPropertyChanged("Battery");
                }
    
                OnPropertyChanged("PluggedIn");
            } 
    

    请注意,电池的设置者永远不会被调用。

    对一个属性的两个OnPropertyChanged 调用是我处理所有与您的情况相似的案例的方式,并且它们运行良好。

    另外,我也使用了这种技术:

    YourViewModel(){
      PropertyChanged += PropertyChangedHandler;
    }
    
    void PropertyChangedHandler(object sender, PropertyChangedEventArgs e)
    {
        switch (e.PropertyName)
        {
            case "Battery":
                PluggedIn = CalculateNewValueForPluggedIn();
                break;
    
            case "PluggedIn":
                Battery = CalculateNewValueForBattery();
                break;
        }
    }
    

    【讨论】:

    • 在您的代码中,如果新的_battery 值等于旧值,则无论如何都会触发事件。所以你必须在这里额外检查它,这会让你的代码更像是复制粘贴(因为你也需要在Battery setter 中检查它)。
    • @dymanoid:好吧,注意方法名称“FindTheValueLessThanPluggedIn”,根据该定义,如果Battery >= PluggedIn 评估为真,则始终会触发该事件。
    • 是的,这很重要,设置 _battery 而不是 setter。
    • @Chris,我同意,但仅限于这种特殊情况。不过,我不建议将这种方法用于一般用途。
    • @kennyzx,我一般同意“H W”的回答。这就是你应该这样做的方式。从属性中重构“保持属性同步”逻辑,并添加监听器。您可以订阅NotifyPropertyChanged 活动。
    【解决方案2】:

    我的建议是将 OnChange 事件绑定到您在 ViewModel 中实现的“原子”方法。这些方法应该计算两个下拉菜单的最终值,然后设置属性 PluggedIn 和 Battery。因此,您根本不需要修改 Properties 的 setter,理论上甚至不需要延迟触发 PropertyChanged 事件(让 Property setter 为您做这件事)。

    通过将所有逻辑外包给它自己的方法并保持设置器简单,属性不会相互干扰。但是请注意,虽然在您使用掩码时会满足所有限制,但这将允许您通过在代码中手动设置属性来覆盖您的限制。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-06-30
      • 1970-01-01
      • 1970-01-01
      • 2019-03-03
      • 2010-09-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多