Binding a Silverlight DataGrid to dynamic data via IDictionary

April 17th, 2009 by Colin Eberhardt

This post demonstrates a technique for binding a Silverlight DataGrid to dynamic data, the structure of which is not know at compile-time …

Update: I have extended this technique to add include change notification so that the DataGrid can be made editable. Read all about it in part two.

With Silverlight binding data to a DataGrid is a very straightforward process. With an XML datasource, you can simply parse your data via Linq to SQL into an anonymous type then bind the resulting collection to the grid. Just a few simple lines of code. But, what if the data you want to bind to your grid is dynamic? That is, at compile time you do not know how many columns are required, or what their contents is. This is a common problem that Silverlight users have faced again, and again, and again and again!

The most obvious solution to this problem is to create a dictionary for each row of your DataGrid, and bind your columns to your values via the dictionary’s string indexer:

Binding="{Binding Path=[Name]}"

However, unfortunately the PropertyPath syntax used for binding does not understand indexers, making it impossible to bind to a dictionary.

Vladimir Bodurov presents an ingenious solution to this problem by dynamically generating a new Type based on the values present within the dictionary, i.e. If the dictionary contains the keys “Name” and “Age”, a Type will be generated that has properties of Name and Age. Crazy stuff! Here I would like to show an alternative method that does not use intermediate Language or other black magic!

We will start with a simple example of a class which can be used to store ‘dynamic’ data for rendering in our DataGrid:

public class Row
{
  private Dictionary<string, object> _data = new Dictionary<string, object>();
  public object this [string index]
  {
    get { return _data[index]; }
    set { _data[index] = value; }
  }
}

We can populate a collection of these objects and associate them with a DataGrid as follows:

Random rand = new Random();
 
var rows = new ObservableCollection<Row>();
for (int i = 0; i < 200; i++)
{
  Row row = new Row();
  row["Forename"] = s_names[rand.Next(s_names.Length)];
  row["Surname"] = s_surnames[rand.Next(s_surnames.Length)];
  row["Age"] = rand.Next(40) + 10;
  row["Shoesize"] = rand.Next(10) + 5;
  rows.Add(row);
}
 
_dataGrid.ItemsSource = rows;

However, as mentioned earlier, we cannot create a binding path that accesses our Rows indexer, so just how do we bind the columns to our data?

The classic .NET solution to this problem would be to create a custom property descriptor. When databinding, the properties of an object are not accessed directly, rather they are accessed via their associated property descriptor. A custom property descriptor can be supplied for our Row class, via the ICustomTypeDescriptor interface for example, that exposes properties which when accessed invoke our indexer. This is how the .NET DataRowView exposes its properties. Unfortunately there is one small snag here … Silverlight does not include the required interfaces to create custom properties.

A simple workaround to this problem is to use a binding that binds each row directly to each Row item rather than a specific property of the item, then use a value converter to access the indexer and extract the required value. Here is an example:

<data:DataGrid Name="_dataGrid" AutoGenerateColumns="False" Height="300" IsReadOnly="False">
  <data:DataGrid.Columns>
    <data:DataGridTextColumn Header="Forename" Binding="{Binding Converter={StaticResource RowIndexConverter}, ConverterParameter=Forename}"/> 
    <data:DataGridTextColumn Header="Surname" Binding="{Binding Converter={StaticResource RowIndexConverter}, ConverterParameter=Surname}"/> 
    <data:DataGridTextColumn Header="Age" Binding="{Binding Converter={StaticResource RowIndexConverter}, ConverterParameter=Age}"/> 
    <data:DataGridTextColumn Header="Shoesize" Binding="{Binding Converter={StaticResource RowIndexConverter}, ConverterParameter=Shoesize}"/> 
  </data:DataGrid.Columns>
</data:DataGrid>

And here is the value converter:

public class RowIndexConverter : IValueConverter
{
  public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
  {
    Row row = value as Row;
    string index = parameter as string;
    return row[index];
  }
 
  public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
  {
    throw new NotImplementedException();
  }
}

In the above XAML, the binding expression does not have a Path specified, therefore, the binding source is our Row instance rather than a property of the Row. The RowIndexConverter value converter simply uses the supplied ConverterParameter for each column to access the Row indexer and extract the correct value. This works quite nicely, and we are able to see our data within the grid:

grid

However, there is one problem here, if you click on the column headers the grid does not sort the data. The columns of a DataGrid have CanUserSort and SortMemberPath properties, however setting these will not help because the DataGrid will expect the bound object to have a property with the given name which of course it does not! To solve this problem (without resorting to dynamically generated types) we need to delve a little deeper into the way in which the DataGrid binds to the data.

A lot of insight can be gained from the documentation of ICollectionView:

The DataGrid control uses this interface to access the indicated functionality in the data source assigned to its ItemsSource property. If the ItemsSource implements IList, but does not implement ICollectionView, the DataGrid wraps the ItemsSource in an internal ICollectionView implementation.

The ICollectionView interface is responsible for filtering, sorting and grouping of the data bound to the DataGrid. WPF also has the ICollectionViewinterface, you can read a good overview of its features on Marlon Grech’s blog. However, whereas WPF wraps the DataContext itself in a view which is shared across multiple controls, it would appear that Silverlight restricts its usage to the DataGrid. This causes problems if, for example, you want to synchronize the current item between controls (However, Laurent Bugnion has a novel solution for emulating this behaviour).

So, it is the ICollectionView which is responsible for sorting our data. The internal implementation of this interface which the DataGrid creates will expects our object to expose the bound properties, which explains why it does not work. However, if we supply our own ICollectionView interface, we can take control of sorting and implement it ourselves, accessing our ‘property’ values via the Row’s string indexer.

The ICollectionView interface has a lot of methods, events and properties, fortunately I was able to find a suitable implementation of this interface on Manish Dalal’s blog. He had implemented this interface, on a class which extends ObservableCollection, in order to bind a DataGrid to a collection where the data from the server is being paged, hence sorting must be done server side. This gives us pretty much everything we need here, a collection class which is able to manage sorting itself. The only change required is to the ICollectiomView.Refresh() method which is responsible for refreshing the view after the SortDescriptions have changed.

The implementation of this method is as follows:

public class SortableCollectionView : ObservableCollection<Row>, ICollectionView
{
 
  ...
  public void Refresh()
  {
      IEnumerable<Row> rows = this;
      IOrderedEnumerable<Row> orderedRows = null;
 
      // use the OrderBy and ThenBy LINQ extension methods to
      // sort our data
      bool firstSort = true;
      for (int sortIndex = 0; sortIndex < _sort.Count; sortIndex++)
      {
          SortDescription sort = _sort[sortIndex];
          Func<Row, object> function = row => row[sort.PropertyName];
          if (firstSort)
          {
              orderedRows = sort.Direction == ListSortDirection.Ascending ?
                  rows.OrderBy(function) : rows.OrderByDescending(function);
 
              firstSort = false;
          }
          else
          {
              orderedRows = sort.Direction == ListSortDirection.Ascending ?
                  orderedRows.ThenBy(function) : orderedRows.ThenByDescending(function);
          }
      }
 
      _suppressCollectionChanged = true;
 
      // re-order this collection based on the result if the above
      int index = 0;
      foreach (var row in orderedRows)
      {
          this[index++] = row;
      }
 
      _suppressCollectionChanged = false;
 
      // raise the required notification
      this.OnCollectionChanged(
          new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
   }
 
  ...
}

When the user clicks on a grid view header, it modifies the bounds ICollectionView.SortDescription to reflect this change in state. The implementation of ICollectiomView from Manish’s blog invokes the Refresh method whenever the ICollectionView.SortDescription collection changes. In the above implementation we use the OrderBy and ThenBy LINQ extension methods to order the data. Interestingly, OrderBy is a method on IEnumerable, whereas ThenBy is a methods on the IOrderedEnumerable, hence the funny looking logic involving ‘firstSort’. Once the sorting has been performed, the underlying collection is re-ordered to match. The only subtle part is that we use a boolean field, _suppressCollectionChanged to suppress the numerous CollectionChanged events that would be fired by ObservableCollection during this process. Finally we raise NotifyCollectionChanged, resulting in the DataGrid updating to reflect the sort order.

Putting it all together, we simply populate our SortableCollectionView and bind this to the DataGrid, modifying the XAML to explicitly inform the grid that it can sort and which property each column sorts on (usually this is inferred from the Binding Path):

<data:DataGrid Name="_dataGrid" AutoGenerateColumns="False"  IsReadOnly="False" Margin="5">
    <data:DataGrid.Columns>
        <data:DataGridTextColumn Header="Forename" CanUserSort="True" SortMemberPath="Forename" 
                                  Binding="{Binding Converter={StaticResource RowIndexConverter},
                                    ConverterParameter=Forename}"/>
        <data:DataGridTextColumn Header="Surname" CanUserSort="True" SortMemberPath="Surname" 
                                 Binding="{Binding Converter={StaticResource RowIndexConverter},
                                    ConverterParameter=Surname}"/>
        <data:DataGridTextColumn Header="Age" CanUserSort="True" SortMemberPath="Age" 
                                 Binding="{Binding Converter={StaticResource RowIndexConverter},
                                    ConverterParameter=Age}"/>
        <data:DataGridTextColumn Header="Shoesize" CanUserSort="True" SortMemberPath="Shoesize" 
                                 Binding="{Binding Converter={StaticResource RowIndexConverter},
                                    ConverterParameter=Shoesize}"/>                
    </data:DataGrid.Columns>
</data:DataGrid>

And here it is in action:

You can download the complete solution here: silverlightdynamicbinding.zip.

Regards,
Colin E.

Tags: , ,

49 Responses to “Binding a Silverlight DataGrid to dynamic data via IDictionary”

  1. Matt says:

    We tried this in the past and it does work. Problem is the sorting in the grid doesn’t work if I remember correctly.

  2. Hi Matt, I am not quite sure what you mean? the second half of this article is about just that – making sort work. The DataGrid at the end of this article even shows it working in practice. Give it a go! … multi-column sorting works too.

    Regards, Colin E.

  3. David Roh says:

    Awesome solution Colin – thank you very much for sharing this.

    David Roh

  4. Thanks David – glad you like it :-)

    Regards, Colin E.

  5. Jones says:

    Nice Blog…

  6. Kevin says:

    Great job, really useful.
    I have a collection of objects binding to the DataGrid, but one of the object’s property is also a collection of items. I’m trying to (2-ways) bind this sub-collection to the DataGrid’s column but I am not able to get the result.

    Can I use your way to resolve my issue?

    Thanks

  7. Hi Kevin,

    I would not have thought my method would be necessary for the problem you describe. How are you rendering this collection object property? Are you using an ItemsControl (or subclass) of some sort? You will need to ensure that this collection property provides suitable change notification – is it an ObservableCollection? If you get stuck try the forums here:

    http://silverlight.net/forums

    There are lots of helpful people there!

    Regards,
    Colin E.

  8. Fallon Massey says:

    Colin, is it possible to make the data in the grid editable(NotifyPropertyChanged)?

  9. Hi Fallon, that is a very good question! Editing seems to fail completely, this is probably due to the lack of a Binding Path. I have also tried implementing INotifyPropertyChanged on the Row class, and raising the PropertyChanged event whenever an indexed property is changed from code-behind, however the grid still does not update – again due to a lack of Binding Path.

    I have a few other ideas to try … if I have any success I will let you know!

    Regards, Colin E.

  10. Antonis says:

    Hi,

    This is very interesting, great work! You know if something like that is feasible when you are dealing with WCF Services?!!?

    Is there any workarounds on this?! I mean not being able to serialize the object..

    Many thanks,

    Antonis

  11. @Antonis,

    I am guessing you mean, “Can I return a dynamic Row object from a WCF service?”, if so, I cannot see any problem with serializing this object and consuming it from a client application. Or are you talking about an existing WCF service that you are consuming?

    Regards, Colin E.

  12. Antonis says:

    My problem is that i cannot serialize a class that it’s contents are type of Dictionary. I am getting an exception cause i think the service cannot serialize the object type.

  13. Antonis says:

    When i say IDictionary i mean the object value of the dictionary

  14. @Antonis,

    I have read about problems with serializing Dictionary from a WCF service, although I haven’t tried it in practice. I would recommend asking this question in the WCF forums:

    http://social.msdn.microsoft.com/forums/en-US/wcf/threads/

    Good luck :-)
    Regards, Colin E.

  15. Colin,

    I am looking for a dynamic column object/datagrid setup, which on first blush seems to be what you’re presenting here. However, it seems that it is not, in the sense that you need to know the names of the columns (or keys of the dictionary) at compile time, as they are passed into the converters in XAML.

    Am I missing something?

    Thanks,
    KB

  16. Hi Kevin,

    Yes, in this example the values are hard-coded into the XAML, however, this was for simplicity more than anything else. The solution dos give fully dynamic binding … XAML is not the only way to add columns to the grid. You can add them in code behind allowing you to programmatically add columns with bindings dependant on the columns in your dynamic data:


    DataGridTextColumn column = new DataGridTextColumn()
    {
    Header = "Forename",
    CanUserSort = true,
    SortMemberPath = "Forename",
    Binding = new Binding()
    {
    Path = new PropertyPath("Data"),
    Converter = new RowIndexConverter(),
    ConverterParameter = "Forename"
    }
    };
    _dataGrid.Columns.Add(column);

    (The above example code works with the second article I wrote on dynamic binding).

    Hope this helps. Perhaps I should add a final article which puts it all together – what do you think?

    Regard, Colin E.

  17. Geoff says:

    I get the below exception running this (silverlight beta 3). Do I need to add support for groups? Better alternative in SLB3?

    System.NotImplementedException was unhandled by user code
    Message=”The method or operation is not implemented.”
    StackTrace:
    at SilverlightTable.SortableCollectionView.get_Groups()
    at System.Windows.Controls.DataGrid.RefreshRowGroupHeaders()
    at System.Windows.Controls.DataGrid.RefreshRows(Boolean recycleRows)
    at System.Windows.Controls.DataGrid.RefreshRowsAndColumns()
    at System.Windows.Controls.DataGrid.MeasureOverride(Size availableSize)
    at System.Windows.FrameworkElement.MeasureOverride(IntPtr nativeTarget, Single inWidth, Single inHeight, Single& outWidth, Single& outHeight)
    InnerException:

  18. Hi Geoff,

    The SL2.0 DataGrid does not support grouping, hence I did not implement the GroupDescriptions or Groups properties of the bound collection. The SL3.0 DataGrid does support grouping, and when the grid is grouped will use the Groups property on the bound collection to access the top level groups – and from there reach the nested groups / items.

    Therefore, to make it work with SL3.0 you are going to have to implement these properties.

    Regards,
    Colin E.

  19. Vitaly says:

    Hi Colin,
    Could you look at this:
    http://silverlightdataset.codeplex.com

  20. Hi,

    Yes – I saw that project a few days ago when I noticed the forum link back to this article. It is an interesting project, I am sure that if it is doen well there will be a lot of people interested in using it.

    Regards,
    Colin E.

  21. Lars Erik says:

    Hi,

    Great sln. Thanks. Now that you have implemented the ICollectionView, I guess it should be possible to also use the Filter property.

    I tried just modifying the Filter, and the CanFilter property, but with no luck. Any idea whats required to get your SortableCollectionView extended to a ‘SortableAndFilterableCollectionView’

    Thanks Larsi

  22. Hi Larsi,

    Yes, i can see how that would not work, the Filter property is there, but it just sets the private _filter field and does nothing. You are going to have to implement it yourself.

    The problem is, even though this class implements ICollectionView, it is not actually a view onto a collection. In other words, if you apply a filter, how do you revert to the non-filtered original collection? I’m afraid that to get filtering to work, you are going to have to rework this class so that it ‘decorates’ a collection rather than extending it so that you can maintain both a filtered collection and the original one.

    Hope that helps,
    Colin E.

  23. Hi Colin,

    the solution seems quite nice, but I’m having a problem still. Because not only the data is created at runtime but also the number and title of the columns is created by the user.

    I’m having a problem binding the things to the dictionary because I don’t know the name of the headers :S … Am using basically what you put here. But not being able to put it to work:s

    Is there anyway of accessing the dictionary items through an index? Or shouldn’t I use a dictionary? Hope you can help me somehow!

    Tks,

    simao

  24. Hi Simao,

    I am not entirely sure what you mean? If the number and title of the columns is determined at runtime, I think the best option is to programmatically add the columns to your DataGrid in code-behind.

    Colin E.

  25. Hi, sorry, I think I was not too clear.

    I ended up doing my table layout with StackPanels and TextBox/TextBlocks. Nevertheless I’ll try to rephrase it in case I find the time to change it into a datagrid later.

    What I have is a Web Application where the user can define a variable number of items of the type Criteria and then items of the type Scenario. Then it does some pairwise comparisons for them all. In the end I want to display the results.
    I tried to implement your solution but didn’t know how to access the dictionary items without knowing what was the name of each row…

    So I tried to use the following class:

    public string scenariOname
    public List listOfVals;

    (where the Type class has a String for Name and a Double for Val)

    And then I want to display like this example:

    ############ | Criteria A | Criteria B | … | Total |
    ############ | – | SubCriteria B1 | SubCriteria B2 | …. | |
    Scenario 1 | Val 1 | Val 2 | Val 3 | … | t1 |
    Scenario 2 | Val 2.1 | Val 2.2 | Val 3.2 | …. | t2 |

    But I don’t know how to bind each item of the list that shared the same name to one column of the DataGrid. I don’t even know if that is possible.

    Sorry for the mess and thanks for the answer!=)

    simao

  26. Jeremy Holt says:

    Hi Colin,
    I also don’t know the names of the columns until run time. The data is a dynamically created DataTable/DataView where the column headers vary on each run. So columns could be:

    Commodity | Jan 09 | Feb 09 | Sep 09 | Mar 10 | Total | on one run and then
    Commodity | Feb 08 | Apr 09 | Total | on another run.

    The only thing I need to do is to left align the Commodity column and right align the other columns, and StringFormat the “month” and “total” columns. However, I can’t work out how to get the Path access to the column in XAML.
    In my (actually yours from a CodeProject article :) ) style I have the following. Path=. or Path=Items gives me access to the DataGridRow, but I need to get the cell value.

    The datatrigger for Commodity doesn’t work either :)
    This is WPF by the way.

    Many thanks in advance for any help on this.
    Jeremy

  27. Jeremy Holt says:

    I should have added that the grid’s AutoGenerateColumns = “True”

  28. Hi Jeremy,

    If you do not know the names of your columns, or how many columns you might have until runtime, I would suggest that the easiest approach would be to add columns in code-behind rather than in XAML. In code-behind you can access the grids column’s collection, set the heading text, apply styles etc …

    Regards, Colin E.

  29. Silverlight DataSet (http://silverlightdataset.codeplex.com) supports binding to DataGrid with AutoGenerateColumns=”True” now

  30. Hi Vitaly,

    Looks very interesting – good luck with that one.

    I see that the discussion thread that referenced this article has been deleted – that’s not very sporting is it ;-)

    Regards, Colin E.

  31. Ben Lowe says:

    Thanks for the post. I’m in the process of prototyping my first SL3 app and surprise surprise, I don’t know what my XML structure will be at design time. So, Got my dynamic objects populating but when I click the column header to order in the DataGrid it goes into DeferRefresh which isn’t currently implemented.

    Any idea what I need to put in there to get it to work in SL3? I am a UX designer so my C# skills are suitably limited I’m afraid!

    Cheers, Ben

  32. Hi Ben,

    I haven’t played with the datagrid in SL3 much,and the documentation of DeferRefresh makes very little sense. You coudl try the following article:

    http://www.codeproject.com/KB/silverlight/autofiltering_silverlight.aspx

    I think it provides an implementation of ICollectionView, so may have what you are looking for.

    Colin E.

  33. grayson mitchell says:

    If anyone has found a solution for SL3 compatibility, would be great if you could link it in.

  34. Grayson Mitchell says:

    I have been trying to implement this for a while, and have just worked out that ria services seems to ’stuff things up’ The binding returns the result of: SilverlightApplication3.Row

    rather than the value… I recreated this demo, only including ria services, and it no longer works

  35. Roby Kaufman says:

    I have implemented this solution into a silverlight 3 app and will post once I figure out how to implement the DeferRefresh method. All of my columns are created in code.
    Here is a partial post.

    void client_GetAncilGroupsCompleted(object sender, GetAncilGroupsCompletedEventArgs e)
    {
    cbAncilGroupSelect.ItemsSource = e.Result;
    }

    void client_GetAncilDataCompleted(object sender, GetAncilDataCompletedEventArgs e)
    {
    ObservableCollection<Dictionary> oc = new ObservableCollection<Dictionary>();
    oc = e.Result;

    //var rows = new ObservableCollection();
    SortableCollectionView rows = new SortableCollectionView();
    foreach (var d in oc)
    {
    Row row = new Row();
    foreach (var item in d)
    {
    row[item.Key.ToString()] = item.Value.ToString();
    }
    rows.Add(row);
    }

    test.ItemsSource = rows;
    }

    private void cbAncilGroupSelect_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
    if (myListBox != null)
    {
    client.GetAncilFieldsCompleted += new EventHandler(client_GetAncilFieldsCompleted);
    client.GetAncilFieldsAsync(e.AddedItems[0].ToString());
    }
    client.GetAncilDataCompleted += new EventHandler(client_GetAncilDataCompleted);
    client.GetAncilDataAsync(e.AddedItems[0].ToString());
    }

    void client_GetAncilFieldsCompleted(object sender, GetAncilFieldsCompletedEventArgs e)
    {
    ObservableCollection colList = e.Result;
    RowIndexConverter ric = new RowIndexConverter();
    DataGridTextColumn aCol = new DataGridTextColumn();
    DataGridTextColumn bCol = new DataGridTextColumn();
    DataGridTextColumn cCol = new DataGridTextColumn();
    Row r = new Row();
    aCol.Header = “MemberId”;
    test.Columns.Add(aCol);
    aCol.Binding = new Binding {Mode = BindingMode.OneWay, Path = new PropertyPath(“Data”),
    Converter = ric, ConverterParameter = “MemberId” };
    aCol.SortMemberPath = “MemberId”;
    aCol.CanUserSort = true;
    bCol.Header = “Name”;
    test.Columns.Add(bCol);
    bCol.Binding = new Binding { Mode = BindingMode.OneWay, Path = new PropertyPath(“Data”),
    Converter = ric, ConverterParameter = “Name” };
    bCol.SortMemberPath = “Name”;
    bCol.CanUserSort = true;
    cCol.Header = “Squadron”;
    test.Columns.Add(cCol);
    cCol.Binding = new Binding { Mode = BindingMode.OneWay, Path = new PropertyPath(“Data”),
    Converter = ric, ConverterParameter = “Squadron” };
    cCol.SortMemberPath = “Squadron”;
    cCol.CanUserSort = true;

    foreach (string k in colList)
    {
    if (k.ToString() != “MemberId” && k.ToString() != “Name” && k.ToString() != “Squadron”)
    {
    DataGridTextColumn col = new DataGridTextColumn();
    col.Header = k;
    col.Binding = new Binding { Mode = BindingMode.TwoWay, Path = new PropertyPath(“Data”),
    Converter = ric, ConverterParameter = (k) };
    col.CanUserSort = true;
    col.SortMemberPath = k;
    test.Columns.Add(col);
    }
    }
    test.Visibility = Visibility.Visible;
    }
    }

  36. Larry Martin says:

    I try and run the source code available for download on your site and the sorting of columns does not work in it. Any reason why? Is this just for me or is there a problem with the source code? Let me know thanks.

  37. Bundle says:

    Hi
    Good solution!
    But I have a small problem.
    I followed the solution, it is adding the columns dynamically, also adding rows to datagrid, however, it is not populating the cells properly, they are empty, although there is a actual row there.
    So I dont see the values, What am i doing missing?
    Cheers

  38. Hi Larry, Not sure what the problem is you are having – it has worked for most people!

    Colin E.

  39. Hi Bindle, it is very hard to say what the problem is from your description. Try downloading the sample attached to this blog post, building it, seeing if it works for you then starting to make changes from there.

    Colin E.

  40. Bundle says:

    I downloaded. The solution is working as expected when I create the columns in XAML. however when i do that in code behind like

    DataGridTextColumn column = new DataGridTextColumn()
    {
    Header = item.Key,
    CanUserSort = true,
    SortMemberPath = item.Key,
    Binding = new Binding()
    {
    Mode = BindingMode.OneWay,
    Path = new PropertyPath(“Data”),
    Converter = new RowIndexConverter(),
    ConverterParameter = item.Key
    }
    };

    MySearchResult.SearchDetailDataGrid.Columns.Add(column);

    It is not populating the cells.is Path = new PropertyPath(“Data”) always constant?

  41. Bundle says:

    or if I do the same modification in demo code like
    DataGridTextColumn column2 = new DataGridTextColumn()
    {
    Header = “Age”,
    CanUserSort = true,
    SortMemberPath = “Age”,
    Binding = new System.Windows.Data.Binding()
    {
    Mode = System.Windows.Data.BindingMode.OneWay,
    Path = new PropertyPath(“Data”),
    Converter = new RowIndexConverter(),
    ConverterParameter = “Age”
    }
    };
    No way…

  42. Bundle says:

    it worked without Path = new PropertyPath(“Data”)
    Cheers

  43. grayson mitchell says:

    Hi Colin,

    This all works great (I have kept coming back to this option/site for over a year), but the one thing that stops me implementing this solution is defining the columns in xaml… I do not know my columns at design time, so I am a bit stuck. Is there any way I can use the RowIndexConverter without specifying the columns up front?

    One very inelegant option would be to assign a maximum number of columns, and then just setting dummy values “Col1″, “Col2″… is there any other options?

  44. Hi … why not just create the columns and their bindings in code-behind? See the following:

    http://blogs.msdn.com/scmorris/archive/2008/04/14/defining-silverlight-datagrid-columns-at-runtime.aspx

    Regards, Colin E.

  45. grayson mitchell says:

    Fantastic! That was the missing piece that was stopping convert my asp mvc application to Silverlight! I Love you man (cry)

  46. Grayson Mitchell says:

    OK, The one thing missing from that link is how to attach the converter. I have put togetther the following code to add column in the code behind, but the converter is still not working… If anyone has any bright ideas what is missing, that would be greatly appresiated.

    // add columns in code-behind
    DataGridTextColumn textColumn = new DataGridTextColumn();
    textColumn.Header = “First Name”;

    Binding bind = new Binding(“Forename”);
    bind.Converter = new RowIndexConverter() ;
    bind.ConverterParameter = “Forename”;

    textColumn.Binding = bind;
    _dataGrid.Columns.Add(textColumn);

  47. grayson mitchell says:

    OK the trick is that you can’t bind to “Forename”, as it is the row object you are binding to… so the code should be like this:
    Binding bind = new Binding();
    bind.Mode = OneWay;

  48. @Grayson, Glad you managed to fix the problem – and thanks for posting back with your findings. Have fun!

Leave a Reply