给 DataGrid 绑定数据的通常做法是:
1、服务器端将数据集合 序列化 成 XML,或者其它格式。
2、在Silverlight 中获取到数据,并反序列化成实体集合。
3、绑定到 DataGrid ,搞定。
象RIA Service 也是类似的做法。只是 VS 将实体自动帮创建好了。
并且,DataGrid 不支持匿名类集合,使用匿名集合,需要自己写 Converter 才能绑定。否则只能绑定出 Title 和 数量。 :D
然后,我的想法是:
获取集合数据,在内存中动态构造出实体,赋值,然后绑定。
有一个类:
DataSourceCreator 可以干这件事情。上面的那个网址可以下载到这个类。
源代码如下:
public static class DataSourceCreator
{
private static readonly Regex PropertNameRegex =
new Regex(@"^[A-Za-z]+[A-Za-z0-9_]*$", RegexOptions.Singleline);
private static readonly Dictionary<string, Type> _typeBySigniture =
new Dictionary<string, Type>();
public static IEnumerable 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 object[] { };
}
if (firstDict == null)
{
throw new ArgumentException("IDictionary entry cannot be null");
}
string typeSigniture = GetTypeSigniture(firstDict);
Type objectType = GetTypeByTypeSigniture(typeSigniture);
if (objectType == null)
{
TypeBuilder tb = GetTypeBuilder(typeSigniture);
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),
GetValueType(pair.Value));
}
else
{
throw new ArgumentException(
@"Each key of IDictionary must be
alphanumeric and start with character.");
}
}
objectType = tb.CreateType();
_typeBySigniture.Add(typeSigniture, objectType);
}
return GenerateEnumerable(objectType, list, firstDict);
}
private static Type GetTypeByTypeSigniture(string typeSigniture)
{
Type type;
return _typeBySigniture.TryGetValue(typeSigniture, out type) ? type : null;
}
private static Type GetValueType(object value)
{
return value == null ? typeof(object) : value.GetType();
}
private static string GetTypeSigniture(IDictionary firstDict)
{
StringBuilder sb = new StringBuilder();
foreach (DictionaryEntry pair in firstDict)
{
sb.AppendFormat("_{0}_{1}", pair.Key, GetValueType(pair.Value));
}
return sb.ToString().GetHashCode().ToString().Replace("-", "Minus");
}
private static IEnumerable GenerateEnumerable(
Type objectType, IEnumerable<IDictionary> list, IDictionary firstDict)
{
var listType = typeof(List<>).MakeGenericType(new[] { objectType });
var listOfCustom = Activator.CreateInstance(listType);
foreach (var currentDict in list)
{
if (currentDict == null)
{
throw new ArgumentException("IDictionary entry cannot be null");
}
var 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);
}
}
listType.GetMethod("Add").Invoke(listOfCustom, new[] { row });
}
return listOfCustom as IEnumerable;
}
private static TypeBuilder GetTypeBuilder(string typeSigniture)
{
AssemblyName an = new AssemblyName("TempAssembly" + typeSigniture);
AssemblyBuilder assemblyBuilder =
AppDomain.CurrentDomain.DefineDynamicAssembly(
an, AssemblyBuilderAccess.Run);
ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule("MainModule");
TypeBuilder tb = moduleBuilder.DefineType("TempType" + typeSigniture
, 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);
}
}
直接拷贝这code,需要加入以下引用:
using System.Text.RegularExpressions;
using System.Collections;
using System.Reflection;
using System.Text;
using System.Reflection.Emit;
这个类的静态方法 ToDataSource 扩展了 IEnumerable 接口,将普通的 IDictionary 集合 转化成了实体集合。
好。现在来看 json。
将 json 转化为 IDictionary 应该不是一件难事,通过试验,代码如下:
public partial class MainPage : UserControl
{
private ProductContext _pro = new ProductContext();
public MainPage()
{
InitializeComponent();
string jsonData = @"
[
{
""TitleID"":1,
""Title"":""CEO""
},
{
""TitleID"":2,
""Title"":""CTO""
},
{
""TitleID"":3,
""Title"":""CXO""
},
{
""TitleID"":4,
""Title"":""数据库管理员1221""
}
]
";
System.Json.JsonArray data = System.Json.JsonArray.Parse(jsonData) as System.Json.JsonArray;
grid.ItemsSource = GenerateData(data).ToDataSource();
}
public IEnumerable<System.Collections.IDictionary> GenerateData(System.Json.JsonArray data)
{
foreach (System.Json.JsonObject jo in data)
{
var dict = new Dictionary<string, object>();
foreach (string key in jo.Keys)
{
dict.Add(key, jo[key]);
}
yield return dict;
}
}
}
现在,我们可以使用同一个 Controller 驱动 silverlight 或者 ext 啦。
btw: 使用 System.Web.Script.Serialization.JavaScriptSerializer 可以序列化出 json, 这个类在 System.Web.Extensions 包里。