zoukankan      html  css  js  c++  java
  • Silverlight实用窍门系列: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='用户邮箱' />
    </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);
    }
    }

            本文实例源码采用VS2010+Silverlight 4.0编写,点击 SLReadDanamicClassToDataGrid.rar 下载此源码。

           

  • 相关阅读:
    Python爬虫之记录一次下载验证码的尝试
    Python之学会测试,让开发更加高效(一)
    NLP(二十八)多标签文本分类
    NLP(二十七)开放领域的三元组抽取的一次尝试
    NLP(二十六)限定领域的三元组抽取的一次尝试
    Numpy之数据保存与读取
    TortoiseGit的首次使用
    NLP(二十五)实现ALBERT+Bi-LSTM+CRF模型
    NLP(二十四)利用ALBERT实现命名实体识别
    NLP(二十三)序列标注算法评估模块seqeval的使用
  • 原文地址:https://www.cnblogs.com/chengxingliang/p/1960146.html
Copyright © 2011-2022 走看看