2010-06-09 129 views
1

我需要根據選定的價格和數量計算交易價值。如何才能C#MVVM計算總計

以下是我的視圖模型:

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

以下是貿易類:

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

這兩個答案的工作,現在我不知道該選擇爲最佳答案是哪一個。盧克的答案似乎更好,因爲似乎有更少的代碼重複,但只是聽OnPropertyChanged事件似乎更輕量級。

+0

你可以發佈SetField的代碼嗎? – luke 2010-06-09 17:41:28

回答

1

在您的VM類的Trade屬性的set方法中訂閱Trade類中的PropertyChanged事件。在該事件處理程序中,發佈屬性TradeValue的屬性已更改。

+0

這似乎是一個更好的解決方案。在Trade類別上訂閱PropertyChanged,如果它的Trade或Amount,則爲TradeValue字段調用OnPropertyChanged事件。 – 2010-06-09 19:54:54

+0

是的這就是我的意思,至少要通知客戶他們需要獲得TradeValue,因爲價格或金額已更改。 – 2010-06-09 20:38:04

+0

我通常採取這種方法,只有我給了'計算'屬性(TradeValue在這種情況下)封裝通知的私人setter。這就清楚地表明,通知是由計算值的更新觸發的,並具有緩存計算值的附加好處,直到下一個基礎屬性更改。 – 2010-06-09 22:46:59

2

這是怎麼了,我通常做到這一點:

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 =新詞典>(); protected void AddPropertyDependency(String prop,String dependantProp) if(DependencyMap.ContainsKey(prop)) DependencyMap [prop]。加入(dependantProp); } else { 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"); 
} 

這絕對是一個更通用的解決方案,最的變化在你的基類中。

+0

但是這個變體也會污染課程。在類內部有一些確定值是否應刷新的布爾值在我看來並不好。另外,這個變體對於可讀性不太好。 – KroaX 2010-06-09 20:03:02

+0

代碼中的哪個位置在監聽交易對象中的屬性更改?只是無法讓我的頭在更新位(我明白列表是什麼等) – 2010-06-10 20:22:24

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