Observable型別

  • 1306
  • 0
  • 2013-08-09

Observable型別

在許多談論MVVM中都必定會提到Model的值有異動時需能夠即時通知並反應在UI的控制項上,但是我在實做上被限制要在WinForm。

 

在WinFrom上撰寫程式其實免不了必需要在程式中撰寫控制項的屬性控制,但是這些程式碼如果沒有好好的規劃和整理,很容易就會成為開發人員的惡夢;而這個惡夢在某些特性的表單上會特別的嚴重,例如像是高互動性的Form,這個就會需要大量的控制項操作,特別是某幾組控制項與另幾組控制項彼此是互斥的時候(例:當值為True時某些控制項是Enable,但與之相對的控制項要Disable)。

 

讓控制控制項的程式太過發散並不是一件好事,為了解此痛苦勢必是需要一些Pattern的配合,而在WinForm中大家最常提到的就是MVP甚至也有神人可以做到MVVM;無論是那一種,我們都需要讓UI與商業邏輯做個較佳的切割,不要混搭在一起,而我最近發現到如果要能夠妥善控制值異動的通知,可以採用ObservableCollection這個型別來完成,但是我看了看發現到這似乎和集合比較有關,是故,我需要一個專為非集合型別量身打造的型別:

Observable型別。

 


public class Observable<T> 
 {
     internal T mValue=default(T); //實際值

     public Observable()
     {
         Type type = typeof(T);
         if (type.IsPrimitive)
             this.mValue = default(T);
         else if (type == typeof(string))
             this.mValue = (T)(object)string.Empty;
         else if (type.IsClass)
             this.mValue = (T)Activator.CreateInstance(type);
     }

     //值異動事件
     public event EventHandler ValueChanged;
     //屬性異動事件
     public event PropertyChangedEventHandler PropertyChanged;

     //通知所有訂閱的事件
     private void NotifyValueChanged()
     {
         if (ValueChanged != null)
         {
             ValueChanged(this, new EventArgs());
         }
         if (PropertyChanged != null)
         {
             PropertyChanged(this, new PropertyChangedEventArgs("Value"));
         }
     }

     public T Value
     {
         get
         {
             return mValue;
         }
         set
         {
             SetValueSilently(value);
             NotifyValueChanged();
         }
     }

     //設定值
     public void SetValueSilently(T value)
     {
         mValue = value;
     }

     //賦值
     public static implicit operator T(Observable<T> observable)
     {
         return observable.Value;
     }

     //接受賦值
     public static implicit operator Observable<T>(T value)
     {
         return new Observable<T> { mValue = value };
     }
 }

異動部份程式碼:

   Observable在上一期發佈之後實際放在案子上發現問題很多,故再次修改接受賦值的函數,並且不採用靜態成員的方式管理物件,改以接收賦值後自動建構一個物件回傳。


 public class Person
 {
     public Observable<int> ID { get; set; }

     public Observable<string> Name { get; set; }

     public Person()
     {
         this.ID = 0;
         this.Name = string.Empty;
     }

     public Person(int id, string name)
     {
         this.ID = id;
         this.Name = name;
     }

     public object Clone()
     {
         return new Person(this.ID.Value, this.Name.Value);
     }
 }

在Model的設計上,若採用貧血式的Model可以考慮上面範例的寫法。

而控制項在處理資料繫結方面也有提供方法,這裡我們先以TextBox的Text屬性為範例:


Person model = new Person();  
txtID.DataBindings.Add("Text", model, "ID.Value", false, 
              DataSourceUpdateMode.OnPropertyChanged);

是否讓屬性採用Observable型別都不會影響資料繫結的功能,但是採用Observable型別的優點在於當值改變時可以訂閱屬性或是值改變的事件,並在事件中進行其它控制。