Posted by: Zeeshan Amjad | September 17, 2011

DataGrid with Dynamic Columns Revisited


We saw one example of dynamic columns here. Now we are going to expand it little bit and introduce different controls in the data grid. We can create multiple data template and use ItemTemplateSelector to load different data template depends on the data. Our first step is add one enum type to define different types.

Code Snippet
public enum ColumnType { Button, CheckBox, TextBox, TextBlock };

 

Now we changed the ColumnData class and add one more property in it.

Code Snippet
public class ColumnData
{
    public string Data
    { get; set; }

    public int Width
    { get; set; }

    public Brush Color
    { get; set; }

    public ColumnType ColumnType
    { get; set; }
}

 

Then we defined different data templates in resource section.

Code Snippet
<DataTemplate x:Key="buttonTemplate">
    <Button Margin="2" Width="{Binding Width}" Content="{Binding Data}"/>
</DataTemplate>
<DataTemplate x:Key="checkBoxTemplate">
    <CheckBox Margin="2" Width="{Binding Width}" Content="{Binding Data}"/>
</DataTemplate>
<DataTemplate x:Key="textBoxTemplate">
    <TextBox Margin="2" Width="{Binding Width}" Text="{Binding Data}"/>
</DataTemplate>
<DataTemplate x:Key="textBlockTemplate">
    <TextBlock Margin="5" Width="{Binding Width}" Text="{Binding Data}"/>
</DataTemplate>

 

Next step is to create a child class of DataTemplateSelector class and defined four properties of DataTemplate types, override SelectTemplate method and return the appropriate data template depends on the data.

Code Snippet
public class MyTypeSelector : DataTemplateSelector
{
    public DataTemplate ButtonTempalate
    { get; set; }

    public DataTemplate TextBoxTemplate
    { get; set; }

    public DataTemplate TextBlockTemplate
    { get; set; }

    public DataTemplate CheckBoxTemplate
    { get; set; }
    public override DataTemplate SelectTemplate(object item, DependencyObject container)
    {
        ColumnData columData = item as ColumnData;

        if (columData.ColumnType == ColumnType.Button)
        {
            return ButtonTempalate;
        }
        else if (columData.ColumnType == ColumnType.CheckBox)
        {
            return CheckBoxTemplate;
        }
        else if (columData.ColumnType == ColumnType.TextBlock)
        {
            return TextBlockTemplate;
        }
        else if (columData.ColumnType == ColumnType.TextBox)
        {
            return TextBoxTemplate;
        }
        else
        {
            return base.SelectTemplate(item, container);
        }
    }
}

 

Then we create its object in XAML and assigned its properties with other data template we just created in the resource section of XAML.

Code Snippet
<local:MyTypeSelector x:Key="typeSelector"
                      ButtonTempalate="{StaticResource buttonTemplate}"
                      TextBlockTemplate="{StaticResource textBlockTemplate}"
                      TextBoxTemplate="{StaticResource textBoxTemplate}"
                      CheckBoxTemplate="{StaticResource checkBoxTemplate}"/>

 

Then use this to set the ItemTemplateSelector property of ItemsControl and do not define data template. Here is complete XAML code of the program.

Code Snippet
<Window x:Class="WpfGridDynamicColumn.MainWindow"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation&quot;
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml&quot;
         xmlns:local="clr-namespace:WpfGridDynamicColumn"
         Title="Dynamic Column Data Grid" Height="350" Width="525">
    <Window.Resources>
        <DataTemplate x:Key="buttonTemplate">
            <Button Margin="2" Width="{Binding Width}" Content="{Binding Data}"/>
        </DataTemplate>
        <DataTemplate x:Key="checkBoxTemplate">
            <CheckBox Margin="2" Width="{Binding Width}" Content="{Binding Data}"/>
        </DataTemplate>
        <DataTemplate x:Key="textBoxTemplate">
            <TextBox Margin="2" Width="{Binding Width}" Text="{Binding Data}"/>
        </DataTemplate>
        <DataTemplate x:Key="textBlockTemplate">
            <TextBlock Margin="5" Width="{Binding Width}" Text="{Binding Data}"/>
        </DataTemplate>
        <local:MyTypeSelector x:Key="typeSelector"
                              ButtonTempalate="{StaticResource buttonTemplate}"
                              TextBlockTemplate="{StaticResource textBlockTemplate}"
                              TextBoxTemplate="{StaticResource textBoxTemplate}"
                              CheckBoxTemplate="{StaticResource checkBoxTemplate}"/>
    </Window.Resources>
    <Grid>
        <DataGrid Margin="5" AutoGenerateColumns="False" ItemsSource="{Binding Columns}">
                            <DataGrid.Columns>
                <DataGridTemplateColumn>
                    <DataGridTemplateColumn.HeaderTemplate>
                        <DataTemplate>
                            <ItemsControl ItemsSource="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}},
                                 Path=DataContext.Headers}">
                                <ItemsControl.ItemsPanel>
                                    <ItemsPanelTemplate>
                                        <StackPanel Orientation="Horizontal"/>
                                    </ItemsPanelTemplate>
                                </ItemsControl.ItemsPanel>
                                <ItemsControl.ItemTemplate>
                                    <DataTemplate>
                                        <TextBlock Margin="2" Width="{Binding Width}" Foreground="{Binding Color}" Text="{Binding Data}"/>
                                    </DataTemplate>
                                </ItemsControl.ItemTemplate>
                            </ItemsControl>
                        </DataTemplate>
                    </DataGridTemplateColumn.HeaderTemplate>
                    <DataGridTemplateColumn.CellTemplate>
                        <DataTemplate>
                            <ItemsControl ItemsSource="{Binding ColumnsData}"
                                          ItemTemplateSelector="{StaticResource typeSelector}">
                                <ItemsControl.ItemsPanel>
                                    <ItemsPanelTemplate>
                                        <StackPanel Orientation="Horizontal"/>
                                    </ItemsPanelTemplate>
                                </ItemsControl.ItemsPanel>
                            </ItemsControl>
                        </DataTemplate>
                    </DataGridTemplateColumn.CellTemplate>
                </DataGridTemplateColumn>
            </DataGrid.Columns>
        </DataGrid>
    </Grid>
</Window>

 

Here is complete C# code of the program.

Code Snippet
using System.Collections.ObjectModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;

namespace WpfGridDynamicColumn
{
    public enum ColumnType { Button, CheckBox, TextBox, TextBlock };

    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

            DataContext = new Table();
        }
    }

    public class Table
    {
        public ObservableCollection<Column> Columns
        { get; set; }

        public ObservableCollection<ColumnData> Headers
        { get; set; }

        public Table()
        {
            Columns = new ObservableCollection<Column>();
            Headers = new ObservableCollection<ColumnData>();

            Columns.Add(new Column()
            {
                ColumnsData = new ObservableCollection<ColumnData>()
                    {
                        new ColumnData()
                        {
                            Data = "row 1 col 1",
                            Width = 100,
                            ColumnType = ColumnType.TextBlock
                        },
                        new ColumnData()
                        {
                            Data = "row 1 col 2",
                            Width = 125,
                            ColumnType = ColumnType.CheckBox
                        },
                        new ColumnData()
                        {
                            Data = "row 1 col 3",
                            Width = 150,
                            ColumnType = ColumnType.TextBox
                        }
                    }
            });

            Columns.Add(new Column()
            {
                ColumnsData = new ObservableCollection<ColumnData>()
                    {
                        new ColumnData()
                        {
                            Data = "row 2 col 1",
                            Width = 100,
                            ColumnType = ColumnType.TextBlock
                        },
                        new ColumnData()
                        {
                            Data = "row 2 col 2",
                            Width = 125,
                            ColumnType = ColumnType.CheckBox
                        },
                        new ColumnData()
                        {
                            Data = "row 2 col 3",
                            Width = 150,
                            ColumnType = ColumnType.TextBox
                        }
                    }
            });

            Columns.Add(new Column()
            {
                ColumnsData = new ObservableCollection<ColumnData>()
                    {
                        new ColumnData()
                        {
                            Data = "row 3 col 1",
                            Width = 100,
                            ColumnType = ColumnType.TextBlock
                        },
                        new ColumnData()
                        {
                            Data = "row 3 col 2",
                            Width = 125,
                            ColumnType = ColumnType.CheckBox
                        },
                        new ColumnData()
                        {
                            Data = "row 3 col 3",
                            Width = 150,
                            ColumnType = ColumnType.TextBox
                        }
                    }
            });

            Columns.Add(new Column()
            {
                ColumnsData = new ObservableCollection<ColumnData>()
                    {
                        new ColumnData()
                        {
                            Data = "row 4 col 1",
                            Width = 100,
                            ColumnType = ColumnType.TextBlock
                        },
                        new ColumnData()
                        {
                            Data = "row 4 col 2",
                            Width = 125,
                            ColumnType = ColumnType.CheckBox
                        },
                        new ColumnData()
                        {
                            Data = "row 4 col 3",
                            Width = 150,
                            ColumnType = ColumnType.TextBox
                        }
                    }
            });

            Headers.Add(new ColumnData()
            {
                Data = "Column 1 Title",
                Width = 100,
                Color = Brushes.Red,
                ColumnType = ColumnType.TextBlock
            });

            Headers.Add(new ColumnData()
            {
                Data = "Column 2 Title",
                Width = 125,
                Color = Brushes.Blue,
                ColumnType = ColumnType.TextBlock
            });

            Headers.Add(new ColumnData()
            {
                Data = "Column 3 Title",
                Width = 150,
                Color = Brushes.Green,
                ColumnType = ColumnType.TextBlock
            });
        }
    }

    public class Column
    {
        public ObservableCollection<ColumnData> ColumnsData
        { get; set; }
    }

    public class ColumnData
    {
        public string Data
        { get; set; }

        public int Width
        { get; set; }

        public Brush Color
        { get; set; }

        public ColumnType ColumnType
        { get; set; }
    }

    public class MyTypeSelector : DataTemplateSelector
    {
        public DataTemplate ButtonTempalate
        { get; set; }

        public DataTemplate TextBoxTemplate
        { get; set; }

        public DataTemplate TextBlockTemplate
        { get; set; }

        public DataTemplate CheckBoxTemplate
        { get; set; }
        public override DataTemplate SelectTemplate(object item, DependencyObject container)
        {
            ColumnData columData = item as ColumnData;

            if (columData.ColumnType == ColumnType.Button)
            {
                return ButtonTempalate;
            }
            else if (columData.ColumnType == ColumnType.CheckBox)
            {
                return CheckBoxTemplate;
            }
            else if (columData.ColumnType == ColumnType.TextBlock)
            {
                return TextBlockTemplate;
            }
            else if (columData.ColumnType == ColumnType.TextBox)
            {
                return TextBoxTemplate;
            }
            else
            {
                return base.SelectTemplate(item, container);
            }
        }
    }
}

 

Here is an output of the program.

DynamicColumnGrid_03

Advertisements

Responses

  1. Hi Zamjad,

    from the screenshot it seems to me that it is actually only one large table column – for example each single “sub-column” can not be sorted.

    Do you think is it possible to get “separated” table columns created dynamically in WPF? I tried some time ago and failed (I am not proficient in WPF) but I did not even found any examples in internet!

    By the way, I am great fan of your blog!

    Mikhail

    • Hi Mikhail

      First thanks to like my blog. Yes you are right it is just one column uses Stack Panel to display multiple data (i.e. multiple columns). I couldn’t came across any solution of generating dynamic columns in Grid Control using XAML conly (other then AutoGenerateColumn property set to true). The only solution i cam across is using the code behind. If you or anyone else came across a XAML based solution, then please let me know.

      Regards
      Zeeshan Amjad

  2. I have this code as a user control library and the headers are not showing…do you know why? I used your exact code that’s here….please help.

    • Hi. I haven’t tried it in user control. I will try it and let you know about my finding. Thanks to visit my blog.

      Regards
      Zeeshan Amjad

  3. Hi,
    first – I like your posts they are very helpfull to me.
    I am a newbe in wpf.

    I have a question – if I want one of the controls to be a combo box.
    the combo box will show a chosen ID according to the column Data. but I also have another datatable containg id’s and appropriate name and I want to display in the combo box the name from that table instead of the ID.
    How do I do that?

    I added whats needed to support the combo box as another column type
    and I have added a data template as follows:

    ComboBoxDataTableList is the datatable I described turned into IList.
    Manufecture_ID and Manufecture_Name are its fields headers.

    When I run the program I get a combo box with an empty list.

    please can you help?

    • Hi Tami

      Thanks to visit my blog. Please correct me if I misunderstood your problem. What I understand is that you want to bind combobox with id but want to display something else like description. If that is the case then please take a look at DisplayMemberPath property. I infact wrote an article on it, but it was on ListBox. It should be same for combo box. You can take a look at article here.

      https://zamjad.wordpress.com/2011/07/20/using-displaymemberpath-in-listbox/

      Regards
      Zeeshan Amjad

  4. Hello!.
    I stumbled upon your blog when looking for an example of creating a dynamic column header. The above example I was approached. Now in this example, I need to change the highlight color of the line. I’m new to the VPF and can not understand how to do it. Could you help me? Thank you.

    • Thanks to visit and like my blog. As far as i know there are multiple ways to do it. You can define your own control template or you can use the PropertyTrigger to change the color when item is selected. Here are few examples of property trigger on my blog

      https://zamjad.wordpress.com/category/wpf/trigger-wpf/propertytrigger/

      Thanks and best Regards
      Zeeshan Amjad


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Categories

%d bloggers like this: