【问题标题】:C# MVVM Calculating TotalC# MVVM 计算总计
【发布时间】:2010-06-09 17:10:28
【问题描述】:

我需要根据所选价格和数量计算交易价值。怎么可能

以下是我的 ViewModel:

class ViewModel : ViewModelBase
{
    public Trade Trade
    {
        get { return _trade; }
        set { SetField(ref _trade, value, () => Trade); }
    } private Trade _trade;

    public decimal TradeValue
    {
        get { return Trade.Amount * Trade.Price; }
    } 

}

ViewModelBase 继承 INotifyPropertyChanged 并包含 SetField()

以下是 Trade 类:

public class Trade : INotifyPropertyChaged
{
    public virtual Decimal Amount
    {
        get { return _amount; }
        set { SetField(ref _amount, value, () => Amount); }
    } private Decimal _amount;
    public virtual Decimal Price
    {
        get { return _price; }
        set { SetField(ref _price, value, () => Price); }
    } private Decimal _price;
    ......
}

我知道,由于设计原因,我的 TradeValue 只会计算一次(在第一次请求时),并且当金额/价格发生变化时 UI 不会更新。实现这一目标的最佳方法是什么?

非常感谢任何帮助。

更新: INotifyPropertyChanged 实施:

    protected virtual void OnPropertyChanged(string propertyName)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
    }
    protected virtual void OnPropertyChanged<T>(Expression<Func<T>> selectorExpression)
    {
        if (selectorExpression == null)
            throw new ArgumentNullException("selectorExpression");
        var body = selectorExpression.Body as MemberExpression;
        if (body == null)
            throw new ArgumentException("The body must be a member expression");
        OnPropertyChanged(body.Member.Name);
    }
    protected bool SetField<T>(ref T field, T value, Expression<Func<T>> selectorExpression)
    {
        if (EqualityComparer<T>.Default.Equals(field, value)) return false;
        field = value;
        OnPropertyChanged(selectorExpression);
        return true;
    }

两个答案都有效,现在我不知道选择哪一个作为最佳答案。 Luke 的答案似乎更好,因为代码重复似乎更少,尽管简单地监听 OnPropertyChanged 事件似乎更轻量级。

【问题讨论】:

  • 你能把 SetField 的代码贴出来吗?

标签: c# wpf data-binding mvvm


【解决方案1】:

这是我通常的做法:

private bool has_inited_tradevalue_linkage = false;
public decimal TradeValue
{
    get 
    { 
        if(!has_inited_tradevalue_linkage)
        {
            has_inited_tradevalue_linkage = true;
            this.PropertyChanged += (_, e) => 
            {
                if(e.PropertyName.Equals("Amount") || e.PropertyName.Equals("Price"))
                    OnPropertyChanged("TradeValue");
            };
        }
        return Trade.Amount * Trade.Price; }
}

这样链接是懒惰地构造的(所以你不会发布没人感兴趣的事件),所有的逻辑仍然包含在这个属性中。相反,您可以在PriceAmount 的设置器中添加对OnPropertyChanged("TradeValue") 的调用,但这会“弄脏”课程的其余部分(至少在我看来)。我已经非常成功地使用了这种模式来创建类似的“依赖”属性。

另一种选择是专门跟踪依赖关系:

在 ViewModelBase 中你可以添加这个:

私有字典> DependencyMap = new Dictionary>(); 受保护的无效 AddPropertyDependency(字符串属性,字符串依赖属性) { if(DependencyMap.ContainsKey(prop)) { 依赖映射[prop].Add(dependantProp); } 别的 { DependencyMap[prop] = new List{dependantProp}; } }

然后在您的OnPropertyChanged 方法中:

protected void OnPropertyChanged(String prop)
{
    var eh = PropertyChanged;
    if(eh != null)
    {
        eh(this, new PropertyChangedEventArgs(prop);
        if(DependencyMap.ContainsKey(prop))
        {
            foreach(var p in DependencyMap[prop])
                OnPropertyChanged(p);//recursive call would allow for arbitrary dependencies
        }
    }
}

然后在您的类构造函数中,您只需定义您的属性依赖项:

public ViewModel()
{
    AddPropertyDependency("Amount", "TradeValue");
    AddPropertyDependency("Price", "TradeValue");
}

这绝对是一个更通用的解决方案,大部分更改都在您的基类中。

【讨论】:

  • 但是这个变种也弄脏了类。在类中有一些布尔值来确定一个值是否应该被刷新在我看来并不好。此外,这个变体对可读性不太好。
  • 在代码中的什么位置监听交易对象的属性变化?只是无法理解更新位(我了解列表的用途等)
【解决方案2】:

在您的 VM 类中的 Trade 属性的 set 方法中订阅 Trade 类的 PropertyChanged 事件。在该事件处理程序中,为属性 TradeValue 发布属性更改。

【讨论】:

  • 这似乎是一个更好的解决方案。订阅 Trade 类的 PropertyChanged,如果是 Trade 或 Amount,则为 TradeValue 字段调用 OnPropertyChanged 事件。
  • 是的,至少我的意思是这样,通知客户他们需要获取 TradeValue,因为价格或金额发生了变化。
  • 我通常采用这种方法,只是我给“计算”属性(在这种情况下为 TradeValue)一个封装通知的私有设置器。这清楚地表明通知是由计算值的更新触发的,并且具有缓存计算值直到下一次基础属性更改的额外好处。
【解决方案3】:
private void UpdateValue()
        {
            this.Value = this.Acres * this.YieldPerAcre * this.UnitPrice;
        }      
       public double Acres
        {
            get { return _cropProductionRecord.Acres; }
            set
            {
                _cropProductionRecord.Acres = value;
                OnPropertyChanged("Acres");
                UpdateValue();
            }
        }
        public double YieldPerAcre
        {
            get { return _cropProductionRecord.YieldPerAcre; }
            set
            {
                _cropProductionRecord.YieldPerAcre = value;
                OnPropertyChanged("YieldPerAcre");
                UpdateValue();
            }
        }

        public double UnitPrice
        {
            get { return _cropProductionRecord.UnitPrice; }
            set
            {
                _cropProductionRecord.UnitPrice = value;
                OnPropertyChanged("UnitPrice");
                UpdateValue();
            }
        }
        public double Value
        {
            get { return _cropProductionRecord.Value; }
            set
            {
                _cropProductionRecord.Value = value;
                OnPropertyChanged("Value");
            }
        }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-02-26
    • 2014-09-16
    • 1970-01-01
    • 2017-10-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多