在实际项目中,我们可能会遇到用户自定义XML模板字段,根据这个模板上的字段来显示相应的字段的值到DataGrid。在这种情况下,需要使用XmlReader解析获取这个用户自定义的XML模板上有哪些字段,根据这个字段动态的生成一个实体类,之后再为此动态生成的实体类实例化,并且生成实体类集合绑定到DataGrid即可。(注意:平时我们绑定DataGrid是先在代码里面声明了实体类,实例化多个实体化类,形成实体类集合,绑定到DataGrid。可如果用户自定义XML格式的字段的时候,每次的实体类就不能为静态的了。必须为动态的才行。)

         一、首先我们准备一个XML格式的模板数据。模拟用户自定义的XML模板字段。这里的XML模板字段可以自由添改。


< 
   NewDataSet 
   > 
   
 
   < 
   Table TableName 
   = 
   ' 
   City 
   ' 
    TableShowName 
   = 
   ' 
   城市 
   ' 
     
   > 
   
 
   < 
   Column Name 
   = 
   ' 
   CityName 
   ' 
    ShowName 
   = 
   ' 
   城市名称 
   ' 
     
   /> 
    
 
   < 
   Column Name 
   = 
   ' 
   CityTel 
   ' 
    ShowName 
   = 
   ' 
   城市区号 
   ' 
     
   /> 
    
 
   < 
   Column Name 
   = 
   ' 
   CityCounty 
   ' 
    ShowName 
   = 
   ' 
   城市所属国家 
   ' 
     
   /> 
   
 
   </ 
   Table 
   > 
    
 
   < 
   Table TableName 
   = 
   ' 
   User 
   ' 
    TableShowName 
   = 
   ' 
   用户 
   ' 
     
   > 
   
 
   < 
   Column Name 
   = 
   ' 
   UserName 
   ' 
    ShowName 
   = 
   ' 
   用户名 
   ' 
     
   /> 
    
 
   < 
   Column Name 
   = 
   ' 
   UserPwd 
   ' 
    ShowName 
   = 
   ' 
   用户密码 
   ' 
     
   /> 
    
 
   < 
   Column Name 
   = 
   ' 
   UserTel 
   ' 
    ShowName 
   = 
   ' 
   用户电话 
   ' 
     
   /> 
    
 
   < 
   Column Name 
   = 
   ' 
   UserEmail 
   ' 
    ShowName 
   = 
   ' 
   用户邮箱 
   ' 
     
   /> 
    
 
   </ 
   Table 
   > 
    
 
   </ 
   NewDataSet 
   > 
 
        再声明一个实体类来保存字段的Name和显示名称ShowName,并且把这些字段存放到List<>中去。
 
  
  
  
   /// 
     
   <summary> 
   
 
   /// 
    存放动态表格的字段
 
   /// 
     
   </summary> 
   
 
     
   public 
     
   class 
    GridClass
 {
 
   private 
     
   string 
    _ShowName;
 
   private 
     
   string 
    _Name;
 
   /// 
     
   <summary> 
   
 
   /// 
    显示名称
 
   /// 
     
   </summary> 
   
 
     
   public 
     
   string 
    ShowName
 {
 
   get 
    {  
   return 
    _ShowName; }
 
   set 
    { _ShowName  
   = 
    value; }
 }
 
   /// 
     
   <summary> 
   
 
   /// 
    字段名称
 
   /// 
     
   </summary> 
   
 
     
   public 
     
   string 
    Name
 {
 
   get 
    {  
   return 
    _Name; }
 
   set 
    { _Name  
   = 
    value; }
 }
 } 
 
        二、准备好数据之后,我们开始解析这个XML文档,并且根据此XML文档生成两个动态Tabel实体类。这里我们首先贴出关键代码,根据代码来解读:
 
  
  
 List 
   < 
   GridClass 
   > 
    gridClassList  
   = 
     
   new 
    List 
   < 
   GridClass 
   > 
   (); 
   // 
   声明一个GridClass实体类的集合。 
   
 
     
   using 
    (XmlReader xmlRead  
   = 
    XmlReader.Create( 
   new 
    StringReader(XMLStr)))
 {
 xmlRead.Read();
 
   while 
    (xmlRead.Read())
 {
 
   // 
   获取到一个TABLE,然后转化为一个动态的实体类。 
   
 
    gridClassList.Clear(); 
   // 
   循环读取Tabel元素的时候,清空GridClass实体类集合。 
   
 
    xmlRead.ReadToFollowing( 
   " 
   Table 
   " 
   ); 
   // 
   读取Table的显示名称和Tabel的名称。 
   
 
     
   string 
    TableShowName  
   = 
    xmlRead.GetAttribute( 
   " 
   TableShowName 
   " 
   );
 
   string 
    TableName  
   = 
    xmlRead.GetAttribute( 
   " 
   TableName 
   " 
   );
 
   try 
   
 {
 
   using 
    (XmlReader xReader2  
   = 
    xmlRead.ReadSubtree()) 
   // 
   将此Tabel读取为一个子XmlReader以供下一步使用。 
   
 
    {
 
   while 
    (xReader2.ReadToFollowing( 
   " 
   Column 
   " 
   ))
 {
 
   // 
   循环读取Column元素,然后获取到Tabel的字段的显示名称和字段名称。并且添加到gridClassList 
   
 
     
   string 
    ShowName  
   = 
    xReader2.GetAttribute( 
   " 
   ShowName 
   " 
   );
 
   string 
    Name  
   = 
    xReader2.GetAttribute( 
   " 
   Name 
   " 
   );
 GridClass gclass  
   = 
     
   new 
    GridClass() { ShowName  
   = 
    ShowName, Name  
   = 
    Name };
 gridClassList.Add(gclass);
 }
 
   // 
   声明一个Dictionary<string, string>的List<>集合 
   
 
    List 
   < 
   Dictionary 
   < 
   string 
   ,  
   string 
   >> 
    dicList  
   = 
     
   new 
    List 
   < 
   Dictionary 
   < 
   string 
   ,  
   string 
   >> 
   ();
 
   // 
   声明一个Dictionary<string, string>实体,然后为此实体赋值列名为读取Column元素得到的字段名称 
   
 
    Dictionary 
   < 
   string 
   ,  
   string 
   > 
    dic  
   = 
     
   new 
    Dictionary 
   < 
   string 
   ,  
   string 
   > 
   ();
 
   for 
    ( 
   int 
    j  
   = 
     
   0 
   ; j  
   < 
    gridClassList.Count; j 
   ++ 
   )
 {
 dic[gridClassList[j].Name]  
   = 
     
   " 
   -- 
   " 
   + 
   gridClassList[j].Name 
   + 
   " 
   -- 
   " 
   ;
 }
 dicList.Add(dic);
 
   // 
   动态生成一个DataGrid,并且绑定数据源 
   
 
    DataGrid dgrid  
   = 
     
   new 
    DataGrid();
 dgrid.HorizontalAlignment  
   = 
    HorizontalAlignment.Left;
 dgrid.VerticalAlignment  
   = 
    VerticalAlignment.Top;
 dgrid.Margin  
   = 
     
   new 
    Thickness( 
   20 
   ,  
   5 
   ,  
   0 
   ,  
   0 
   );
 dgrid.Width  
   = 
     
   960 
   ;
 dgrid.Name  
   = 
    TableName;
 dgrid.ItemsSource  
   = 
    GetEnumerable(dicList).ToDataSource();

 
   this 
   .mainPanel.Children.Add(dgrid);
 }
 }
 
   catch 
    (Exception ex)
 { }
 }
 }
 } 
 
        这里解析出关键的字段值,然后动态生成DataGrid,并且绑定了数据库。这些操作是不是很熟悉?几乎和原来绑定数据差不多?关键在以下几句:
 
  
  
  
   // 
   声明一个Dictionary<string, string>的List<>集合 
   
 
    List 
   < 
   Dictionary 
   < 
   string 
   ,  
   string 
   >> 
    dicList  
   = 
     
   new 
    List 
   < 
   Dictionary 
   < 
   string 
   ,  
   string 
   >> 
   ();
 
   // 
   声明一个Dictionary<string, string>实体,然后为此实体赋值列名为读取Column元素得到的字段名称 
   
 
    Dictionary 
   < 
   string 
   ,  
   string 
   > 
    dic  
   = 
     
   new 
    Dictionary 
   < 
   string 
   ,  
   string 
   > 
   ();
 
   for 
    ( 
   int 
    j  
   = 
     
   0 
   ; j  
   < 
    gridClassList.Count; j 
   ++ 
   )
 {
 dic[gridClassList[j].Name]  
   = 
     
   " 
   -- 
   " 
   + 
   gridClassList[j].Name 
   + 
   " 
   -- 
   " 
   ;
 }
 dicList.Add(dic); 
 
        通过这段代码我们得到了一个 List<Dictionary<string, string>>格式的Dictionary<string, string>集合。这里Dictionary的Key值,即为一列字段名,Value为该列的具体值,那么一个Dictionary[0],Dictionary[1],Dictionary[2],Dictionary[3],Dictionary[4],Dictionary[5]即为一行有字段名的数据,整个List<Dictionary>就是一个多行有字段名的数据,这就相当于一个类似于DataTabel的表了。
        三、当然更关键的下一句:dgrid.ItemsSource = GetEnumerable(dicList).ToDataSource();这句话肯定是得到了一个类似于List<object>对象集的东西,才能够绑定到ItemSource属性上来。具体是如何得到这个数据集的呢?在这里暂且先卖一个关子,请看下面源码:
 
  
  
  
   public 
    IEnumerable 
   < 
   IDictionary 
   > 
    GetEnumerable(List 
   < 
   Dictionary 
   < 
   string 
   ,  
   string 
   >> 
    SourceList)
 {
 
   for 
    ( 
   int 
    i  
   = 
     
   0 
   ; i  
   < 
    SourceList.Count; i 
   ++ 
   )
 {
 var dict  
   = 
     
   new 
    Dictionary 
   < 
   string 
   ,  
   string 
   > 
   ();
 dict  
   = 
    SourceList[i];
 
   yield 
     
   return 
    dict;
 }
 } 
 
        这个函数是将List<Dictionary<string, string>>的数据,通过遍历的方式读取出来,使用yield return关键字来获取到IEnumerable<IDictionary>类型的返回值。在这里取到一个数据格式转换的作用,我们看下面这个隐藏起来的类。这个类是国外友人Vladimir Bodurov 编写的,它扩展了IEnumerable接口,让此接口可以将普通的IEnumerable集合通过Emit转化成为实体类集合。想必现在我们再来看dgrid.ItemsSource = GetEnumerable(dicList).ToDataSource();这句代码就很清晰了吧,在这里我们就实现了动态创建类的过程。
 
  
  
DataSourceCreator.cs 
      
      public 
      
    static 
      
    class 
     DataSourceCreator
 {
     private 
      
    static 
      
    readonly 
     Regex PropertNameRegex  
    = 
    
     new 
     Regex( 
    @" 
    ^[A-Za-z]+[A-Za-z1-9_]*$ 
    " 
    , RegexOptions.Singleline);
     public 
      
    static 
     List 
    < 
    object 
    > 
     ToDataSource( 
    this 
     IEnumerable 
    < 
    IDictionary 
    > 
     list)
 {
 IDictionary firstDict      = 
      
    null 
    ;
     bool 
     hasData  
    = 
      
    false 
    ;
     foreach 
     (IDictionary currentDict  
    in 
     list)
 {
 hasData      = 
      
    true 
    ;
 firstDict      = 
     currentDict;
     break 
    ;
 }
     if 
     ( 
    ! 
    hasData)
 {
     return 
      
    new 
     List 
    < 
    object 
    > 
     { };
 }
     if 
     (firstDict  
    == 
      
    null 
    )
 {
     throw 
      
    new 
     ArgumentException( 
    " 
    IDictionary entry cannot be null 
    " 
    );
 }
 Type objectType      = 
      
    null 
    ;
 TypeBuilder tb      = 
     GetTypeBuilder(list.GetHashCode());
 ConstructorBuilder constructor      = 
    
 tb.DefineDefaultConstructor(
 MethodAttributes.Public      | 
    
 MethodAttributes.SpecialName      | 
    
 MethodAttributes.RTSpecialName);
     foreach 
     (DictionaryEntry pair  
    in 
     firstDict)
 {
     if 
     (PropertNameRegex.IsMatch(Convert.ToString(pair.Key),  
    0 
    ))
 {
 CreateProperty(tb,
 Convert.ToString(pair.Key),
 pair.Value      == 
      
    null 
      
    ? 
    
     typeof 
    ( 
    object 
    ) :
 pair.Value.GetType());
 }
     else 
    
 {
     throw 
      
    new 
     ArgumentException(
     @" 
    Each key of IDictionary must be
 alphanumeric and start with character.     " 
    );
 }
 }
 objectType      = 
     tb.CreateType();
     return 
     GenerateArray(objectType, list, firstDict);
 }
     private 
      
    static 
     List 
    < 
    object 
    > 
     GenerateArray(Type objectType, IEnumerable 
    < 
    IDictionary 
    > 
     list, IDictionary firstDict)
 {
 var itemsSource      = 
      
    new 
     List 
    < 
    object 
    > 
    ();
     foreach 
     (var currentDict  
    in 
     list)
 {
     if 
     (currentDict  
    == 
      
    null 
    )
 {
     throw 
      
    new 
     ArgumentException( 
    " 
    IDictionary entry cannot be null 
    " 
    );
 }
     object 
     row  
    = 
     Activator.CreateInstance(objectType);
     foreach 
     (DictionaryEntry pair  
    in 
     firstDict)
 {
     if 
     (currentDict.Contains(pair.Key))
 {
 PropertyInfo property      = 
    
 objectType.GetProperty(Convert.ToString(pair.Key));
 property.SetValue(
 row,
 Convert.ChangeType(
 currentDict[pair.Key],
 property.PropertyType,
     null 
    ),
     null 
    );
 }
 }
 itemsSource.Add(row);
 }
     return 
     itemsSource;
 }
     private 
      
    static 
     TypeBuilder GetTypeBuilder( 
    int 
     code)
 {
 AssemblyName an      = 
      
    new 
     AssemblyName( 
    " 
    TempAssembly 
    " 
      
    + 
     code);
 AssemblyBuilder assemblyBuilder      = 
    
 AppDomain.CurrentDomain.DefineDynamicAssembly(
 an, AssemblyBuilderAccess.Run);
 ModuleBuilder moduleBuilder      = 
     assemblyBuilder.DefineDynamicModule( 
    " 
    MainModule 
    " 
    );
 TypeBuilder tb      = 
     moduleBuilder.DefineType( 
    " 
    TempType 
    " 
      
    + 
     code
 , TypeAttributes.Public      | 
    
 TypeAttributes.Class      | 
    
 TypeAttributes.AutoClass      | 
    
 TypeAttributes.AnsiClass      | 
    
 TypeAttributes.BeforeFieldInit      | 
    
 TypeAttributes.AutoLayout
 ,      typeof 
    ( 
    object 
    ));
     return 
     tb;
 }
     private 
      
    static 
      
    void 
     CreateProperty(TypeBuilder tb,  
    string 
     propertyName, Type propertyType)
 {
 FieldBuilder fieldBuilder      = 
     tb.DefineField( 
    " 
    _ 
    " 
      
    + 
     propertyName,
 propertyType,
 FieldAttributes.Private);

 PropertyBuilder propertyBuilder      = 
    
 tb.DefineProperty(
 propertyName, PropertyAttributes.HasDefault, propertyType,      null 
    );
 MethodBuilder getPropMthdBldr      = 
    
 tb.DefineMethod(     " 
    get_ 
    " 
      
    + 
     propertyName,
 MethodAttributes.Public      | 
    
 MethodAttributes.SpecialName      | 
    
 MethodAttributes.HideBySig,
 propertyType, Type.EmptyTypes);
 ILGenerator getIL      = 
     getPropMthdBldr.GetILGenerator();
 getIL.Emit(OpCodes.Ldarg_0);
 getIL.Emit(OpCodes.Ldfld, fieldBuilder);
 getIL.Emit(OpCodes.Ret);
 MethodBuilder setPropMthdBldr      = 
    
 tb.DefineMethod(     " 
    set_ 
    " 
      
    + 
     propertyName,
 MethodAttributes.Public      | 
    
 MethodAttributes.SpecialName      | 
    
 MethodAttributes.HideBySig,
     null 
    ,  
    new 
     Type[] { propertyType });
 ILGenerator setIL      = 
     setPropMthdBldr.GetILGenerator();
 setIL.Emit(OpCodes.Ldarg_0);
 setIL.Emit(OpCodes.Ldarg_1);
 setIL.Emit(OpCodes.Stfld, fieldBuilder);
 setIL.Emit(OpCodes.Ret);
 propertyBuilder.SetGetMethod(getPropMthdBldr);
 propertyBuilder.SetSetMethod(setPropMthdBldr);
 }
 }  
SLReadDanamicClassToDataGrid.rar