Powered by MSDN

US - English
NEW! Silverlight 5 is available Learn More

Creating dynamic List for binding to Datagrid RSS

10 replies

Last post Apr 17, 2011 06:50 AM by Zeins

(0)
  • grininmonkey

    grininmonkey

    Member

    12 Points

    9 Posts

    Creating dynamic List for binding to Datagrid

    Dec 03, 2008 02:02 AM | LINK

    I am at wits end and I have seen similar posts asking the same question with no clear answers… I understand that in typical situations you know what fields your looking for and expect from an xml feed or data query via a web service.

    However, in my situation, I am working with data in which a datatset contains different columns based on access (not everyone can see the same fields) and user administration… meaning admin users can add new fields or remove existing ones. With that being the case… I can not hard code any list<> objects and define fieldnames within my ASMX web service or SL project as most examples and forums suggest.

    So I have a web service that is grabbing data and returning the results as an XML string. I am now trying to figure out how to take that result and bind it to a DataGrid while not knowing what the Fields are until I parse the XML string.

    My native language is PHP, however I’m learning C# as best as I can… and this issue has really stumped me. I would “Greatly” appreciate any help on this issue… rather learning how to create dynamic list objects  or some other method in which I can parse an XML feed and then create “Something” that the DataGrid can bind with ?  

  • lingbing

    lingbing

    Contributor

    2249 Points

    406 Posts

    Re: Creating dynamic List for binding to Datagrid

    Dec 03, 2008 03:54 AM | LINK

    Hi, the best dynamic list<> is the ObservableCollection<> in namespace System.Collections.ObjectModel. As you know the DataGrid accepts an IEnumerable<> as its ItemsSource, however, if you just use List<>, when you add/remove/reset/clear the collection, the UI doesn't change as your changes. But ObservableCollection<> supports this. And make sure your binding is for properties but not fields, so that generate your data class using public properties.

    In your case, you said that you don't know what the Fields are until your parse the XML string, what do you mean by that? if you don't know the data structure of the data class? If so, it is a hard work, .net framework supports anonymous class, however, I don't if the <T> class can support it. Thus I think at least you should have a data class type for the IEnumerable<T>. If you can provide this class, everything goes well, you can use LinqToXML to generate your class's instances and put them in the collection -- ObservableCollection<>, and set it as the ItemsSource of the DataGrid.

    And for user administration, I think this is simple in silverlight, maybe you can pass the userid to silverlight app and then determin the user's authority just in silverlight app but not your webservice.

    Regards!

    Ling Bing
    Bei Jing University of Aeronautics and Astronautics
    Bei Jing, China
  • codism

    codism

    Member

    372 Points

    121 Posts

    Re: Creating dynamic List for binding to Datagrid

    Dec 03, 2008 05:12 AM | LINK

    Let me try to throw some idea here. The following are pseudo code: 

    class HeaderDescription {
    string HeaderText;
    string PropertyName;
    }

    void DisplayData(List<HeaderDescription> headers, Collection<Dictionary<string, object>> data) {
    datagrid.Columns.Clear();
    headers.ForEach(description => {
    datagrid.Columns.Add(new DataGridTextColumn() {
    Header = description.HeaderText,
    Binding = new Binding() {
    Converter = myValuePicker,
    ConverterParameter = description.PropertyName
    }
    });
    });
    datagrid.ItemsSource = data;
    }

     The key idea is to use a value converter (myValuePicker in the above code) that can pick up a value based on different converter parameters. Good luck.
     

  • vinCracker

    vinCracker

    Contributor

    4059 Points

    673 Posts

    Re: Creating dynamic List for binding to Datagrid

    Dec 03, 2008 05:55 AM | LINK

    Hi,

    If you want something like this then let me know.

    http://silverlight.services.live.com/invoke/81567/Test2/iframe.html

    -Vinit 

  • grininmonkey

    grininmonkey

    Member

    12 Points

    9 Posts

    Re: Creating dynamic List for binding to Datagrid

    Dec 03, 2008 02:13 PM | LINK

    codism,

    pseudo might be to "high level" for me on this topic, I'm not sure how to use your idea as I have only recently picked up C# while being fluent in PHP web development.

     What might help is.. I can work on this issue at the web service level and work with a dataset object before being converted to an xml string. My goal is for the web service to return data as a type, the DataGrid control can bind to and automatically populate headers.

    I didn't want to explain in to much detail, but in reply to lingbing’s posting, what I have is a web service that is getting data from analysis service (OLAP) and the queries within my web service require user arguments which I pass from my SL app to the service method. The back end of the OLAP source is very complicated but in short the resulted query that is called from my web service works like this…

    let’s say the web service passes the following query to the OLAP source < I’ll use sql and not MDX to keep it simple… “ Select * from DatasetA,”  .. If  the user is John Doe, because of their access level, the result might be a dataset with FieldB,FieldC,FieldF and FieldG ….. but for Joe Smith the result might be a dataset with FieldA,FieldB,FieldE and FieldI. To add to the complication, With in the Cube on the backend, DatasetA may have a new field added or removed at any time on the back end.. and Fields a user can see change as well (Promotion, department transfer, new employee, etc…), So what Joe Smith can see today might be different another day if there department access level changes.

    So there is absolutely no way to predefine fields in a collection as a class. So I am stuck with a successful passing of the user from SL to a data interface (My web service) which successfully retrieves a dataset as a dataset type… in which I am currently converting into an XML string to return back to the SL app ( in hopes of being able to do something with it as SL does not support dataset object)… in which I am currently stuck trying to figure out how to present the data back to the user in a DataGrid. L

     

  • grininmonkey

    grininmonkey

    Member

    12 Points

    9 Posts

    Re: Creating dynamic List for binding to Datagrid

    Dec 03, 2008 02:28 PM | LINK

    Is there a way that I could use the TypeBuilder Class to create a class during runtime that would logically perform something like this:

     

    Create new public class as MyDataClass

    While looping thru each field name -> add new member to MyDataClass as public string with the name of this.fieldname

    Then I could  construct a list<MyDataClass> and then populate it by traversing the rows within the dataset .... and when finished... destroy the MyDataClass Instance.

     Is this logically possible in C# ?

  • vladb

    vladb

    Member

    61 Points

    12 Posts

    Re: Creating dynamic List for binding to Datagrid

    Dec 03, 2008 09:51 PM | LINK

    blog.bodurov.com
  • grininmonkey

    grininmonkey

    Member

    12 Points

    9 Posts

    Re: Creating dynamic List for binding to Datagrid

    Dec 03, 2008 10:17 PM | LINK

    Ok ... This looks like a good start.. except.. How do I change the return type in my [WebMethod] function to IEnumerable so that I have a Dictonary feed instead of my current xml string...  before I got started working on chaning my output i got the following when I messed with the function declaration: "Using the generic type 'System.Collections.Generic.IDictionary<TKey,TValue>' requires '2' type arguments" 

     [WebPart]

    Public IEnumerable<IDictionary> myGetDataFunction( etc..... )

    What should I do on my web service ?

  • grininmonkey

    grininmonkey

    Member

    12 Points

    9 Posts

    Re: Re: Creating dynamic List&lt;&gt; for binding to Datagrid

    Dec 04, 2008 01:16 AM | LINK

    Well... like I said.... I'm learning as I go. I just finished looking up and learning more about what types/objects and interfaces are supported with in webservice. Unless I read wrong.. interfaces are not supported due the XML serializer ...

     So I'll keep it as an xml string -> within the SL app parse it out to a Dictonary and then use the custom class that creates an IEnumerable of Dictionary...

     

  • Raag80

    Raag80

    Member

    396 Points

    91 Posts

    Re: Re: Creating dynamic List&lt;&gt; for binding to Datagrid

    Mar 13, 2009 12:16 PM | LINK

    Hi,

    I was having the same issue. as you have....then i found a article and which i have pasted here....i have implemeted with live data and its works

    If you want to define the number and the type of Silverlight DataGrid columns at runtime you can use the following approach. The technique can actually be used not only for Silverlight but also anywhere where you have to transform IDictionary (for example Dictionary or Hashtable, SortedDictionary etc) into anonymous typed object with each dictionary key turned into an object property.

    You may say that this way I can violate the object constraints. For example each IDictionary inside IEnumerable can have different set of keys. And to a certain degree I can agree with that and if you rely on the data to come from a third party I wouldn’t recommend this approach or at least I would advice you to validate each IEnumerable entry before binding it to the control. But if you have a complete control on the transformation of the data – this is definitely a good approach. You can think of this as a way to use C# as a dynamic language. In the current solution I check the keys in the first entry of IEnumerable and if the second has more keys they will be ignored or if it has less the default values will be passed (null, Guid.Empty etc.)

    So here is how your code can look. This is the code behind of an Xaml page:

    public partial class Page : UserControl
        {
            public Page()
            {
                InitializeComponent();

                this.theGrid.Columns.Add(
                            new DataGridTextColumn
                            {
                                Header = "ID",
                                Binding = new Binding("ID")
                            });
                this.theGrid.Columns.Add(
                            new DataGridTextColumn
                            {
                                Header = "Name",
                                Binding = new Binding("Name")
                            });
                this.theGrid.Columns.Add(
                            new DataGridTextColumn
                            {
                                Header = "Index",
                                Binding = new Binding("Index")
                            });
                this.theGrid.Columns.Add(
                            new DataGridTextColumn
                            {
                                Header = "Is Even",
                                Binding = new Binding("IsEven")
                            });
                this.theGrid.ItemsSource = GenerateData().ToDataSource();
            }

            public IEnumerable GenerateData()
            {
                for(var i = 0; i < 15; i++)
                {
                    var dict = new Dictionary<string, object>();
                    dict["ID"] = Guid.NewGuid();
                    dict["Name"] = "Name_" + i;
                    dict["Index"] = i;
                    dict["IsEven"] = ( i%2 == 0);
                    yield return dict;
                }
            }
        }Or in Visual Basic:

        ...
    Public Function GenerateData() As IEnumerable(Of IDictionary)
        Dim list As New List(Of IDictionary)()
        For i As Integer = 0 To 9
            Dim dict As IDictionary = New Dictionary(Of String, Object)()
            dict("ID") = Guid.NewGuid()
            dict("Name") = "Name_" & i.ToString()
            dict("Index") = i
            dict("IsEven") = (i Mod 2 = 0)
            list.Add(dict)
        Next
        Return list
    End Function

        The Xaml can be as simple as that:

        "LayoutRoot" Background="White">
           
    "theGrid"
                    Grid.Column="0"
                    Grid.Row="0"
                    AutoGenerateColumns="False">
           
        As you can see the IEnumerable of IDictionary has no ToDataSource() method so we have to define this extension method and there is where the magic happens.


    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Reflection;
    using System.Reflection.Emit;
    using System.Text;
    using System.Text.RegularExpressions;
    using System.Windows.Browser;

    namespace com.bodurov
    {

        public static class DataSourceCreator
        {
            private static readonly Regex PropertNameRegex =
                    new Regex(@"^[A-Za-z]+[A-Za-z1-9_]*$", RegexOptions.Singleline);

            private static readonly Dictionary<string, Type> _typeBySigniture =
                    new Dictionary<string,Type>();


            public static IEnumerable ToDataSource(this IEnumerable 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 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);
            }
        }




    }

     
    Regards,
    Raag

    If it is Solved your problem then mark as answer bcoz other with same question can benefit.....