天天看點

9.動态生成實體類,根據XML模闆使用Emit生成動态類綁定到DataGrid

   在實際項目中,我們可能會遇到使用者自定義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='使用者郵箱' />  

            </NewDataSet> 

        再聲明一個實體類來儲存字段的Name和顯示名稱ShowName,并且把這些字段存放到List<>中去。

/// <summary> 

/// 存放動态表格的字段 

/// </summary> 

public class GridClass 

private string _ShowName; 

private string _Name; 

/// 顯示名稱 

public string ShowName 

get { return _ShowName; } 

set { _ShowName = value; } 

/// 字段名稱 

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; 

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) 

object row = Activator.CreateInstance(objectType); 

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.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, 

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); 

文轉自程興亮 51CTO部落格,原文連結:http://blog.51cto.com/chengxingliang/821464