序言
第一篇講解了UI與業務邏輯分層的框架(UIMediator)的使用。本篇將說明該框架的原理及代碼實現。
整體結構
UI與后台類綁定主要分為UI輸入->后台屬性,后台屬性-UI更新兩部分,為符合依賴倒置原則,分別抽象出IUIToProperty和IPropertyToUI兩個接口。
為了匹配WinForm的窗體事件委托方法格式(object sender, EventArgs e)兩個接口方法都實現了多態。
Mediator采用了模板方法的設計模式,實現了整個綁定方法的算法框架,子類只需實現ChangeType、BindPropertyValue、BindUIValue三個抽象方法即可。
TextBoxMediator、RadioButtonMediator、CheckBoxMediator為Mediator子類,根據各個不同的WinForm控件而實現的中介類,實現了上述三個抽象方法。
ChangeType:將Control基類轉換為具體的控件類;
BindPropertyValue:實現UI輸入->后台屬性;
BindUIValue:實現后台屬性-UI更新。
UML圖如下所示。接下來講解具體代碼實現。
依賴倒置
UI輸入->后台屬性接口:IUIToProperty
public interface IUIToProperty { void BindPropertyValue(object sender, EventArgs e); void BindPropertyValue(PropertyInfo prop); }
后台屬性-UI更新接口:IPropertyToUI
public interface IPropertyToUI { void BindUIValue(object sender, EventArgs e); void BindUIValue(PropertyInfo prop); }
Mediator模板類
public abstract class Mediator:IUIToProperty,IPropertyToUI { protected Type type; protected object BindInstance; protected string BindProperty; public void Bind<T>(Control control, T BindInstance, string BindProperty) where T : class ,IPropertyChange { this.BindInstance = BindInstance as T; this.BindProperty = BindProperty; type = typeof(T); BindInstance.PropertyChanged += new EventHandler(BindUIValue); ChangeType(control); BindPropertyValue(null, null); } public void BindPropertyValue(object sender, EventArgs e) { BindPropertyValue(GetProperty()); } private PropertyInfo GetProperty() { return type.GetProperties().First(c => c.Name == BindProperty); } public void BindUIValue(object sender, EventArgs e) { BindUIValue(GetProperty()); } public abstract void BindPropertyValue(PropertyInfo prop); protected abstract void ChangeType(Control control); public abstract void BindUIValue(PropertyInfo propertyInfo);
TextBoxMediator類
public class TextBoxMediator:Mediator { private TextBox tb; public override void BindPropertyValue(System.Reflection.PropertyInfo prop) { if (prop.PropertyType.IsValueType && string.IsNullOrEmpty(tb.Text)) { prop.SetValue(BindInstance, 0, null); return; } try { object value = Convert.ChangeType(tb.Text, prop.PropertyType); prop.SetValue(BindInstance, value, null); } catch (FormatException fex) { throw fex; } catch (Exception ex) { throw ex; } } protected override void ChangeType(Control control) { tb = control as TextBox; tb.TextChanged+=new EventHandler(BindPropertyValue); } public override void BindUIValue(System.Reflection.PropertyInfo prop) { tb.Text = prop.GetValue(BindInstance, null).ToString(); } }
CheckBoxMediator類
public class CheckBoxMediator:Mediator { private CheckBox cb; public override void BindPropertyValue(PropertyInfo prop) { prop.SetValue(BindInstance, cb.Checked, null); } protected override void ChangeType(Control control) { cb = control as CheckBox; cb.CheckedChanged += new EventHandler(BindPropertyValue); } public override void BindUIValue(PropertyInfo prop) { cb.Checked = Convert.ToBoolean(prop.GetValue(BindInstance, null)); } }
RadioButtonMediator類
public class RadioButtonMediator:Mediator { RadioButton rb; public override void BindPropertyValue(System.Reflection.PropertyInfo prop) { prop.SetValue(BindInstance, rb.Checked, null); } protected override void ChangeType(System.Windows.Forms.Control control) { rb = control as RadioButton; rb.CheckedChanged += new EventHandler(BindPropertyValue); }
public override void BindUIValue(System.Reflection.PropertyInfo prop) { rb.Checked = Convert.ToBoolean(prop.GetValue(BindInstance, null)); } }
關於后台屬性-UI更新的說明
分析下Mediator類中的Bind方法
public void Bind<T>(Control control, T BindInstance, string BindProperty) where T : class ,IPropertyChange { this.BindInstance = BindInstance as T; this.BindProperty = BindProperty; type = typeof(T); BindInstance.PropertyChanged += new EventHandler(BindUIValue); ChangeType(control); BindPropertyValue(null, null); }
泛型T有一個IPropertyChange的約束,具有PropertyChanged事件,用來注冊綁定BindUIValue方法。
IPropertyChange的代碼如下
public interface IPropertyChange { event EventHandler PropertyChanged; void UpdateUI(); }
由於.NET只支持類的單繼承,為避免框架對代碼的侵入性選擇了接口繼承。
后台類通過繼承IPropertyChange,在UpdateUI實現方法中調用PropertyChanged事件。
在需要后台驅動UI更新時調用UpdateUI方法即可。
public void UpdateUI() { PropertyChanged(null, null); }