在实际项目中,我们可能会遇到用户自定义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