四、通过映射集合实现数据绑定
通过《上篇》我们知道,DataBinder提供两种数据绑定方式:一种是直接通过传入数据实体对象和容器控件对具有匹配关系的所有子控件进行绑定;另外一种则是通过调用上面BuildBindingMappings静态方法建立的BindingMapping集合,然后再借助于这个集合进行数据绑定。这两种方式的数据绑定对应于如下两个重载的BindData方法:
- public class DataBinder
- {
- //...
- public void BindData(object entity, Control container, string suffix = "");
- public void BindData(object entity,IEnumerable<BindingMapping> bindingMappings);
- }
已经上在内部,上面一个方法也是需要通过调用BuildBindingMappings来建立映射。数据绑定始终是根据BindingMapping集合进行的。由于在BindingMapping中已经定义了完成数据绑定所需的必要信息,数据绑定的逻辑变得很简单。具体来说,数据绑定的逻辑是这样的:遍历所有的集合中每个BindingMapping,根据DataSourceProperty得到属性名称,然后进一步从数据源实体中得到具体的值。根据ControlValuePropertyType得到目标控件绑定属性的类型,然后将之前得到的值转换成该类型。最后,通过ControlValueProperty得到控件的绑定属性,将之前经过转换的值给控件的这个属性就可以了。整个数据绑定实现在如下一个OnBindData方法中。关于属性操作则借助于PropertyAccessor这个组件。
- protected virtual void OnBindData(IEnumerable<BindingMapping> bindingMappings, object entity)
- {
- foreach (var mapping in bindingMappings)
- {
- var bindingMapping = mapping.Clone();
- object value = PropertyAccessor.Get(entity, bindingMapping.DataSourceProperty);
- if (null != this.DataItemBinding)
- {
- var args = new DataBindingEventArgs(bindingMapping, value);
- this.DataItemBinding(this, args);
- value = args.DataValue;
- }
- if (!bindingMapping.AutomaticBind)
- {
- continue;
- }
- if (!string.IsNullOrEmpty(bindingMapping.FormatString))
- {
- value = Format(value, bindingMapping.FormatString);
- }
- Type controlValuePropertyType = PropertyAccessor.GetPropertyType(bindingMapping.Control.GetType(), bindingMapping.ControlValueProperty);
- value = ChangeType(value, controlValuePropertyType);
- if (null == value && typeof(ValueType).IsAssignableFrom(controlValuePropertyType))
- {
- value = Activator.CreateInstance(controlValuePropertyType);
- }
- PropertyAccessor.Set(bindingMapping.Control, bindingMapping.ControlValueProperty, value);
- if (null != this.DataItemBound)
- {
- this.DataItemBound(this, new DataBindingEventArgs(bindingMapping, value));
- }
- }
- }
DataBinder设计的目标是让默认的绑定行为解决80%的问题,并且提供给相应的方式去解决余下的问题。为了让开发者能够有效解决余下的这20%的绑定问题,我们定义两个事件:DataItemBinding和DataBound,它们分别在进行绑定之前和之后被触发。关于事件的触发,已经体现在OnBindData方法的定义中了。