使用Silverlight开发前台页面,需要将后台查询回的集合绑定到前台的DataGrid上。
一般情况下,我们都有定义好的实体类Po,然后可以.xaml文件中固定绑定Po的成员变量到DataGrid的列上,当取到实体集合时直接将 dataGrid.itemsource = List<Po> 即可。
但有些时候,我们并不知道返回的集合中的实体是什么类型,无法预先定义PO,那么该如何动态绑定列和集合呢?
想到一个方法就是在拿到集合后通过反射动态的创建PO类,动态添加public属性,并动态绑定到DataGrid Column。然后再绑定实体集合就可实现需求。
首先在工程中添加动态类型工厂类 DynamicTypeBuilder:
public class DynamicTypeBuilder { TypeBuilder tb; /// <summary> /// 构造函数 /// </summary> /// <param name="typeNm">动态类型的名称</param> public DynamicTypeBuilder(string typeNm) { // 在 Silverlight 中 AssemblyBuilderAccess 没有 RunAndSave AssemblyBuilder ab = AppDomain.CurrentDomain.DefineDynamicAssembly( new AssemblyName("TempAssembly"), AssemblyBuilderAccess.Run); ModuleBuilder mb = ab.DefineDynamicModule("TempModule"); this.tb = mb.DefineType(typeNm, TypeAttributes.Public); } /// <summary> /// 添加一个public的可读写属性,并且会创建对应的名为 propertyNm + "Field" 的私有字段 /// </summary> /// <param name="propertyNm"></param> /// <param name="type"></param> public void AppendPublicProperty(string propertyNm, Type type) { this.AppendPublicProperty(propertyNm, type, true, true); } /// <summary> /// 添加一个public属性,并且会创建对应的名为 propertyNm + "Field" 的私有字段 /// </summary> /// <param name="propertyNm"></param> /// <param name="type"></param> /// <param name="canGet">是否实现getter</param> /// <param name="canSet">是否实现setter</param> public void AppendPublicProperty(string propertyNm, Type type, bool canGet, bool canSet) { FieldBuilder field = this.tb.DefineField(string.Format("{0}Field", propertyNm), type, FieldAttributes.Private); PropertyBuilder property = tb.DefineProperty(propertyNm, PropertyAttributes.HasDefault, type, null); MethodAttributes getSetAttr = MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig; if (canGet) { MethodBuilder getAccessor = tb.DefineMethod(string.Format("get_{0}", propertyNm), getSetAttr, type, Type.EmptyTypes); ILGenerator getIL = getAccessor.GetILGenerator(); // For an instance property, argument default is the instance. Load the // instance, then load the private field and return, leaving the // field value on the stack. getIL.Emit(OpCodes.Ldarg_0); getIL.Emit(OpCodes.Ldfld, field); getIL.Emit(OpCodes.Ret); property.SetGetMethod(getAccessor); } if (canSet) { MethodBuilder setAccessor = tb.DefineMethod(string.Format("set_{0}", propertyNm), getSetAttr, null, new Type[] { type }); setAccessor.DefineParameter(1, ParameterAttributes.None, "value"); ILGenerator setIL = setAccessor.GetILGenerator(); // Load the instance and then the numeric argument, then store the // argument in the field. setIL.Emit(OpCodes.Ldarg_0); setIL.Emit(OpCodes.Ldarg_1); setIL.Emit(OpCodes.Stfld, field); setIL.Emit(OpCodes.Ret); property.SetSetMethod(setAccessor); } } /// <summary> /// 在添加完各个 public 属性之后,调用此方法以完成对动态类型的定义并加载之, /// 此后通过 Activator.CreateInstance() 便可实例化动态类型 /// </summary> /// <returns></returns> public Type CreateDynamicType() { return this.tb.CreateType(); } }
有了它我们就可以动态创建类了。
DynamicTypeBuilder dyClass = new DynamicTypeBuilder("dy");//创建动态类,dy可以随便替换 datagrid.Columns.Clear();//清空datagrid的已有列 ....... //ColInfos为已经取到的列信息集合 foreach (ColInfo colInfo in colInfos) { String name = colInfo.name;//列绑定名 String title = colInfo.title;//列显示名 DataGridTextColumn dtc = new DataGridTextColumn();//动态创建列,完成绑定 dtc.Header = name; dtc.Binding = new Binding("_" + name); this.datagrid.Columns.Add(dtc);//添加列 dyClass.AppendPublicProperty("_" + name, typeof(string));//同时动态添加公共属性到自定义类 } Type dyType = dyClass.CreateDynamicType();//创建自定义类 ........ //构造绑定DataGrid ItemSource的集合 List<Object> datas = new List<Object>(); var po = Activator.CreateInstance(dyType);//创建自定义类实例 PropertyInfo property = dyType.GetProperty("_" + colInfos[i].name); property.SetValue(po, [value], null); datas.add(po); dataGrid.itemsource = datas }