Recommended

Skip Navigation LinksHome / Articles / View Article

Defining Silverlight DataGrid Columns at Runtime

+ Add to SilverlightShow Favorites
7 comments   /   aggregated from Scott Morrison on Apr 14, 2008  /  original article
(5 votes)
Categories: Controls , Learn , Tutorials , QuickStarts

Now that you know the basics of the Silverlight DataGrid and how to specify the Columns in XAML, you might want to customize your DataGrid's columns at runtime.

This process is pretty straight forward, so instead of doing the usual end-to-end walk through, I'm going to provide you with a Rosetta Stone between the static XAML form and the dynamic C#/VB form for the scenario in the last post.

Defining a DataGrid Text Column

The following creates a DataGridTextBoxColumn bound to the FirstName property with a header of "First Name", and adds it to the columns collection of the DataGrid named "targetDataGrid".

Static
 <my:DataGrid x:Name="targetDataGrid">
     <my:DataGrid.Columns>
         <my:DataGridTextBoxColumn Header="First Name" 
             DisplayMemberBinding="{Binding FirstName}" />
     my:DataGrid.Columns>
 my:DataGrid>
Dynamic

C#

using System.Windows.Data;
...
DataGridTextBoxColumn textBoxColumn = new DataGridTextBoxColumn();
textBoxColumn.Header = "First Name";
textBoxColumn.DisplayMemberBinding = new Binding("FirstName");
targetDataGrid.Columns.Add(textBoxColumn);

VB

Imports System.Windows.Data
...
Dim TextBoxColumn As New DataGridTextBoxColumn
TextBoxColumn.Header = "First Name"
TextBoxColumn.DisplayMemberBinding = New Binding("FirstName")
TargetDataGrid.Columns.Add(TextBoxColumn)

 

Defining a DataGrid CheckBox Column

The following creates a DataGridCheckColumn bound to the Available property with a header of "Available", and adds it to the columns collection of the DataGrid named "targetDataGrid".

Static
 <my:DataGrid x:Name="targetDataGrid">
     <my:DataGrid.Columns>
         <my:DataGridCheckBoxColumn Header="Available " 
             DisplayMemberBinding="{Binding Available}" />
     my:DataGrid.Columns>
 my:DataGrid>
Dynamic

C#

using System.Windows.Data;
...
DataGridCheckBoxColumn checkBoxColumn = new DataGridCheckBoxColumn();
checkBoxColumn.Header = "Available";
checkBoxColumn.DisplayMemberBinding = new Binding("Available");
targetDataGrid.Columns.Add(checkBoxColumn);

VB

Imports System.Windows.Data
...
Dim CheckBoxColumn As New DataGridCheckBoxColumn
CheckBoxColumn.Header = "Available"
CheckBoxColumn.DisplayMemberBinding = New Binding("Available")
targetDataGrid.Columns.Add(CheckBoxColumn)

 

Defining a DataGrid Template Column

The following creates a DataGridTemplateColumn bound to the Birthday property with a header of "Birthday", and adds it to the columns collection of the DataGrid named "targetDataGrid".

Static
<UserControl.Resources>
    <local:DateTimeConverter x:Key="DateConverter" />
UserControl.Resources>
...
<my:DataGrid x:Name="targetDataGrid" AutoGenerateColumns="False" >
    <my:DataGrid.Columns>
        <my:DataGridTemplateColumn Header="Birthday">
            <my:DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <TextBlock 
                        Text="{Binding Birthday, 
                        Converter={StaticResource DateConverter}}" 
                        FontFamily="Trebuchet MS" FontSize="11" 
                        Margin="5,4,5,4"/>
                DataTemplate>
            my:DataGridTemplateColumn.CellTemplate>
            <my:DataGridTemplateColumn.CellEditingTemplate>
                <DataTemplate>
                    <DatePicker 
                        SelectedDate="{Binding Birthday, Mode=TwoWay}" />
                DataTemplate>
            my:DataGridTemplateColumn.CellEditingTemplate>
        my:DataGridTemplateColumn>
    my:DataGrid.Columns>
my:DataGrid>
Dynamic

There are two ways to dynamically create a template column for a DataGrid.  One is to load in the CellTemplate and CellEditingTemplates as DataTemplates from resources, and the other is to construct the DataTemplates on the fly using XamlReader.

1. Resources Method

This method creates the CellTemplate DataTemplate and the CellEditingTemplate DataTemplate in XAML and stores them as named resources.  Then when the column is created the DataTemplates are used.

Use the below XAML to create the DataTemplates as resources to support the code for this method.

<UserControl.Resources>
    <local:DateTimeConverter x:Key="DateConverter" />
    <DataTemplate x:Key="myCellTemplate">
        <TextBlock 
            Text="{Binding Birthday, 
            Converter={StaticResource DateConverter}}" 
            FontFamily="Trebuchet MS" FontSize="11" 
            Margin="5,4,5,4"/>
    DataTemplate>
    <DataTemplate x:Key="myCellEditingTemplate">
        <DatePicker 
            SelectedDate="{Binding Birthday, Mode=TwoWay}" />
    DataTemplate>
UserControl.Resources>

C#

using System.Windows.Data;
...
DataGridTemplateColumn templateColumn = new DataGridTemplateColumn();
templateColumn.Header = "Birthday";
templateColumn.CellTemplate = (DataTemplate)Resources["myCellTemplate"];
templateColumn.CellEditingTemplate = 
                       (DataTemplate)Resources["myCellEditingTemplate"];
targetDataGrid.Columns.Add(templateColumn);

VB

Imports System.Windows.Data
...
Dim TemplateColumn As New DataGridTemplateColumn
TemplateColumn.Header = "Birthday"
TemplateColumn.CellTemplate = Resources("myCellTemplate")
TemplateColumn.CellEditingTemplate = Resources("myCellEditingTemplate")
targetDataGrid.Columns.Add(TemplateColumn)

2. XamlReader Method

This method creates the DataTemplate inline using the XamlReader class.  This class takes a string and parses it to try to build a visual tree.  In this case we are creating DataTemplates.  This method is especially useful if the DataTemplate itself has to be dynamic.  One example being if you wanted to modify what the element in the template was data bound to.

Warning: This method is considerably more difficult than the resources method.  I recommend only using this if you need to dynamically create the DataTemplate.

Some things to watch out for:

  1. You will need to declare any XAML namespace that is used in the data template
  2. Any custom XAML namespace needs to specify both the clr-namespace and the assembly
  3. You cannot have white space between the xmlns: and the name of your namespace
  4. External resources cannot be referenced, they need to be declared inline
  5. The entire template is a single line, so if you get a XAML Parse exception, it will always say line 1, however the character is fairly accurate if you were to concatenate all of your lines.
  6. When using the StringBuilder approach shown below, make sure that you have the correct white space at the end of a line so that it is correctly separated when concatenated with the next line.

Now that the warnings are out of the way, here is how you do the equivalent of the code above:

C#

using System.Windows.Data;
using System.Windows.Markup;
using System.Text;
...
DataGridTemplateColumn templateColumn = new DataGridTemplateColumn();
templateColumn.Header = "Birthday";
StringBuilder CellTemp = new StringBuilder();
CellTemp.Append(");
CellTemp.Append("xmlns='http://schemas.microsoft.com/client/2007' ");
CellTemp.Append("xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml' ");
//Be sure to replace "YourNamespace" and "YourAssembly" with your app's 
//actual namespace and assembly here
CellTemp.Append("xmlns:local = 'clr-namespace:YourNamespace");
CellTemp.Append(";assembly=YourAssembly'>");
CellTemp.Append("");
CellTemp.Append("");
CellTemp.Append("");
CellTemp.Append("");
CellTemp.Append(");
CellTemp.Append("Text = '{Binding Birthday, ");
CellTemp.Append("Converter={StaticResource DateConverter}}' ");
CellTemp.Append("FontFamily='Trebuchet MS' FontSize='11' ");
CellTemp.Append("Margin='5,4,5,4'/>");
CellTemp.Append("");
CellTemp.Append("");
StringBuilder CellETemp = new StringBuilder();
CellETemp.Append(");
CellETemp.Append("xmlns='http://schemas.microsoft.com/client/2007' ");
CellETemp.Append("xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>");
CellETemp.Append(");
CellETemp.Append("SelectedDate='{Binding Birthday, Mode=TwoWay}' />");
CellETemp.Append("");
templateColumn.CellTemplate = 
    (DataTemplate)XamlReader.Load(CellTemp.ToString());
templateColumn.CellEditingTemplate = 
    (DataTemplate)XamlReader.Load(CellETemp.ToString());
targetDataGrid.Columns.Add(templateColumn);

VB

Imports System.Windows.Data
Imports System.Text
Imports System.Windows.Markup
...
Dim TemplateColumn As New DataGridTemplateColumn
TemplateColumn.Header = "Birthday"
Dim CellTemp As New StringBuilder
CellTemp.Append(")
CellTemp.Append("xmlns = 'http://schemas.microsoft.com/client/2007' ")
CellTemp.Append("xmlns:x = 'http://schemas.microsoft.com/winfx/2006/xaml' ")
'Be sure to replace "YourNamespace" and "YourAssembly" with your app's
'actual namespace and assembly here
CellTemp.Append("xmlns:local = 'clr-namespace:YourNamespace")
CellTemp.Append(";assembly=YourAssembly'>")
CellTemp.Append("")
CellTemp.Append("")
CellTemp.Append("")
CellTemp.Append("")
CellTemp.Append(")
CellTemp.Append("Text = '{Binding Birthday, ")
CellTemp.Append("Converter={StaticResource DateConverter}}' ")
CellTemp.Append("FontFamily='Trebuchet MS' FontSize='11' ")
CellTemp.Append("Margin='5,4,5,4'/>")
CellTemp.Append("")
CellTemp.Append("")
Dim CellETemp As New StringBuilder
CellETemp.Append(")
CellETemp.Append("xmlns = 'http://schemas.microsoft.com/client/2007' ")
CellETemp.Append("xmlns:x = 'http://schemas.microsoft.com/winfx/2006/xaml'>")
CellETemp.Append(")
CellETemp.Append("SelectedDate='{Binding Birthday, Mode=TwoWay}' />")
CellETemp.Append("")
TemplateColumn.CellTemplate = XamlReader.Load(CellTemp.ToString())
TemplateColumn.CellEditingTemplate = XamlReader.Load(CellETemp.ToString())
targetDataGrid.Columns.Add(TemplateColumn)

 

Up next time - a reason to dynamically define columns at runtime: responding to the AutoGeneratingColumn event.

Share


Comments

Comments RSS RSS
  • RE: Defining Silverlight DataGrid Columns at Runtime  

    posted by Muhammad Saifullah. on Nov 18, 2008 08:41

    Excelent...

  • RE: Defining Silverlight DataGrid Columns at Runtime  

    posted by MSW on Dec 24, 2008 14:54

    Scott - this works assuming that the field names for binding (i.e. Birthday) are pre-defined at design time.  The trickier case is where your fields are not known until run-time.  As an example, a grid where the rows are various products and the columns are categories defined and populated by the user at run-time.  How can we solve that requirement, since the binding cannot be defined at design time?

  • RE: Defining Silverlight DataGrid Columns at Runtime  

    posted by bobdupuy on Jan 27, 2009 04:43

    Hi,

    I'm interested in the second approach using XamlReader.Load.

    Unfortunately, the c# (and VB) code provided doesn't compile. There are extra " characters in the string literals. Futhermore, looking at the XAML generated by the string builder, it looks incomplete: There's no data template, no graphic object, just namespaces and properties...Is it expected ?

    I'm trying to figure out how to use custom converters with XamlReader.Load: How to declare the converter in XAML ?

    Thanks

    Robert.

  • RE: Defining Silverlight DataGrid Columns at Runtime  

    posted by Enrai on Jan 27, 2009 06:59

    Hi, bobdupuy,

    in these two tips you can find some useful information regarding the usage of custom value converters in Silverlight - How to format a binding value using converters? and How to pass a parameter to a value converter? ;)

  • RE: Defining Silverlight DataGrid Columns at Runtime  

    posted by Muhammad Saifullah on Jan 29, 2009 08:48

    Hi,

    It realy helped to add template column dynamically in the datagrid. But when we try to attach keydown event to the textbox when using xaml Reader metho?

  • Runtime data provider  

    posted by Jeti on Feb 01, 2009 14:05

     Hey! I'm new to Silverlight. I come from a JAVA background with a small detour to Flex. While developing Flex, I got used to adding random properties to Object instances any time I wanted. That helped a lot when I wanted to render a two dimensional data grid with column names unknown until web service result arrived.

    I'm asking the same thing as MSW: how can I define a two dimensional array as source of data and give the coloumn names at runtime?

    Thanks, Jeti

     

  • RE: Defining Silverlight DataGrid Columns at Runtime  

    posted by Babita on Apr 01, 2010 10:51

    Hi ,

    I have to generate a radgridview dynamically at runtime based on  the no of rows and column is given by user .Suppose 2 rows and 3 column is created.Now In first row user will enter column name in all the cell except the first cell.And in first column user will enter row name except  the first cell.In the remaining cell i have to create a textbox and place an image .by clicking on the image a pop up will open and after selecting value from it that value is displayed in the textbox of the that cell.finally i have to save the whole value in the database.How would i do this.I will able to create no of rows and column by creating Datatable .But not able to do remaining part.please help.thanks in advance.

    Thanks in advance.

Add Comment

 
 

   
  
  
   
Please add 2 and 4 and type the answer here:

Help us make SilverlightShow even better and win a free t-shirt. Whether you'd like to suggest a change in the structure, content organization, section layout or any other aspect of SilverlightShow appearance - we'd love to hear from you! Need a material (article, tutorial, or other) on a specific topic? Let us know and SilverlightShow content authors will work to have that prepared for you. (hide this)