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 DataGridTextBoxColumn { 
                        Header = "ID", DisplayMemberBinding = new Binding("ID") });
            this.theGrid.Columns.Add(
                        new DataGridTextBoxColumn { 
                        Header = "Name", DisplayMemberBinding = new Binding("Name") });
            this.theGrid.Columns.Add(
                        new DataGridTextBoxColumn { 
                        Header = "Index", DisplayMemberBinding = new Binding("Index") });
            this.theGrid.Columns.Add(
                        new DataGridCheckBoxColumn { 
                        Header = "Is Even", DisplayMemberBinding = new Binding("IsEven") });
            this.theGrid.ItemsSource = GenerateData().ToDataSource();
        }

        public IEnumerable<IDictionary> 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:

    <Grid x:Name="LayoutRoot" Background="White">
        <data:DataGrid x:Name="theGrid" 
                Grid.Column="0" 
                Grid.Row="0" 
                AutoGenerateColumns="False">
        </data:DataGrid>
    </Grid>

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.

C# source: DataSourceCreator.cs; Visual Basic source: DataSourceCreator.vb

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

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

        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");
            }

            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 GenerateEnumerable(objectType, list, firstDict);
        }

        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(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);
        }
    }



}

Feedback

Posted on 5/13/2008 1:45:22 PM

So how about the performance implications of that approach?

Posted on 5/13/2008 5:08:50 PM

The type is generated once not per each record, so I don't think this should be of a great concern.

Posted on 5/19/2008 4:47:13 PM

Wow, very well done! I struggled with this problem and ended up with a much less elegant solution. (Basically create an IValueConverter that returns an index into a dictionary -- using the parameter as the index.)

I have to say I'm impressed with what you've done here and the fact that Silverlight is powerful enough to allow this approach.

Excellent job!

- Ethan

Posted on 5/19/2008 6:06:00 PM

Thank you Ethan, it is truly amazing how powerful .NET is and now we have a lot of this power on the client side with Silverlight!

.NET is strongly typed but as a matter of fact with the extension methods, anonymous types and all the other new features it can be as powerful as the dynamic languages.

Posted on 5/20/2008 11:32:21 PM

Impressive, both code wise, and performance wise.

I first tried 100 records, fearing that the reflection would be slow... it came right up. Then I tried 500, same result.

Finally, I tried 1000, which is more than I would use(we always prefer to page results), and it still came up very fast.

GREAT WORK!!!

Posted on 5/21/2008 12:47:54 AM

Thanks samcov,

A possible slow down due to the reflection will be in the number of columns (or properties generated), while the rows are handled after the type generation has been completed. But I don’t see why you may need a huge number of columns.

Posted on 5/21/2008 3:06:13 AM

thanks for that info dude, however i'm having the following problem:

Error 1 Using the generic type 'System.Collections.Generic.IDictionary<TKey,TValue>' requires '2' type arguments

at: public IEnumerable<IDictionary> GenerateData() -> Page.xaml.cs

How do i fix that?

Posted on 5/21/2008 3:26:07 AM

Ok i was missing System.Collections
working now :)

Posted on 6/1/2008 10:39:24 PM

This took me a week to find and is exactly what I was looking for. Thank you very much. I have converted your code to VB for anyone who is interested.

Imports Microsoft.VisualBasic  
Imports System.Reflection.Emit  
Imports System.Reflection  
Imports System  
Imports System.Collections  
Imports System.Collections.Generic  
Imports System.Text.RegularExpressions  
  
Public Class DataSourceCreator  
    Private ReadOnly Property PropertNameRegex() As Regex  
        Get  
            Return New Regex("^[A-Za-z]+[A-Za-z1-9_]*$", RegexOptions.Singleline)  
        End Get  
    End Property  
  
  
    Public Function ToDataSource(ByVal list As IEnumerable(Of IDictionary)) As Object()  
        Dim firstDict As IDictionary = Nothing  
        Dim hasData As Boolean = False  
        Dim currentDict As IDictionary  
        For Each currentDict In list  
            hasData = True  
            firstDict = currentDict  
            Exit For  
        Next  
  
        If hasData = False Then  
            Return New Object() {}  
        End If  
        If firstDict Is Nothing Then  
            Throw New ArgumentException("IDictionary entry cannot be null")  
        End If  
  
        Dim objectType As Type = Nothing  
        Dim tb As TypeBuilder = GetTypeBuilder(list.GetHashCode())  
  
        Dim constructor As ConstructorBuilder = tb.DefineDefaultConstructor(MethodAttributes.Public Or MethodAttributes.SpecialName Or MethodAttributes.RTSpecialName)  
        Dim pair As DictionaryEntry  
        For Each pair In firstDict  
            If (PropertNameRegex.IsMatch(Convert.ToString(pair.Key), 0)) Then  
                CreateProperty(tb, Convert.ToString(pair.Key), pair.Value.GetType)  
            Else  
                Throw New ArgumentException("Each key of IDictionary must be alphanumeric and start with character. :: key=" & pair.Key & " value=" & pair.Value)  
            End If  
        Next  
        objectType = tb.CreateType()  
        Return GenerateArray(objectType, list, firstDict)  
    End Function  
  
    Private Function GenerateArray(ByVal objectType As Type, ByVal list As IEnumerable(Of IDictionary), ByVal firstDict As IDictionary) As Object()  
        Dim itemsSource = New List(Of Object)()  
        Dim currentDict As IDictionary  
        For Each currentDict In list  
  
            If currentDict Is Nothing Then  
                Throw New ArgumentException("IDictionary entry cannot be null")  
            End If  
            Dim row As Object = Activator.CreateInstance(objectType)  
            Dim pair As DictionaryEntry  
            For Each pair In firstDict  
                If (currentDict.Contains(pair.Key)) Then  
                    Dim [property] As PropertyInfo = objectType.GetProperty(Convert.ToString(pair.Key))  
                    [property].SetValue(row, Convert.ChangeType(currentDict(pair.Key), [property].PropertyType, Nothing), Nothing)  
                End If  
            Next  
            itemsSource.Add(row)  
        Next  
        Return itemsSource.ToArray()  
    End Function  
  
    Private Function GetTypeBuilder(ByVal code As Integer) As TypeBuilder  
        Dim an As AssemblyName = New AssemblyName("TempAssembly" & code)  
        Dim assemblyBuilder As AssemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(an, AssemblyBuilderAccess.Run)  
        Dim moduleBuilder As ModuleBuilder = assemblyBuilder.DefineDynamicModule("MainModule")  
  
        Dim tb As TypeBuilder = moduleBuilder.DefineType("TempType" & code, TypeAttributes.Public Or TypeAttributes.Class Or TypeAttributes.AutoClass Or _  
                                                          TypeAttributes.AnsiClass Or TypeAttributes.BeforeFieldInit Or TypeAttributes.AutoLayout, GetType(Object))  
        Return tb  
    End Function  
  
    Private Sub CreateProperty(ByVal tb As TypeBuilder, ByVal propertyName As String, ByVal propertyType As Type)  
        Dim fieldBuilder As FieldBuilder = tb.DefineField("_" & propertyName, propertyType, FieldAttributes.Private)  
        Dim propertyBuilder As PropertyBuilder = tb.DefineProperty(propertyName, PropertyAttributes.HasDefault, propertyType, Nothing)  
        Dim getPropMthdBldr As MethodBuilder = tb.DefineMethod("get_" & propertyName, MethodAttributes.Public Or MethodAttributes.SpecialName _  
                Or MethodAttributes.HideBySig, propertyType, Type.EmptyTypes)  
  
        Dim getIL As ILGenerator = getPropMthdBldr.GetILGenerator()  
  
        getIL.Emit(OpCodes.Ldarg_0)  
        getIL.Emit(OpCodes.Ldfld, fieldBuilder)  
        getIL.Emit(OpCodes.Ret)  
  
  
        Dim setPropMthdBldr As MethodBuilder = tb.DefineMethod("set_" & propertyName, MethodAttributes.Public Or MethodAttributes.SpecialName Or _  
                                                                MethodAttributes.HideBySig, Nothing, New System.Type() {propertyType})  
  
        Dim setIL As ILGenerator = 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)  
    End Sub  
End Class 
Posted on 6/1/2008 11:11:16 PM

Any interest in showing how one could take the resulting object and make it serializable for LINQ processing or so you can generate the objects on the fly with a web service and pass them to silverlight that way? Even a nudge in the right direction would be a great help.

Thanks.

Posted on 6/2/2008 12:08:58 AM

Thanks for the VB code. Anything that implements IEnumerable<T> is querable by LINQ if you want to have higher capabilities for quering you will need to make it implement IQueryable http://msdn.microsoft.com/en-us/library/system.linq.iqueryable.aspx

If you want the object to be returned by WCF service you'd have to twick reflection code to add System.Runtime.Serialization.DataContractAttribute to the object and System.Runtime.Serialization.DataMemberAttribute for each property.

Posted on 6/20/2008 2:24:11 AM

Very nice, but when I try this on a grid in slb2 and click a column to sort it you get.

SortDescription's property name "Name" is invalid.
at System.Windows.Controls.ListCollectionView.GetPropertyType(Type parentType, String propertyPath)
at System.Windows.Controls.SortFieldComparer`1.CreatePropertyInfo(SortDescriptionCollection sortFields)
at System.Windows.Controls.SortFieldComparer`1..ctor(SortDescriptionCollection sortFields)

Posted on 6/20/2008 11:20:38 AM

These are the new features of the latest silverlight release. Sorting cannot look at real type behind object[]. So please take the current code on the blog where I replaces object[] with IEnumerable.

Posted on 7/4/2008 7:46:12 AM

Vladimir, do you maybe know how to convert the 'yield return' thing to VB code? I know VB .NET doesn't have the keyword yield, but i really need the code in VB (and i don't understand yield at all)

Posted on 7/4/2008 8:42:24 AM

Try this:

Public Function GenerateData() As IEnumerable(Of IDictionary)   
    Dim list As New List(Of Dictionary(Of String, Object))()   
    For i As Integer = 0 To 14   
        Dim dict As IDictionary = New Dictionary(Of String, Object)()   
        dict.Add("ID", Guid.NewGuid())   
        dict.Add("Name", "Name_" + i)   
        dict.Add("Index", i)   
        dict.Add("IsEven", i Mod 2 = 0)   
        list.Add(dict)   
    Next   
    Return list   
End Function   
Posted on 7/14/2008 11:22:37 AM
Posted on 7/17/2008 5:57:46 AM

@jercra

Did you ever manage to serialize an object and pass it over to silverlight, I'm halfway through figuring this out at the moment, but stuck as to why wcf doesn't seem to want to serialize my object, I cannot see any obvious reason as to why it is not (I'm sure I'm doing something silly somewhere).

Btw great article Vladimir! Thank you!

Posted on 7/17/2008 1:16:16 PM

Thanks Petel,

Silverlight and usual .NET are different frameworks even though they are designed to look similar. But if you create proxy you can easily create a silverlight version of the server object. For the sake of experiment it is quite interesting to try serializing and desterilising an object between the two frameworks, but I haven’t tried that. I am not sure this is possible.

Posted on 7/22/2008 2:36:43 PM

It's really G R E A T !

I was struggling with this kind of problem for some days now, and you brings the nice and clean solution I was exactly looking for. Thank you !

Posted on 8/6/2008 5:23:49 AM

Vladimir,

Neat solution that works well!

What I don't understand is how you can access the Data Grid's ItemsSource property. When a grid is editable then, as I understand it, the ItemsSource is updated when a cell is edited. I want to get access to the edited ItemsSource to iterate over the rows and cells. I thought I would be able to do this by casting the ItemsSource back to the original IEnumerable<IDictionary> type that I used to populate the grid. However, this cast doesn't work and my knowledge of reflection isn't good enough. Any ideas or suggestions?

Thanks

John

Posted on 8/6/2008 9:57:50 AM

It is not IEnumerable<IDictionary> after the ToDataSource method. you can get the changed value as shown here

private void TestBtn_Click(object sender, RoutedEventArgs e) 
{ 
    IEnumerable<object> collection = this.theGrid.ItemsSource.Cast<object>(); 
    List<object> list = collection.ToList(); 
    object name = list[0].GetType().GetProperty("Name").GetValue(list[0], null); 
    HtmlPage.Window.Alert("name: " + name); 
} 
Posted on 8/7/2008 7:17:02 AM

That works!

Thanks for your help and prompt reply!

Posted on 8/13/2008 11:03:07 AM

Your code creates Property type using “the first row” of data.
In some cases one ore more field can be null (System.DBNull) in the first row but not null for others rows. In that case property.SetValue generates an exception of conversion a type like System.Int32 to System.DBNull.
In generally, is that possible to have null in the Silverlight DataGrid? It would be nice to have.

There is another issue. It would be nice to have more readable column name, for example
“First name (given name)” instead of “First_Name”. I removed the matching of a column name and found out that binding doesn’t work for name that include ‘(‘, ‘)’.
‘.’ in the name did not generate an exception, but later I found out that the column was not populated at all.

Thank you.

Posted on 8/13/2008 11:57:13 AM

Hi Dima,

Thanks for your comments. Convertion from and to DBNull is a concern of your database - business object mapper, not IDictionary - DataSource converter. You can read more about the separation ofconcerns here: http://en.wikipedia.org/wiki/Separation_of_concerns or just google it.

The name has current constraints because they are transformed to object properties and those are the constraints of the .NET properties. There should be no reasons why you couldn't work within those constraints as the property name is not something that is shown to the end user.

Posted on 8/15/2008 11:29:25 AM

Hi Vladimir,

Thank you for answer.

1) DbNull. My point was the code does not work properly with null at all. If the first element in a column is null than others element must be null.
I can not convert null to int( or double or DateTime). Actually, I can convert all elements in column to int? (double?, DateTiem?) that can accept null . However, the code should be modified a bit. I did it and it works fine.

2) I totally forgot that a Header name and an object property name could be different.
I fixed my code and now in my DataGrid user can see columns like “Price(before tax)”, “Price (with tax)” for property names like price_1, price_2.

Posted on 10/16/2008 8:44:22 PM

Hi, have you tried this with the released version of Silverlight 2? After I updated to the released version, this no longer works for me.

Posted on 10/17/2008 10:59:02 AM

Hi Brian,

I have Silverlight 2.0.30523.8 ( you can see the number at C:\Program Files\Microsoft Silverlight) and I can see no problems.

What is the version of your silverlight? What is the error message you get?

Posted on 10/21/2008 7:11:56 PM

Hi Vladimir,

Thanks for your prompt response!

The version I am using is 2.0.31005.0 . It was working fine with Silverlight 2.0 Beta 2 but stopped working when I upgraded to the official release version of Silverlight 2.0

The error I am seeing in the debugger is that it is not recognizing the generated datasource as being valid. The message says "Could not evaluate expression" and there are no columns in the bound DataGrid even though there are columns in the original data. I am wondering if Silverlight 2 is no longer recognizing the dynamically generated types.

Copied from the debugger:

DataSource = Could not evaluate expression

Thanks,

Brian Lane

Posted on 10/21/2008 11:42:29 PM

Hi Brian,

I just updated my Silverlight tools to the latest version. I seem to have no problems with the grid dynamic data binding.

I sent to your e-mail a test project with simple grid binding. Try it to see if this works for you. I can see no problem so far.

Vlad

Posted on 10/23/2008 2:46:56 AM

Excellent job! Thank you!

Posted on 10/23/2008 9:09:20 PM

Hi Vladmir,

Thanks for trying it out with the latest Silverlight and sending me the test project! I should have tried a test project like the one you sent me before thinking that the released version of Silverlight 2.0 was the culprit.

I tried your test project and it works fine for me as well. It looks like it is probably another third party Silverlight component that is not working. The debugger problem I was having made it difficult to figure out why the third party grid wasn't binding properly as it had in earlier versions of Silverlight.

Thanks again!

Brian

Posted on 11/3/2008 11:48:17 AM

I receive the "Could not evaluate expression" not when binding, but when attempting to reference the SelectedItem of the bound grid. This was not a problem prior to Silverlight 2.0 full release. Any ideas?

Posted on 11/3/2008 12:07:11 PM

Here is a test project http://www.bodurov.com/files/GridTest.zip that works fine with 2.0 release

Posted on 11/3/2008 12:20:07 PM

And if your question is how to access a property. Here is the answer:

public Page()  
{  
    InitializeComponent();  
  
    this.TheGrid.ItemsSource = GenerateData().ToDataSource();  
  
    this.TheGrid.MouseLeftButtonUp += this.TheGrid_SelectionChanged;  
}  
  
private void TheGrid_SelectionChanged(object sender, EventArgs e)  
{  
    var t = this.TheGrid.SelectedItem.GetType();  
    var s = (string)t.GetProperty("Name").GetValue(this.TheGrid.SelectedItem, null);   
    HtmlPage.Window.Alert("Name:" + s);  
} 
Posted on 11/5/2008 9:15:20 AM

I am attempting GetCellContent(myGrid.SelectedItem) and receive the "expression" error. I don't need the property, I need the cell.

Posted on 11/5/2008 10:33:35 AM

I am not sure what is your issue man. It works just fine for me:

public partial class Page : UserControl 
{ 
    public Page() 
    { 
        InitializeComponent(); 
 
        this.TheGrid.ItemsSource = GenerateData().ToDataSource(); 
 
        this.TheGrid.MouseLeftButtonUp += this.TheGrid_SelectionChanged; 
    } 
 
    private void TheGrid_SelectionChanged(object sender, EventArgs e) 
    { 
        var element = (TextBlock)this.TheGrid.Columns[0].GetCellContent(this.TheGrid.SelectedItem); 
        HtmlPage.Window.Alert("element:" + element.Text); 
    } 
 
    public IEnumerable<IDictionary> 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; 
        } 
    } 
} 
Posted on 11/26/2008 10:26:47 AM

cool code man! I am trying to use this code in SL's AutoCompleteBox. It works very well except for when I type somthing in the box. It displays "TempType49212206" or something like it. I was wondering if you had any thoughts on how to override the ToString() method in the typed object?

thanks,
R

Posted on 11/26/2008 11:08:07 AM

It is possible but that would polute th clean model that this solution follows now. Instead of that you better access the properties of TempType49212206

Posted on 11/26/2008 8:32:05 PM

Hi Vladimir,

Nice code. I am trying to implement it in my solution and am running into following issues.
1. In your code you are adding a "DataGridTextBoxColumn". Is this something that you wrote because I could not find it in SL library.

2. I ended up using a "DataGridTextColumn" but it does not have "DisplayMemberBinding".

Please let me know what I am doing wrong.

Thanks

Gaurav

Posted on 11/26/2008 8:39:05 PM

Please see http://www.bodurov.com/files/GridTest.zip for a test project. Check the references.

Posted on 12/2/2008 8:41:54 AM

Could you give a short example on how to access the properties of TempType49212206?

Thanks,
R

Posted on 12/2/2008 10:14:20 AM

2nd question. When I add a watcher to "row" in this line:
"var row = Activator.CreateInstance(objectType);" I get a "Could not evaluate expression" in the watcher. I think this may be a limatation of Silverlight. do you have any thoughts on that?

Thanks,
R

Sorry about the double posts. not sure what happend.

Posted on 12/2/2008 11:54:21 AM
public Page()   
{   
    InitializeComponent();   
   
    this.TheGrid.ItemsSource = GenerateData().ToDataSource();   
   
    this.TheGrid.MouseLeftButtonUp += this.TheGrid_SelectionChanged;   
}   
   
private void TheGrid_SelectionChanged(object sender, EventArgs e)   
{   
    var t = this.TheGrid.SelectedItem.GetType();   
    var s = (string)t.GetProperty("Name").GetValue(this.TheGrid.SelectedItem, null);    
    HtmlPage.Window.Alert("Name:" + s);   
}  
Posted on 12/2/2008 12:39:12 PM

Thank you Vladimir!
I found another way to make the autocompletebox work with your code and still maintain your "clean model". just set the converter property of the autocompletebox to this:

public class ValueConverter : IValueConverter
{
public object Convert(object value, Type targetType,
object parameter, System.Globalization.CultureInfo culture)
{

if (value != null)
{

System.Reflection.PropertyInfo[] pi = value.GetType().GetProperties();

int last = pi.Count() - 1;
string ret = pi[last].GetValue(value, null).ToString();
return ret;

//Gets property by name//
//System.Reflection.PropertyInfo pi = value.GetType().GetProperty("Index");
//string test = pi.GetValue(value, null).ToString();
}
return null;
}

public object ConvertBack(object value, Type targetType,
object parameter, System.Globalization.CultureInfo culture)
{
return null;
}
}

Thanks again for your help.

R

Posted on 12/18/2008 2:17:54 PM

@petel
@jercra

Did you ever manage to serialize an object and pass it over to silverlight? I am in the middle of figuring this out too.

Rick

Posted on 12/19/2008 2:20:12 PM

No I always use wcf to transfer data, you probably should too, otherwise its like reinventing the wheel, though as something to play with it's may be an interesting thing. I have never tried that.

Posted on 12/29/2008 4:43:41 AM

Hi Vladimir,
First of all thank you for your code - it works really great.
Just one tiny question re: memory allocation - every time I assign new dataset, your code creates a new type. Is this type will ever be garbage collected? If not, what is rough estimation of memory "leak" say for a dataset with 100 columns? Is it small enough so we can all forget about it?

Thanks,
Karol

Posted on 12/29/2008 12:03:41 PM

Oh don't worry about the garbage collection. It works fine. I have set up a project that demonstrates that. You can get it from here: