This project has moved. For the latest updates, please go here.

Applying Styles to each column in Grid Dynamically

Jun 13, 2014 at 3:58 AM
Edited Jun 13, 2014 at 4:32 AM
Hello I am using <Xcdg:Datagridcontrol> in my project. My customer requested to customize the styles(like foreground background, font family, font size, font stretch,font style) of each column in grid dynamically. I have used below logic to update the fore ground deponds on the cell value but it works for single column. If i apply the same logic for each column it is not working properly. It takes style which is applied to last column to all other columns.

This logic is only working for Foreground.Its throwing Null exception for font weight,font stretch,font style,font size when binding the values

XAML:
 <xcdg:DataGridControl.Resources>                  
                    <DataTemplate x:Key="nameDataTemplate">
                        <TextBlock Text="{Binding RelativeSource={RelativeSource AncestorType=xcdg:DataRow},Path=DataContext.name}" Foreground="{Binding RelativeSource={RelativeSource AncestorType=xcdg:DataRow},Path=DataContext.Foreground}" FontSize="{Binding RelativeSource={RelativeSource AncestorType=xcdg:DataRow},Path=DataContext.FontSize}" FontStyle="{Binding RelativeSource={RelativeSource AncestorType=xcdg:DataRow},Path=DataContext.FontStyle}" FontFamily="{Binding RelativeSource={RelativeSource AncestorType=xcdg:DataRow},Path=DataContext.FontFamily}" FontStretch="{Binding RelativeSource={RelativeSource AncestorType=xcdg:DataRow},Path=DataContext.FontStretch}"/>
                    </DataTemplate>
                    <DataTemplate x:Key="test1Template">
                        <TextBlock Text="{Binding RelativeSource={RelativeSource AncestorType=xcdg:DataRow},Path=DataContext.test1}" Foreground="{Binding RelativeSource={RelativeSource AncestorType=xcdg:DataRow},Path=DataContext.CardForeground}" FontSize="{Binding RelativeSource={RelativeSource AncestorType=xcdg:DataRow},Path=DataContext.FontSize}" FontStyle="{Binding RelativeSource={RelativeSource AncestorType=xcdg:DataRow},Path=DataContext.FontStyle}" FontFamily="{Binding RelativeSource={RelativeSource AncestorType=xcdg:DataRow},Path=DataContext.FontFamily}" FontStretch="{Binding RelativeSource={RelativeSource AncestorType=xcdg:DataRow},Path=DataContext.FontStretch}"/>
                    </DataTemplate>               
                </xcdg:DataGridControl.Resources>
                <xcdg:DataGridControl.Columns>                    
                    <xcdg:Column Title="id" FieldName="id">                        
                    </xcdg:Column>                      
                    
                    <xcdg:Column Title="name"
                                 FieldName="name"
                                 IsMainColumn="True"  CellContentTemplate="{StaticResource nameTemplate}"/>
                    <xcdg:Column Title="c" FieldName="c" CellContentTemplate="{StaticResource test1DataTemplate}"/>
                    <xcdg:Column Title="tes1" FieldName="tes1" />
                    <xcdg:Column Title="test2" FieldName="test2"/>
                    <xcdg:Column Title="test3" FieldName="test3">
                    </xcdg:Column>                  
                </xcdg:DataGridControl.Columns>
__Code Behind:
__
 foreach (CardStyle _cardstyleelement in db.CardStyles.Where(x => x.Id == grouping.Id))
                        {
                            foreach (var value in Results)
                            {
                                if (value.CardName == _cardstyleelement.CardName)
                                {
                                    value.CardName = _cardstyleelement.CardName;
                                    value.FontFamily = new FontFamily(_cardstyleelement.FontFamily);
                                    value.FontSize = _cardstyleelement.FontSize;
                                    string[] _SplitFontStye = _cardstyleelement.FontStyle.Split('-');
                                    FontWeight fontWt = new FontWeight();
                                    switch (_SplitFontStye[1])
                                    {
                                        case "Bold":
                                            fontWt = FontWeights.Bold;
                                            break;
                                        case "ExtraBold":
                                            fontWt = FontWeights.ExtraBold;
                                            break;
                                        case "Normal":
                                            fontWt = FontWeights.Normal;
                                            break;
                                        case "Light":
                                            fontWt = FontWeights.Light;
                                            break;
                                        default:
                                            fontWt = FontWeights.Normal;
                                            break;
                                    }
                                    value.FontWeight = fontWt;
                                    FontStretch fontStretch = new FontStretch();
                                    switch (_SplitFontStye[2])
                                    {
                                        case "Bold":
                                            fontStretch = FontStretches.Condensed;
                                            break;
                                        case "ExtraBold":
                                            fontStretch = FontStretches.UltraCondensed;
                                            break;
                                        case "Normal":
                                            fontStretch = FontStretches.Normal;
                                            break;
                                        case "Light":
                                            fontStretch = FontStretches.Expanded;
                                            break;
                                        default:
                                            fontStretch = FontStretches.ExtraExpanded;
                                            break;
                                    }
                                    value.FontStretch = fontStretch;

                                    FontStyle fontstyle = new FontStyle();
                                    switch (_SplitFontStye[0])
                                    {
                                        case "Normal":
                                            fontstyle = FontStyles.Normal;
                                            break;
                                        case "Italic":
                                            fontstyle = FontStyles.Italic;
                                            break;
                                        case "Obilique":
                                            fontstyle = FontStyles.Oblique;
                                            break;

                                        default:
                                            fontStretch = FontStretches.Normal;
                                            break;
                                    }
                                    value.FontStyle = fontstyle;

                                    value.CardForeground = new BrushConverter().ConvertFromString(_cardstyleelement.FontColor) as SolidColorBrush;
                                }
                            }
                        }
Am using DatagridCollection view as item source not a Datatable.
   this.Results = new ObservableCollection<TestCaseResult>();
   collectionView = new DataGridCollectionView(this.Results);
   ResultGrid.ItemsSource = collectionView;
Please help me to resolve the same. I am struck with this nearly 2 weeks.
Developer
Jun 19, 2014 at 8:37 PM
Hi,

In your example, each row contains a Foreground. When you set the Column[1].CellContentTemplate, to a DataTemplate binding on row.Foreground, it will work. But if you try to use the same CellContentTemplate on Column[2], the row is the same, with the same Foreground, so will be similar the Column[1]'s foreground. And this is the same for all Font properties.

An idea is to reset the Column's CellContentTemplate. So you pre-define some DataTemplates in the Resources (TextBlock with Foreground, FontFamily...). When you need to change the Display of items in Column[1], you just get the wanted DataTemplate from the resource (or build it in code-behind) and apply it on column[1] :
//Get the DataTemplate in Resources or create one in code-behind
DataTemplate newTemplate = this.ResultGrid.Resources[ "test2Template" ] as DataTemplate;
if( newTemplate != null )
{
   this.ResultGrid.Columns[ "FirstName" ].CellContentTemplate = newTemplate;
}
Another option is to define a ViewModel with Foreground and Font properties...as Dependency properties :
public class MyViewModel : DependencyObject
  {
    public Brush Foreground
    {
      get
      {
        return ( Brush )GetValue( ForegroundProperty );
      }
      set
      {
        SetValue( ForegroundProperty, value );
      }
    }

    public static readonly DependencyProperty ForegroundProperty = DependencyProperty.Register( "Foreground", typeof( Brush ), typeof( MyViewModel ),
                                                                  new UIPropertyMetadata(
                                                                  new SolidColorBrush(Colors.Black) ) );
  }
Then in the resource, define as many of these as you have columns ;
<local:MyViewModel x:Key="View1"  Foreground="Green"/> 
The column's CellContentTemplate can then bind to the corresponding ViewModel's properties in its DataTemplate :
<DataTemplate x:Key="test1Template">
                  <TextBlock Text="{Binding}"
                             Foreground="{Binding Foreground, Source={StaticResource View1}}"/>
               </DataTemplate>
When you need to change dynamically those properties, you just change them on the ViewModels that you retrieve from Resources ;
MyViewModel viewModel2 = this.Resources[ "View2" ] as MyViewModel;
      if( viewModel2 != null )
      {
        viewModel2.Foreground = new SolidColorBrush( Colors.Pink );
      }
Jun 20, 2014 at 6:34 AM
Edited Jun 20, 2014 at 6:34 AM
Thank you for your answer. Could you please upload XAML Code? I have tried the above one, Style is not applied properly to Column.
Developer
Jun 20, 2014 at 12:16 PM
Hi,

Here's the sample for option 2), using a ViewModel by column.
<Window x:Class="WpfApplication75.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:xcdg="http://schemas.xceed.com/wpf/xaml/datagrid"
        xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
        xmlns:local="clr-namespace:WpfApplication75"
        Title="MainWindow" Height="350" Width="1000">
   <Window.Resources>
      <local:MyViewModel x:Key="View1"
                         Foreground="Green"/>
      <local:MyViewModel x:Key="View2"
                         Foreground="Green" />
   </Window.Resources>
    
   <Grid>
      <StackPanel>
         <xcdg:DataGridControl x:Name="ResultGrid"
                               AutoCreateColumns="False">
            <xcdg:DataGridControl.Resources>
               <DataTemplate x:Key="test1Template">
                  <TextBlock Text="{Binding}"
                             Foreground="{Binding Foreground, Source={StaticResource View1}}"/>
               </DataTemplate>
               <DataTemplate x:Key="test2Template">
                  <TextBlock Text="{Binding}"
                             Foreground="{Binding Foreground, Source={StaticResource View2}}" />
               </DataTemplate>               
            </xcdg:DataGridControl.Resources>

            <xcdg:DataGridControl.Columns>
               <xcdg:Column Title="id"
                            FieldName="ID">
               </xcdg:Column>

               <xcdg:Column Title="name"
                            FieldName="FirstName"
                            IsMainColumn="True"
                            CellContentTemplate="{StaticResource test1Template}" />
               <xcdg:Column Title="last"
                            FieldName="LastName"
                            CellContentTemplate="{StaticResource test2Template}" />
            </xcdg:DataGridControl.Columns>

         </xcdg:DataGridControl>
         <Button Content="Click Me"
                 Click="Button_Click" />
      </StackPanel>
   </Grid>
</Window>

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

      this.Results = new ObservableCollection<TestCaseResult>() 
      {
        new TestCaseResult() { ID = 1, FirstName = "AA", LastName = "AAAA" },
        new TestCaseResult() { ID = 1, FirstName = "BB", LastName = "BBBB" },
        new TestCaseResult() { ID = 1, FirstName = "CC", LastName = "CCCC" },
        new TestCaseResult() { ID = 1, FirstName = "DD", LastName = "DDDD"},
        new TestCaseResult() { ID = 1, FirstName = "EE", LastName = "EEEE" },
        new TestCaseResult() { ID = 1, FirstName = "FF", LastName = "FFFF" },
      };
      DataGridCollectionView collectionView = new DataGridCollectionView( this.Results );
      ResultGrid.ItemsSource = collectionView;
    }

    public ObservableCollection<TestCaseResult> Results
    {
      get;
      set;
    }

    private void Button_Click( object sender, RoutedEventArgs e )
    {
      MyViewModel viewModel = this.Resources[ "View1" ] as MyViewModel;
      if( viewModel != null )
      {
        viewModel.Foreground = new SolidColorBrush(Colors.Yellow);
      }
      MyViewModel viewModel2 = this.Resources[ "View2" ] as MyViewModel;
      if( viewModel2 != null )
      {
        viewModel2.Foreground = new SolidColorBrush( Colors.Pink );
      }
    }
  }

  public class TestCaseResult
  {
    public int ID
    {
      get;
      set;
    }

    public string FirstName
    {
      get;
      set;
    }

    public string LastName
    {
      get;
      set;
    }
  }

  public class MyViewModel : DependencyObject
  {
    public Brush Foreground
    {
      get
      {
        return ( Brush )GetValue( ForegroundProperty );
      }
      set
      {
        SetValue( ForegroundProperty, value );
      }
    }

    public static readonly DependencyProperty ForegroundProperty = DependencyProperty.Register( "Foreground", typeof( Brush ), typeof( MyViewModel ),
                                                                  new UIPropertyMetadata(
                                                                  new SolidColorBrush(Colors.Black) ) );
  }
}
Jun 23, 2014 at 10:40 AM
Edited Jun 23, 2014 at 10:46 AM
thank you Mr.Boucher. Its working for me.

When apply the Foreground based on the cell values, the style is not applying properly.
For example: I want to apply the Red Color only the cell contains Value "CC" in Column Firstname.

But , It is not working When I apply same logic to FontFamily , FontSize , FontStretch, FontWeight , FontStyle. is there any other way to apply those styles for each column? Please help me.
Developer
Jun 23, 2014 at 12:49 PM
Hi,

1) If you want to apply a red foreground color only to the cell with Value equals "CC" in Column Firstname, you can use a DataTrigger in the "test1Template" :
<DataTemplate x:Key="test1Template">
                  <TextBlock x:Name="_textBlock"
                             Text="{Binding}"
                             Foreground="{Binding Foreground, Source={StaticResource View1}}"/>
                  <DataTemplate.Triggers>
                     <DataTrigger Binding="{Binding Path=.}"
                                  Value="CC">
                        <Setter Property="Foreground"
                                Value="Red" 
                                TargetName="_textBlock"/>
                     </DataTrigger>
                  </DataTemplate.Triggers>
</DataTemplate>
If you want to apply it to all the cells that contains "CC", you can use a converter in the binding of the DataTrigger to get them.

2) I tried it with FontSize, and it worked :
<DataTemplate x:Key="test1Template">
                  <TextBlock Text="{Binding}"
                             Foreground="{Binding Foreground, Source={StaticResource View1}}"
                             FontSize="{Binding FontSize, Source={StaticResource View1}}"/>
</DataTemplate>

 public class MyViewModel : DependencyObject
  {
    //.....

    public double FontSize
    {
      get
      {
        return ( double )GetValue( FontSizeProperty );
      }
      set
      {
        SetValue( FontSizeProperty, value );
      }
    }

    public static readonly DependencyProperty FontSizeProperty = DependencyProperty.Register( "FontSize", typeof( double ), typeof( MyViewModel ),
                                                                  new UIPropertyMetadata( 11d ) );
  }

private void Button_Click( object sender, RoutedEventArgs e )
    {
      MyViewModel viewModel = this.Resources[ "View1" ] as MyViewModel;
      if( viewModel != null )
      {
        viewModel.Foreground = new SolidColorBrush(Colors.Yellow);
        viewModel.FontSize = 20d;
      }
    }
Marked as answer by TamilKarthick on 6/24/2014 at 2:31 AM
Jun 24, 2014 at 6:52 AM
Thank you very much Mr.boucher. Its working for me.
Jun 24, 2014 at 9:13 AM
Edited Jun 24, 2014 at 9:33 AM
Hello Mr.Boucher,

I have tried with Data Trigger. By using data trigger I can apply style to single value like "CC". But my requirement is that the style to applied based on the user value.
For example if the user select CC it has to apply the styles to Column contains "CC" Later user may change the value to "BB" then the styles should be applied to Column that contains the value "BB".

I have tried creating the Data trigger in code behind but Trigger is not working.

Is there any possibility to change styles based on the cell value which is given by user with the logic(2) above?
Developer
Jun 25, 2014 at 2:17 PM
Hi,

So this is a different behavior. From what I understand, you want to be able to change a value, let's call it "X". When "X" == "AA", you want the column in the grid containing "AA" to have a specific style. When "X" == "BB", you want to clear all the column's style and set a specific style on the column that contains "BB".

In that case, in XAML, don't set any CellContentTemplate for columns. When the value "X" changes, in code-behind, scan the grid to find the DataCell containing the same value as "X". Then set it's parent Column's CellContentTemplate to the specific DataTemplate you want.

The following code snippet will detect "AA" in the grid and set the corresponding column's CellContentTemplate.
private void Button_Click( object sender, RoutedEventArgs e )
{
      foreach( Column col in ResultGrid.Columns )
      {
        col.CellContentTemplate = null;
      }
      
      foreach( TestCaseResult item in ResultGrid.Items )
      {
        DataRow row = ResultGrid.GetContainerFromItem( item ) as DataRow;
        if( row != null )
        {
          foreach( DataCell c in row.Cells )
          {
            if( c != null )
            {
              if( (c.Content is string) && (string)c.Content == "AA" )
              {
                DataTemplate template = ResultGrid.Resources[ "test1Template" ] as DataTemplate;
                if( template != null )
                {
                  c.ParentColumn.CellContentTemplate = template;
                }
              }
            }
          }
        }
      }
}
Jun 26, 2014 at 11:38 AM
Hello Mr.Boucher,

Thank you for your response.

Not for the Column, have to apply the style to cell which contains the value X in a particular column based on user value.

Thanks and regards