CellTemplateSelector for DataGridTemplateColumn example

Jul 8, 2009 at 7:46 PM
Edited Jul 8, 2009 at 7:50 PM

Hi,

Could someone please provide a sample code/markup on how to use CellTemplateSelector with DataGridTemplateColumn.

I am binding a generic List<Product> to the DataGrid.  I would like to display different CellTemplates according to the Product Type (part of the Product object).

Thanks! 

Jul 8, 2009 at 9:12 PM
Edited Jul 9, 2009 at 8:58 PM

Here is an example:

C# code for TemplateSelector:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Reflection;

namespace GridTester
{
    public class TemplateSelector : DataTemplateSelector
    {
        private DataTemplate _defaultTemplate;
        public DataTemplate DefaultTemplate
        {
            get { return _defaultTemplate; }
            set { _defaultTemplate = value; }
        }

        private DataTemplate _alternateTemplate;
        public DataTemplate AlternateTemplate
        {
            get { return _alternateTemplate; }
            set { _alternateTemplate = value; }
        }

        private string _propertyToEvaluate;
        public string PropertyToEvaluate
        {
            get { return _propertyToEvaluate; }
            set { _propertyToEvaluate = value; }
        }

        private string _propertyValueToAlter;
        public string PropertyValueToAlter
        {
            get { return _propertyValueToAlter; }
            set { _propertyValueToAlter = value; }
        }

        public override DataTemplate SelectTemplate(object item, System.Windows.DependencyObject container)
        {
            DataUnit dataUnit = item as DataUnit;

            if (dataUnit == null) return this.DefaultTemplate;

            //use reflection to retrieve property
            Type type = dataUnit.GetType();
            PropertyInfo property = type.GetProperty(this.PropertyToEvaluate);

            //lets see what template we need to select according to the specified property value
            if (property.GetValue(dataUnit, null).ToString().ToLower() == this.PropertyValueToAlter.ToLower())
            {
                return this.AlternateTemplate;
            }
            else
            {
                return this.DefaultTemplate;
            }

        }
    }

}

C# code for DataUnit:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;

namespace GridTester
{
    public class DataUnit : INotifyPropertyChanged
    {
        private int _id = int.MinValue;
        private string _name = string.Empty;
        private double _price = double.MinValue;
        private DateTime _dateOfBirth = DateTime.MinValue;

        public DataUnit(int id, string name, double price, DateTime dt)
        {
            this._id = id;
            this._name = name;
            this._price = price;
            this._dateOfBirth = dt;
        }

        #region INotifyPropertyChanged Members

        public event PropertyChangedEventHandler PropertyChanged;
        public void OnPropertyChanged(PropertyChangedEventArgs e)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, e);
        }

        #endregion

        public int ID
        {
            get { return this._id; }
            set { this._id = value; OnPropertyChanged(new PropertyChangedEventArgs("ID")); }
        }

        public string Name
        {
            get { return this._name; }
            set { this._name = value; OnPropertyChanged(new PropertyChangedEventArgs("Name")); }
        }

        public double Price
        {
            get { return this._price; }
            set { this._price = value; OnPropertyChanged(new PropertyChangedEventArgs("Price")); }
        }

        public DateTime DateOfBirth
        {
            get { return this._dateOfBirth; }
            set { this._dateOfBirth = value; OnPropertyChanged(new PropertyChangedEventArgs("DateOfBirth")); }
        }
        
    }
}
Markup for Window1.xaml:
<Window x:Class="GridTester.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"    
    xmlns:my="http://schemas.microsoft.com/wpf/2008/toolkit"
    xmlns:local="clr-namespace:GridTester"
    Title="Grid Tester" Height="400" Width="600" WindowStyle="SingleBorderWindow">
    <Window.Resources>
        <DataTemplate x:Key="DefaultTemplate">
            <DataTemplate.Triggers>
                <DataTrigger Binding="{Binding Path=Name}" Value="Andre">
                    <Setter Property="my:DataGrid.Foreground" Value="Yellow"></Setter>
                </DataTrigger>
            </DataTemplate.Triggers>

            <TextBlock>
                <TextBlock.Text>
                    <Binding Path="Name"/>
                </TextBlock.Text>
            </TextBlock>
       
        </DataTemplate>

        <DataTemplate x:Key="AlternateTemplate">
            <DataTemplate.Triggers>
                <DataTrigger Binding="{Binding Path=Name}" Value="Andre">
                    <Setter Property="my:DataGrid.Foreground" Value="Yellow"></Setter>
                </DataTrigger>
            </DataTemplate.Triggers>

            <Button>
                <Button.Content>
                    <TextBox Text="{Binding Path=Name}"></TextBox>
                </Button.Content>
            </Button>

        </DataTemplate>

    </Window.Resources>
    <Grid ShowGridLines="False">
        <Grid.ColumnDefinitions>
            <ColumnDefinition/>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="2*"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition/>
        </Grid.RowDefinitions>
        <my:DataGrid Name="dgrMain" AutoGenerateColumns="False" 
            Grid.ColumnSpan="2" Margin="2,2,2,2" 
            CanUserResizeColumns="True"                     
            AutoGeneratingColumn="dgrMain_AutoGeneratingColumn">

            <!-- HEADER STYLE -->
            <my:DataGrid.ColumnHeaderStyle>
                <Style TargetType="{x:Type my:DataGridColumnHeader}">
                    <Setter Property="Template">
                        <Setter.Value>
                            <ControlTemplate TargetType="{x:Type my:DataGridColumnHeader}">
                                <Grid>
                                    <my:DataGridHeaderBorder SortDirection="{TemplateBinding SortDirection}"
                                     IsHovered="{TemplateBinding IsMouseOver}" IsPressed="{TemplateBinding IsPressed}"
                                     IsClickable="{TemplateBinding CanUserSort}" Background="{TemplateBinding Background}"
                                     BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}"
                                     Padding ="{TemplateBinding Padding}" SeparatorVisibility="Visible"
                                     SeparatorBrush="{TemplateBinding SeparatorBrush}">
                                        <ContentPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
                                            VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
                                            HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}">
                                            <!-- rotate the column header contents by -90 degrees -->
                                        </ContentPresenter>
                                    </my:DataGridHeaderBorder>
                                </Grid>
                            </ControlTemplate>
                        </Setter.Value>
                    </Setter>
                </Style>
            </my:DataGrid.ColumnHeaderStyle>

            <!-- CELL STYLE -->
            <my:DataGrid.CellStyle>
                <Style TargetType="{x:Type my:DataGridCell}">
                    <Setter Property="Template">
                        <Setter.Value>
                            <ControlTemplate TargetType="{x:Type my:DataGridCell}">
                                <Border Background="{TemplateBinding Background}" 
                                          BorderBrush="{TemplateBinding BorderBrush}"  
                                          BorderThickness="{TemplateBinding BorderThickness}" 
                                          SnapsToDevicePixels="True">
                                    <ContentPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}">
                                        <!-- rotate the cell contents by -90 degrees -->
                                        <ContentPresenter.LayoutTransform>
                                            <RotateTransform Angle="360"/>
                                        </ContentPresenter.LayoutTransform>
                                    </ContentPresenter>
                                </Border>
                            </ControlTemplate>
                        </Setter.Value>
                    </Setter>
                </Style>
            </my:DataGrid.CellStyle>

            <my:DataGrid.Columns>
                <my:DataGridTextColumn Header="ID" Binding="{Binding Path=ID}" Width="*"/>
                <my:DataGridHyperlinkColumn  Header="Name" Binding="{Binding Path=Name}" Width="*"/>
                <my:DataGridTextColumn Header="Price" Binding="{Binding Path=Price}" Width="*"/>
                <my:DataGridTemplateColumn Header="Custom" Width="150">
                    <!--CellEditingTemplateSelector=""-->
                    <my:DataGridTemplateColumn.CellTemplateSelector>
                        <local:TemplateSelector 
                         DefaultTemplate="{StaticResource DefaultTemplate}"
                         AlternateTemplate="{StaticResource AlternateTemplate}"
                         PropertyToEvaluate="Name"
                         PropertyValueToAlter="Pedro"
                        >                            
                        </local:TemplateSelector>
                    </my:DataGridTemplateColumn.CellTemplateSelector>
                    
                    <!-- editing template 
                    <my:DataGridTemplateColumn.CellEditingTemplate>
                        <DataTemplate>
                            <my:DatePicker
                                SelectedDate="{Binding Path=DateOfBirth, Mode=TwoWay}" BorderThickness="0"/>
                        </DataTemplate>
                    </my:DataGridTemplateColumn.CellEditingTemplate>
                    
                    standard template 
                    <my:DataGridTemplateColumn.CellTemplate>
                        <DataTemplate>
                            <TextBlock>
                                <TextBlock.Text>
                                    <Binding Path="DateOfBirth"  ConverterCulture="en-US" StringFormat="{}{0:d}"/>
                                </TextBlock.Text>
                            </TextBlock>
                        </DataTemplate>
                    </my:DataGridTemplateColumn.CellTemplate>-->

                </my:DataGridTemplateColumn>
            </my:DataGrid.Columns>

        </my:DataGrid>
        
        <GridSplitter Grid.Row="1" Grid.ColumnSpan="2" HorizontalAlignment="Stretch"  VerticalAlignment="Center" Height="10"></GridSplitter>
        <Grid Grid.Row="2" Grid.ColumnSpan="2">
            <Button Height="30" Width="100" Click="Button_Click">Hit Me</Button>
        </Grid>
    </Grid>
</Window>

C# code-behind for Window1.xaml.cs: 
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace GridTester
{
    /// <summary>
    /// Interaction logic for Window1.xaml
    /// </summary>
    public partial class Window1 : Window
    {
        public Window1()
        {
            InitializeComponent();
        }

        /// <summary>
        /// Creates fake data (list)
        /// </summary>  
        /// <remarks>Use ObservableCollection if need to track items in the collection. DataUnit is already implementing INotifyPropertyChanged</remarks>
        private List<DataUnit> GetSampleData()
        {
            DataUnit item = null;
            List<DataUnit> list = new List<DataUnit>();
            
            //populate list
            item = new DataUnit(23, "Andre", 23.5, DateTime.Now.AddDays(-3));
            list.Add(item);
            item = new DataUnit(45, "Jack", 52.5, DateTime.Now.AddDays(-2));
            list.Add(item);
            item = new DataUnit(11, "Pedro", 250.45, DateTime.Now.AddDays(-1));
            list.Add(item);

            return list;
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            this.dgrMain.ItemsSource = GetSampleData();
        }

        private void dgrMain_AutoGeneratingColumn(object sender, Microsoft.Windows.Controls.DataGridAutoGeneratingColumnEventArgs e)
        {
            int x = 1;
        }

       
    }
}

 

 

Dec 28, 2010 at 10:37 PM

One problem/question: The project runs fine, however, if at runtime I change Pedro’s name to Alejandro (by editing Pedro’s name in the “Name” column”) the control remains a button and does not change to a TextBlock. Put otherwise, TemplateSelector.SelectTemplate() is not being called when data is edited.

 

You can, however, force the change by sorting on a column after changing Pedro’s name, and then suddenly the Button goes away and is replaced by a TextBlock, as we would expect. But, why can’t we have the DataTemplate switched as soon as Pedro’s name is changed?

 

Jerimiah