在Silverlight中使用List构造交叉表作为Silverlight的DataGrid的数据源

问题起源:一个同事取出一个表的数据,然后根据选择的监测因子和区域,构建一个交叉表,然后要绑定到Silverlight的DataGrid上,他没头绪,我帮忙解决这个问题。

问题解决:

1.最好的办法是在获取数据时就把交叉表构建好,可以构建2种交叉表,一种是定列的,一种是动态列的,都可以通过SQL构建好,这里就不熬诉了。

2.同事说数据对象已经取好了,不想改代码,也是因为用的Ibatis.net构建的实体,要是构建动态的交叉表,实体层代码确实要改挺多。最后决定在客户端写C#代码构建一个交叉表数据源绑定到DataGrid上。

DataGrid的ItemsSource需要一个实现了IEnumerable的对象

需求:

因子可多选:a,b,c…

区域可多选: 广州, 佛山, 深圳

取出的数据:

区域    监测因子  值

广州      a         a11

广州      b         b22

广州      d         d33

佛山      a         a44

佛山      c         c55

选择了"a,b,c”和 "广州, 佛山, 深圳"  效果图:

image

选择了"a,b”和 "广州, 佛山, 深圳"  效果图:

image

测试代码:

 

//选择的多个因子
string[] monitoringFactor = new string[] { "a", "b", "c" };
//选择的多个区域
string[] area = new string[] { "广州", "佛山", "深圳" };
//没取实际表,构建一个数组模拟数据
string[][] factorData = new string[5][];
factorData[0] = new string[] { "广州", "a", "a11" };
factorData[1] = new string[] { "广州", "b", "b22" };
factorData[2] = new string[] { "广州", "d", "d33" };
factorData[3] = new string[] { "佛山", "a", "a44" };
factorData[4] = new string[] { "佛山", "c", "c55" };

//xGrid是一个DataGrid控件名

xGrid.ItemsSource = BindData(monitoringFactor, area, factorData).ToDataSource();

 

public IEnumerable<IDictionary> BindData(string[] factor, string[] area, string[][] factordata)
{
    //构造交叉表数据,返回IEnumerable<IDictionary>
    for (int j = 0; j < area.Length; j++)
    {
        int m = 0;
        var returnDictionary = new Dictionary<string, object>();
        for (int i = 0; i < factor.Length; i++)
        {
            int x = 0;
            for (int k = 0; k < factordata.Length; k++)
            {
                if (factordata[k][0].ToString() == area[j].ToString() && factordata[k][1] == factor[i].ToString())
                {
                    x = x + 1;
                    if (m == 0)
                    {
                        returnDictionary["区域"] = area[j].ToString();
                    }
                    returnDictionary[factor[i].ToString()] = factordata[k][2].ToString();
                    m = m + 1;
                }
            }
            if (x == 0)
            {
                returnDictionary[factor[i].ToString()] = "0";
            }
            if (m == 0 && x == 0)
            {
                returnDictionary["区域"] = area[j].ToString();
                returnDictionary[factor[i].ToString()] = "0";
            }
        }
        yield return returnDictionary;
    }
}

 

public static class DataSourceCreator
{
    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)
            {
                    CreateProperty(tb,
                                    Convert.ToString(pair.Key),
                                    GetValueType(pair.Value));
            }
            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);
    }
}

 

可以改变监测因子和区域数组看界面效果,没有考虑大数据量的性能,纯粹是一个测试代码。

题外说明:

为了减小Silverlight运行时的文件大小,大多数非泛型集合类型将不会再被支持。这些非泛型集合类型主要是那些曾经一度被看作.NET编程本质的类型,如ArrayList、Hashtable和Comparer等。
根据微软基础类库团队的成员Inbar Gazit所言,非泛型集合将不会随Silverlight 1.1一起发布 。这就意味着虽然你仍可以在主要的.NET发行版本中使用这些类型,但它们却不能被用来访问Silverlight程序。受影响的类包括:
ArrayList
BitArray
CaseInsensitiveComparer
CaseInsensitiveHashCodeProvider
CollectionBase
Comparer
CompatibleComparer
DictionaryBase
EmptyReadOnlyDictionaryInternal
Hashtable
IHashCodeProvider
KeyValuePairs
ListDictionaryInternal
Queue
ReadOnlyCollectionBase
SortedList
Stack
为了不至于太过迷惑,在主要的.NET发行版本里,微软现在还不打算清除这些类,或者将它们标识为弃用(obsolete)。
为了支持像数据绑定这样不需要知道类型的场景,在Silverlight里下面的一些非泛型接口将仍会得到保留:

IEnumerator
IEnumerable
ICollection
IComparer
IDictionary
IDictionaryEnumerator
DictionaryEntry
IEqualityComparer
IList
有一些泛型集合也将不再被Silverlight支持,Inbar解释说:
有三个泛型也被从Silverlight中清除掉了,包括Queue、Stack和LinkedList等。这儿并不是因为它们是非泛型,而是因为我们认为它们不是Silverlight所应提供的本质类型的一部分。记住,Silverlight是一个非常小的下载,它应该只包括对开发有用的最小API集合。用List实现Queue和Stack非常容易,而LinkedList也只是一个带有不同性能特性List的不同实现而已,因此这些并不是我们核心集合群组的本质部分。
Inbar还发表了一篇关于为什么泛型集合应该被用在传统.NET编程中的文章,并提出了一些针对现有代码库如何进行改变的建议。 查看英文原文

Silverlight to Not Support ArrayList

你可能感兴趣的:(silverlight)