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型別的優點在於當值改變時可以訂閱屬性或是值改變的事件,並在事件中進行其它控制。