众所周知,.Net有一项很强大的功能即数据绑定功能,它能将对象的属性绑定到用户输入的界面上的各种控件上。在WinForm的编程中,大家常见的用法是将从数据库或者文件中读取的信息绑定到Datagrid或者DataList这样一些控件上做界面显示和编辑吧!呵呵,其实数据绑定还可以将对象的属性绑定在TextBox或者ComboBox这样的控件上。由此我们可以将界面上的用户输入元素的合法性验证或者是交互都放在同一个对象中进行管理,这也正是中介者模式(Mediator)的思想。最重要的是,我们可以通过改变对象的状态使得我们在一个控件中的数值发生改变,另一些关联控件的数据也发生相应变化,而这些都是在我们定义的这个中介对象中进行控制,而不需要写任何依赖于界面的控制代码,因此这个对象可以应用在不同的界面下,而不需要发生任何变化!下面我们举个简单的例子:
相信大家都做过这样的程序,比如根据前一个ComboBox的List Item中选定的值产生后一个ComboBox的ListItem,如图,根据选择的省,产生其下的城市名称:
当然这里的模型很简单,完全可以在界面上完成两个Combo之间的逻辑代码的实现,但是如果界面元素非常丰富,而用户逻辑很复杂的情况呢?从设计模式可以知道,我们应该建立一个Intermediator对象来完成各输入控件之间的逻辑控制。但是请注意,如果我们如下这样的代码,该Intermediator显然只能由一个对话框中的元素使用:
public class Intermediator
{
//......
public void UpdateControls()
{
if (cbProv.SelectedText == "武汉")
{
List<string> cityList = GetCityList("武汉");
cbCity.DataSource = cityList;
//......
}
}
}
在.Net中,我们可以用数据绑定功能,建立一个完全独立的Intermediator, 它可以不考虑界面元素的影响,而专注于控制若干用户输入之间的逻辑实现。
先说明一下,这里的代码仅仅演示Intermediator和数据绑定的思想,如何获取数据来源显然不能象代码中那样,在正规的程序开发中返回string进行比较是很不合理的,而且中文字符也不能硬编码到代码中,这里仅仅是为了突出重点,您可以从数据库或XML文件中定义这些城市省份的拓扑关系,而不是象本文一样:)
该Intermediator的代码如下:
public class AddressMediator { private string _prov; private string _city; private List<Control> controlList;
public AddressMediator() {
_prov = "湖北"; _city = "武汉";
}
public void AddBinding(string bindProp, Control control, string propertyName) { if (controlList == null) controlList = new List<Control>(); control.DataBindings.Add(bindProp, this, propertyName);
if (controlList.IndexOf(control) == -1)controlList.Add(control); }
private string[] GetProvinceList() { return new String[]{"湖北", "浙江", "江西"}; }
private string[] GetCityList(string prov) { if (prov == "湖北") return new string[] { "武汉", "襄樊", "黄石" }; else if (prov == "浙江") return new string[] { "杭州", "温州", "宁波" }; else if (prov == "江西") return new string[] { "九江","南昌"}; return null; }
public string[] ProvinceList { get { return GetProvinceList(); } }
public string[] CityList { get { return GetCityList(_prov); }
}
public string CurrentProvince { get { return _prov; } set { _prov = value;
}
}
public string CurrentCity { get { return _city; } set { _city = value; } }
public string FullName { get { return _prov + _city; }
}
public Color ViewColor { get { if (_city == "武汉") return Color.Red; return Color.Blue; } }
/// <summary> /// 更新所有数据绑定的控件 /// </summary> /// <param name="sourceControl"></param> public void UpdateBindedControls(Control sourceControl) { foreach (Control c in controlList) { c.Focus(); } sourceControl.Focus();
} } |
请注意AddBinding方法,该方法将需要进行绑定的数据控件绑定到Intermediator的各种属性上,而Control控件提供了这样的支持,因此我可以把任何控件的属性和Intermediator的属性进行绑定,这样intermediator将与界面特定的Control解耦。
值得一提的是,UpdateBindedControls,该方法用于当Intermediator的状态发生变化时更新相关控件的状态,这样做是因为部分控件的属性变化了,在没被Lost Focus以前,并不会使其他控件重新向Intermediator请求值,这里或许有更好的解决方法,不过我尝试过很多种方式了,因为Intermediator里面的成员状态变化了并不一定会让控件认为它所绑定的Intermediator对象属性变化了,因此这些控件状态并不会更新。
最后界面调用是很方便的:
1.在Form中加入中介对象:
private AddressMediator _addrM = new AddressMediator();
2.Form_Load中加入:
_addrM.AddBinding("DataSource", cbProv, "ProvinceList");
_addrM.AddBinding("SelectedItem", cbProv, "CurrentProvince");
_addrM.AddBinding("DataSource", cbCity, "CityList");
_addrM.AddBinding("SelectedItem", cbCity, "CurrentCity");
_addrM.AddBinding("ForeColor", tbFullName, "ViewColor");
_addrM.AddBinding("Text", tbFullName, "FullName");
3.在两个Combo的SelectedChangeCommited中分别加入:
_addrM.UpdateBindedControls(cbProv);
_addrM.UpdateBindedControls(cbCity);
这里有意加了个TextBox,一是展现下其实不仅是文本,即便前景颜色都是可以这样绑定的,同样可以由Intermediator控制,二是TextBox同样可以用来做数据绑定,实现Validate等功能。
以上是我对Intermediator模式和绑定的一些想法,不足之处多多,请高手指点,谢谢。