序言
第一篇讲解了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); }