WPF datagrid: converter and StringFormat - c#

I have a standard (WPF toolkit) data grid. Some of the columns (which are explicitly defined) have to be shown as percentages. Some columns have to be shown in red if the values are below 0. (The two sets of columns are not the same). I tried to implement these requirements using a StringFormat and Style, respectively. My XAML:
<Window xmlns:local="clr-namespace:myNamespace"
xmlns:tk="clr-namespace:Microsoft.Windows.Controls;assembly=WPFToolkit">
<Window.Resources>
<local:ValueConverter x:Key="valueToForeground" />
<Style TargetType="{x:Type tk:DataGridCell}">
<Setter Property="Foreground"
Value="{Binding RelativeSource={RelativeSource Self}, Path=Content.Text, Converter={StaticResource valueToForeground}}" />
</Style>
</Window.Resources>
<Grid>
<tk:DataGrid AutoGenerateColumns="False"
ItemsSource="{Binding Path=myClass/myProperty}">
<tk:DataGrid.Columns>
<tk:DataGridTextColumn Header="A"
Binding="{Binding colA}" />
<tk:DataGridTextColumn Header="B"
Binding="{Binding colB, StringFormat=\{0:P\}}" />
<tk:DataGridTextColumn Header="C"
Binding="{Binding colC, StringFormat=\{0:P\}}" />
<tk:DataGridTextColumn Header="D"
Binding="{Binding colD, StringFormat=\{0:P\}}" />
</tk:DataGrid.Columns>
</tk:DataGrid>
</Grid>
</Window>
And the relevant converter:
namespace myNamespace
{
public class ValueConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
SolidColorBrush brush = new SolidColorBrush(Colors.Black);
Double doubleValue = 0.0;
if (value != null)
{
if (Double.TryParse(value.ToString(), out doubleValue))
{
if (doubleValue < 0)
brush = new SolidColorBrush(Colors.Red);
}
}
return brush;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
}
I think it's all pretty standard, but the problem is that the converter gets the Text value after it's gone through the StringFormat, and at that point it's difficult to parse it correctly (since in reality, not all columns have the same format). If I take out the StringFormats, the converter works fine and the text shows up in red. Am I missing something obvious? Is there an easy way to work around this? The only thing that I can think of right now is moving the formatting into a different converter, and I'm not convinced that would work.

We had a similar situation where we needed a different Path Property for the Binding but otherwise a similar CellStyle for each DataGridColumn. We solved this with a custom MarkupExtension. In your case it would look like this
<tk:DataGrid AutoGenerateColumns="False"
ItemsSource="{Binding MyItems}">
<tk:DataGrid.Columns>
<tk:DataGridTextColumn Header="A"
Binding="{Binding colA}" />
<tk:DataGridTextColumn Header="B"
Binding="{Binding colB, StringFormat=\{0:P\}}"
CellStyle="{markup:ForegroundCellStyle PropertyName=colB}"/>
<tk:DataGridTextColumn Header="C"
Binding="{Binding colC, StringFormat=\{0:P\}}"
CellStyle="{markup:ForegroundCellStyle PropertyName=colC}"/>
<tk:DataGridTextColumn Header="D"
Binding="{Binding colD, StringFormat=\{0:P\}}"
CellStyle="{markup:ForegroundCellStyle PropertyName=colD}"/>
</tk:DataGrid.Columns>
</tk:DataGrid>
and then ForegroundCellStyleExtension creates the Style for DataGridCell depending on PropertyName
ForegroundCellStyleExtension
public class ForegroundCellStyleExtension : MarkupExtension
{
public ForegroundCellStyleExtension() { }
public ForegroundCellStyleExtension(string propertyName)
{
PropertyName = propertyName;
}
public string PropertyName
{
get;
set;
}
public override object ProvideValue(IServiceProvider serviceProvider)
{
IProvideValueTarget service = (IProvideValueTarget)serviceProvider.GetService(typeof(IProvideValueTarget));
DependencyObject targetObject = service.TargetObject as DependencyObject;
if (targetObject == null)
{
return null;
}
Binding foregroundBinding = new Binding
{
Path = new PropertyPath(PropertyName),
Converter = new ValueConverter()
};
Style foregroundCellStyle = new Style(typeof(DataGridCell));
foregroundCellStyle.Setters.Add(new Setter(DataGridCell.ForegroundProperty, foregroundBinding));
return foregroundCellStyle;
}
}
Also, if you have some other Setters etc. that you would like to use then they can be included by another parameter to the MarkupExtension.
<Window.Resources>
<Style x:Key="dataGridCellStyle" TargetType="{x:Type tk:DataGridCell}">
<Setter Property="Background" Value="Blue"/>
</Style>
</Window.Resources>
<!-- ... -->
<tk:DataGridTextColumn Header="B"
Binding="{Binding colB, StringFormat=\{0:P\}}"
CellStyle="{markup:ForegroundCellStyle colB, {StaticResource dataGridCellStyle}}"/>
And ForegroundCellStyleExtension would then use the second parameter as BasedOn for the DataGridCell Style
ForegroundCellStyleExtension with BasedOn
public class ForegroundCellStyleExtension : MarkupExtension
{
public ForegroundCellStyleExtension() { }
public ForegroundCellStyleExtension(string propertyName, Style basedOnCellStyle)
{
PropertyName = propertyName;
BasedOnCellStyle = basedOnCellStyle;
}
public string PropertyName
{
get;
set;
}
public Style BasedOnCellStyle
{
get;
set;
}
public override object ProvideValue(IServiceProvider serviceProvider)
{
IProvideValueTarget service = (IProvideValueTarget)serviceProvider.GetService(typeof(IProvideValueTarget));
DependencyObject targetObject = service.TargetObject as DependencyObject;
if (targetObject == null)
{
return null;
}
Binding foregroundBinding = new Binding
{
Path = new PropertyPath(PropertyName),
Converter = new ValueConverter()
};
Style foregroundCellStyle = new Style(typeof(DataGridCell), BasedOnCellStyle);
foregroundCellStyle.Setters.Add(new Setter(DataGridCell.ForegroundProperty, foregroundBinding));
return foregroundCellStyle;
}
}

Specify a cell style for each column as follows:
<DataGridTextColumn Header="ColA" Binding="{Binding colA, StringFormat=\{0:P\}}">
<DataGridTextColumn.CellStyle>
<Style TargetType="DataGridCell">
<Setter Property="Foreground"
Value="{Binding colA, Converter={StaticResource valueToForeground}}" />
</Style>
</DataGridTextColumn.CellStyle>
</DataGridTextColumn>
<DataGridTextColumn Header="ColB" Binding="{Binding colB, StringFormat=\{0:P\}}">
<DataGridTextColumn.CellStyle>
<Style TargetType="DataGridCell">
<Setter Property="Foreground"
Value="{Binding colB, Converter={StaticResource valueToForeground}}" />
</Style>
</DataGridTextColumn.CellStyle>
</DataGridTextColumn>
...
and modify your converter
public class ValueConverter : IValueConverter
{
public object Convert(
object value, Type targetType, object parameter, CultureInfo culture)
{
return ((double) value < 0) ? Brushes.Red : Brushes.Black;
}
public object ConvertBack(
object value, Type targetType, object parameter, CultureInfo culture)
{
return Binding.DoNothing;
}
}

The simplest way I figured out is to bind your full item instead of the item/content.text only to your converter. Then you will be able to do what you wanted to do with your cells with need to worry about the item and parameter values.
In your Cell Style:
<Setter Property="Foreground"
Value="{Binding Converter={StaticResource valueToForeground}}" />
and in your Converter code:
public object Convert(object value, Type targetType,
object parameter, System.Globalization.CultureInfo culture)
{
SolidColorBrush brush = new SolidColorBrush(Colors.Black);
Double doubleValue = 0.0;
if (value != null)
{
mydatatype data = value as mydatatype;
//your logic goes here and also can play here with your dataitem.
if (Double.TryParse(data.CollD.ToString(), out doubleValue))
{
if (doubleValue < 0)
brush = new SolidColorBrush(Colors.Red);
}
}
return brush;
}

Related

XAML colour binding converter from bool not working

I'm trying to change the text colour in a datagrid depending on a bool in my model using a converter but I get the following error.
System.Windows.Data Error: 2 : Cannot find governing FrameworkElement
or FrameworkContentElement for target element.
BindingExpression:Path=DiameterCustom; DataItem=null; target element
is 'DataGridTextColumn' (HashCode=7886611); target property is
'Foreground' (type 'Brush')
Does anyone know why this is?
My xaml is as follows:
<UserControl.Resources>
<conv:UnitConverter x:Key="UnitConverter"></conv:UnitConverter>
<conv:CustomColourConverter x:Key="CustomColourConverter"></conv:CustomColourConverter>
</UserControl.Resources>
<DataGridTextColumn
Header="Diameter
(mm)"
Binding="{Binding Diameter, Mode=TwoWay, StringFormat={}{0:n0}, Converter={StaticResource UnitConverter}, ConverterParameter=1000}"
Foreground="{Binding DiameterCustom, Converter={StaticResource CustomColourConverter}}"/>
This is my converter:
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
bool v = (bool)value;
if (v == true)
{
//return return System.Windows.Media.Brushes.Red;
return new SolidColorBrush(Colors.Red);
}
//return return System.Windows.Media.Brushes.Blue;
return new SolidColorBrush(Colors.Blue);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
and just for completeness, here is my model property:
private bool diameterCustom;
public bool DiameterCustom
{
get { return diameterCustom; }
set { SetAndNotify(ref this.diameterCustom, value); }
}
Note, the data binding for Diameter and the unit converter work fine.
To change the text colour cell by cell the following solution worked:
<DataGridTextColumn
Header="Diameter
(mm)"
Binding="{Binding Diameter, Mode=TwoWay, StringFormat={}{0:n0}, Converter={StaticResource UnitConverter}, ConverterParameter=1000}">
<DataGridTextColumn.ElementStyle>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="Foreground" Value="{Binding DiameterCustom, Converter={StaticResource CustomColourConverter}}"/>
</Style>
</DataGridTextColumn.ElementStyle>
</DataGridTextColumn>

WPF DataGrid Highlighting all cells in a column that match the selected cell

I fill an ObservableCollection<> (also tried a List<>) with a custom Class. I've bound the collection to the DataGrid and defined the columns. Now I want to select a cell and have the other cells (in the same column although data in other columns won't ever match) that have the same string in it to be highlighted.
<Window.Resources>
<local:CellHighlighterConverter x:Key="cellHighlighterConverter" />
<CollectionViewSource x:Key="ScanCollectionViewSource" CollectionViewType="ListCollectionView" />
<Style x:Key="CenterCell" TargetType="TextBlock">
<Setter Property="TextBlock.TextAlignment" Value="Center" />
</Style>
<Style x:Key="CellPad" TargetType="TextBlock">
<Setter Property="Margin" Value="15,0,15,0" />
</Style>
<Style x:Key="CellHighlighterStyle" TargetType="TextBlock" >
<Setter Property="Background" Value="{Binding IsMatching, NotifyOnSourceUpdated=True, Converter={StaticResource cellHighlighterConverter}}" />
<Setter Property="TextBlock.TextAlignment" Value="Center" />
</Style>
</Window.Resources>
<Grid HorizontalAlignment="Center" >
<DataGrid x:Name="scans" DataContext="{StaticResource ScanCollectionViewSource}" ItemsSource="{Binding}" AutoGenerateColumns="False" FontFamily="Lucida Console" Margin="10" MouseUp="scans_MouseUp" >
<DataGrid.Columns>
<DataGridTextColumn Header="Device Name" Binding="{Binding Hostname}" Width="125" />
<DataGridTextColumn Header="Scan Date" Binding="{Binding ScanDate}" Width="75" ElementStyle="{StaticResource CenterCell}" />
<DataGridTextColumn Header="GUID" Binding="{Binding GUID}" Width="300" ElementStyle="{StaticResource CenterCell}" />
<DataGridTextColumn Header="MAC" Binding="{Binding MAC}" Width="105" ElementStyle="{StaticResource CellHighlighterStyle}" />
</DataGrid.Columns>
</DataGrid>
</Grid>
Code behind
private ObservableCollection<Scan> ReadFiles()
{
scanList = new List<Scan>();
...
foreach(string file in files)
{
Scan newScan = new Scan();
...fill newScan with data
scanList.Add(newScan);
}
scanList = scanList.OrderBy(x => x.Hostname).ThenByDescending(x => x.ScanDate).ToList();
scanCollection = new ObservableCollection<Scan>(scanList);
return scanCollection;
}
Ways I've tried to accomplish this. Added a property to the class, then on mouse up, set that property if data matches (this works), then use a converter to set the background based on that data. Either the grid isn't refreshing or something else is wrong. Am I on the right track or is there a better way?
private void scans_MouseUp(object sender, MouseButtonEventArgs e)
{
string selMAC = ((Scan)((DataGrid)sender).SelectedValue).MAC;
foreach (Scan scan in scanList)//((DataGrid)sender).Items.OfType<Scan>().ToList() )
{
// compare values
if (scan.MAC == selMAC)
{
scan.IsMatch = true;
} else
{
scan.IsMatch = false;
}
//scans.Items.Refresh();
}
}
}
public class CellHighlighterConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if( (Boolean)value )
{
return new SolidColorBrush(Colors.Green);
}
return SystemColors.AppWorkspaceColor;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
public class Scan
{
public Scan()
{
this.IsMatch = false;
}
public string Hostname { get; set; }
public string ScanDate { get; set; }
public string GUID { get; set; }
public string MAC { get; set; }
public bool IsMatch { get; set; }
}
Scan should implement INotifyPropertyChanged interface and raise notificattion for IsMatch property:
private bool _IsMatch;
public bool IsMatch
{
get { return _IsMatch; }
set
{
if (_IsMatch == value) return;
_IsMatch = value;
OnPropertyChanged("IsMatch");
}
}
without such notification Convert method isn't triggered even if you change IsMatch.
other things I would fix:
Convert method should return Brush, not Color
{
return (bool)value ? Brushes.Green : SystemColors.AppWorkspaceBrush;
}
use SelectionChanged event instead of MouseUp. MouseUp can happen on the same cell multiple times, no need to search mathes each time

How can I change the value sent to the converter?

I have a project, which contain converter. After completion of the ContinentSelectionChanged method call, is called automatically the converter. How can I change the value that converter gets?
XAML
<DataGridComboBoxColumn x:Name="DGKontynent" Header="Kontynent" Width="120" CanUserSort="False" SelectedItemBinding="{Binding Kontynent}" >
<DataGridComboBoxColumn.EditingElementStyle>
<Style TargetType="ComboBox">
<EventSetter Event="SelectionChanged" Handler="ContinentSelectionChanged" />
</Style>
</DataGridComboBoxColumn.EditingElementStyle>
</DataGridComboBoxColumn>
<DataGridComboBoxColumn x:Name="DGKraj" Header="Kraj" Width="120" CanUserSort="False" SelectedValueBinding="{Binding Kraj}" >
<DataGridComboBoxColumn.ElementStyle>
<Style TargetType="ComboBox">
<Setter Property="ItemsSource" Value="{Binding Path=Kontynent,Converter={StaticResource CountryConverter}}"/>
<Setter Property="SelectedValue" Value="{Binding Path=Kraj}"/>
</Style>
</DataGridComboBoxColumn.ElementStyle>
<DataGridComboBoxColumn.EditingElementStyle>
<Style TargetType="ComboBox">
<Setter Property="ItemsSource" Value="{Binding Path=Kontynent,Converter={StaticResource CountryConverter}}" />
<Setter Property="SelectedValue" Value="{Binding Path=Kraj}"/>
</Style>
</DataGridComboBoxColumn.EditingElementStyle>
</DataGridComboBoxColumn>
C# Converter
public class CountrySingleConverter : IValueConverter
{
Funkcje FK = new Funkcje();
List<string> kraje = new List<string>();
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value != null && !string.IsNullOrWhiteSpace(value.ToString()))
{
kraje = FK.kraj_wybierz(value.ToString(), Edycja.dgKraj);
return kraje;
}
return null;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
C#
List<string> l3 = new List<string>();
static string kontynent_tmp;
private void ContinentSelectionChanged(object sender, SelectionChangedEventArgs e)
{
Xceed.Wpf.Toolkit.MessageBox.Show(DGKraj.SelectedValueBinding.);
string kontynent = null;
l4.Clear();
var row = ZdarzeniaDataGrid.GetSelectedRow();
DataGridCell cell = ZdarzeniaDataGrid.GetCell(row, 5);
if (cell != null)
{
ComboBox combo = GetVisualChild<ComboBox>(cell);
if (combo != null)
{
if (combo.SelectedValue != null)
{
kontynent = combo.SelectedValue.ToString();
kontynent_tmp = kontynent;
}
else
{
kontynent = kontynent_tmp;
}
}
}
l3 = FK.kraj_wybierz(kontynent, DGKraj);
DataGridCell cell3 = ZdarzeniaDataGrid.GetCell(row, 6);
if (cell3 != null)
{
ComboBox combo3 = GetVisualChild<ComboBox>(cell3);
if (combo3 != null)
{
combo3.ItemsSource = l3;
}
}
}
The value sent to the converter is always what you bound to in {Binding}. If you want to bind to something else you must specify that.
e.g.
{Binding Kontynent.Name}

Change style in ModelView (MVVM + WPF)

I have an application developed in WPF using the MVVM pattern (MVVM Light Toolkit).
So far, I had no problems, until it is time to change at runtime the style associated with some of my controls, a set of MenuItems. (They can have up to three different styles).
If I was not working with MVVM I could solve it using the command:
MenuElement_Left4.Style = (Style)FindResource("MenuButtonTabsLeft");
But because I want do it completely in MVVM, I've done these tests to achieve that:
1) Try to change the style with a binding (this has not worked):
<MenuItem x:Name="MenuElement_Left4" Header="Test" Style="{Binding SelectedStyle}">
And in the ViewModel:
public string SelectedStyle
{
get { return this.selectedStyle; }
set { this.selectedStyle = value;
RaisePropertyChanged("SelectedStyle");
}
}
private string selectedStyle;
2) Change the style with DataTrigger (this has not worked too. Raises an exception (Style Trigger to Apply another Style)):
<MenuItem.Style>
<Style TargetType="{x:Type MenuItem}">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=TestStyle}" Value="True">
<Setter Property="Style" Value="{StaticResource MenuButtonTabsLeftArrow}"/>
</DataTrigger>
<DataTrigger Binding="{Binding Path=TestStyle}" Value="False">
<Setter Property="Style" Value="{StaticResource MenuButtonTabsLeft}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</MenuItem.Style>
At the end, I managed to solve it, using a Combox using the following code (I only use the ComboBox to change the style of the MenuItems so it is invisible). Got the idea from (How can I change an elements style at runtime?):
<MenuItem x:Name="MenuElement_Left4" Header="Test" Style="{Binding ElementName=AvailableStyles, Path=SelectedItem.Tag}">
<ComboBox Name="AvailableStyles" SelectedIndex="{Binding AvailableStylesIndex}" Visibility="Collapsed">
<ComboBoxItem Tag="{x:Null}">None</ComboBoxItem>
<ComboBoxItem Tag="{StaticResource MenuButtonTabsLeftArrow}">MenuButtonTabsLeftArrow</ComboBoxItem>
<ComboBoxItem Tag="{StaticResource MenuButtonTabsLeft}">MenuButtonTabsLeft</ComboBoxItem>
</ComboBox>
And in my ViewModel:
public int AvailableStylesIndex
{
get { return this.availableStylesIndex; }
set
{
this.availableStylesIndex = value;
RaisePropertyChanged("AvailableStylesIndex");
}
}
I'd rather use a cleaner way. Any suggestions? A piece of code would be very helpful.
Since you are keeping your styles in you resources, cleaner approch would be to use IMultiValueConverter's with you first approch something like this:
ViewModel
public string SelectedStyle
{
get { return this.selectedStyle; }
set { this.selectedStyle = value;
RaisePropertyChanged("SelectedStyle");
}
}
private string selectedStyle;
Xaml:
<MenuItem.Style>
<MultiBinding Converter="{StaticResource StyleConverter}">
<MultiBinding.Bindings>
<Binding RelativeSource="{RelativeSource Self}"/>
<Binding Path="SelectedStyle"/>
</MultiBinding.Bindings>
</MultiBinding>
</MenuItem.Style/>
In the converter find the style you want and apply it
class StyleConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
FrameworkElement targetElement = values[0] as FrameworkElement;
string styleName = values[1] as string;
if (styleName == null)
return null;
Style newStyle = (Style)targetElement.TryFindResource(styleName);
if (newStyle == null)
newStyle = (Style)targetElement.TryFindResource("MyDefaultStyleName");
return newStyle;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
Taken out from Steven Robbins's answer in this post

WPF - Binding multiple values to a datagrind

UPDATED : Clean subject, and summarize it.
Hi,
I've a datable filled, where each cell is a class like this
class CValue{
public object Value;
public Brush Quality;
private int m_quality;
public override String toString(){
return Value.toString();
}
}
My datagrid is bind on the datable, and it's working well.
But my aim is to switch the background color of the cell depending of the Quality value.
I intend tu use datatemplate but don't know how it's working at all...
<dg:DataGrid Name="DataGridResult" IsReadOnly="True" AutoGenerateColumns="False"
BorderThickness="1" BorderBrush="{DynamicResource clBLACK}"
CanUserReorderColumns="False"
ItemsSource="{Binding Path=Result}">
<dg:DataGrid.Resources>
<Style TargetType="{x:Type dg:DataGridCell}">
<Style.Setters>
<Setter Property="Background" Value="{Binding [1].Quality}"/>
</Style.Setters>
</Style>
</dg:DataGrid.Resources>
<dg:DataGrid.ItemTemplate>
<DataTemplate>
<dg:DataGridCell>
</dg:DataGridCell>
</DataTemplate>
</dg:DataGrid.ItemTemplate>
</dg:DataGrid>
Actually, if the Value of the background's setter is set to "Blue", all cells are blued, so it's fine, but I can't find a way to bind it to my property.
the [1] seems to return the column 1 of the row...
How to set to the cell dynamically ?
'Cause i've got a dynamic number of columns but there all of the CValue Type.
Ok. So for an entire example databinding to the Brush of the model instead of using converters, styles etc. For the following cs -code:
class CValue
{
public string Value { get; set; } // Notice we use properties for binding and not fields
public Brush Quality { get; set; } // Notice we use properties for binding and not fields
private int m_quality;
public override String ToString()
{
return Value.ToString();
}
}
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
// Databind the list
myGrid.ItemsSource = new List<CValue>
{
new CValue
{
Value = "First",
Quality = new SolidColorBrush(Color.FromArgb(255, 0, 255, 255))},
new CValue
{
Value = "Second",
Quality = new SolidColorBrush(Color.FromArgb(255, 255, 0, 255))
},
new CValue
{
Value = "Third",
Quality = new SolidColorBrush(Color.FromArgb(0, 255, 255, 255))
}
};
}
}
You would use a xaml for the rowstyle (notice the TargetType on the style and the AutoGenerateColumns="false") to bind the row-color and the value:
<Controls:DataGrid x:Name="myGrid" AutoGenerateColumns="False">
<Controls:DataGrid.RowStyle>
<Style TargetType="{x:Type Controls:DataGridRow}">
<Setter Property="Background" Value="{Binding Quality}" />
</Style>
</Controls:DataGrid.RowStyle>
<Controls:DataGrid.Columns>
<Controls:DataGridTemplateColumn Header="Value">
<Controls:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Label Content="{Binding Value}" />
</DataTemplate>
</Controls:DataGridTemplateColumn.CellTemplate>
</Controls:DataGridTemplateColumn>
</Controls:DataGrid.Columns>
</Controls:DataGrid>
Hope it helps!
One good way of doing this which keeps the coloring visible in the XAML is to use a style with binding to the quality. We put this style in the some resourcedictionary above the template, like in my case in the DataGrid.Resources.
<Controls:DataGrid>
<Controls:DataGrid.Resources>
<Style TargetType="{x:Type Controls:DataGridCell}">
<Style.Triggers>
<DataTrigger Binding="{Binding Quality}" Value="0">
<Setter Property="Background" Value="Red" />
</DataTrigger>
<DataTrigger Binding="{Binding Quality}" Value="0">
<Setter Property="Background" Value="Blue" />
</DataTrigger>
</Style.Triggers>
</Style>
</Controls:DataGrid.Resources>
<Controls:DataGrid.ItemTemplate>
<DataTemplate>
<Controls:DataGridCell>
</Controls:DataGridCell>
</DataTemplate>
</Controls:DataGrid.ItemTemplate>
</Controls:DataGrid>
Update:
To be able to databind the values or whatever use a converter like this:
[ValueConversion(typeof(int), typeof(SolidColorBrush))]
public class QualityToColorConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
// Cast value
int intValue = (int) value;
if (intValue == 1)
return new SolidColorBrush(Color.FromArgb(255, 255, 255, 255));
return new SolidColorBrush(Color.FromArgb(255, 0, 0, 255));
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException("TwoWay binding not supported!");
}
}
Bind it in the XAML like the follwing:
<Window.Resources>
<WpfApplication1:QualityToColorConverter x:Key="ColorConverter" />
</Window.Resources>
<Controls:DataGridCell Background="{Binding Quality, Converter={StaticResource ColorConverter}}">
</Controls:DataGridCell>
You should use DataTemplateSelector class to perform this logic.
The scenario is described below:
Create the set of DataTemplates;
Derive from DataTemplateSelector Class and implement there logic of selecting appropriate DataTemplate as described in MSDN Article;
Define your custom DataTemplateSelector as the resource specifying the x:Key attribute;
Bind needed object to defined DataTemplateSelector resource.
UPDATE
The upper approach works best when you need to completely redesign cells as the guys mentioned in comment.
So for this task you should create your converter, define it as a resource and add it to your binding:
<!--somewhere in resources-->
<QualityToBackgroundConverter x:Key="qualityToBackgroundConverter "/>
then binding will look like:
Background="{Binding Quality, Converter={StaticResource qualityToBackgroundConverter }}"
and finally the converter:
[ValueConversion(typeof(Quality), typeof(Brush))]
public class QualityToBackgroundConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value != null)
{
Quality quality = (Quality)value;
switch (quality)
{
case 0: return Brushes.Red;
case 1: return Brushes.Yellow;
case 2: return Brushes.Green;
default: return Brushes.Transparent;
}
}
return Brushes.Transparent;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw NotImplementedException();
}
}

Categories