Trying to setup the background of a cell dependend on a cell-object property in a WPF DataGrid I get an error, that the property is not found (but on the row-object):
System.Windows.Data Error: 40 : BindingExpression path error: 'IsOn' property not found on 'object' ''MyRow' (HashCode=48826322)'. BindingExpression:Path=IsOn; DataItem='MyRow' (HashCode=48826322); target element is 'DataGridCell' (Name=''); target property is 'NoTarget' (type 'Object')
I wonder, why the DataTrigger Binding is addressing the row object "MyRow", since the DataTrigger is defined for/inside a CellStyle.
XAML:
<DataGrid Name="tblTest" Grid.Column="2" IsReadOnly="True" AutoGenerateColumns="True">
<DataGrid.CellStyle>
<Style TargetType="{x:Type DataGridCell}">
<Setter Property="Background" Value="PaleGreen" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsOn}" Value="True">
<Setter Property="Background" Value="Red"/>
</DataTrigger>
</Style.Triggers>
</Style>
</DataGrid.CellStyle>
</DataGrid>
C#
class MyCell
{
public MyCell( string v)
{
Value = v;
}
public string Value { get; set; }
public bool IsOn { get => Value == "one"; }
public override string ToString()
{
return Value;
}
}
class MyRow
{
public MyCell One { get; set; }
public MyCell Two { get; set; }
}
void SetupTestTable()
{
List<MyRow> data = new();
data.Add(new MyRow
{
One = new MyCell("one"),
Two = new MyCell("two")
});
tblTest.ItemsSource = data;
}
So how to bind against the cell object "MyCell" correctly?
DataGridCells have the same DataContext as DataGridRow - there are many obstacles to do differently in general-purpose manner. So single DataGrid.CellStyle won't work
I will use AutoGeneratingColumn
to create cell styles for each column. However they will be based on existing style which is stored in DataGrid.Resources.
<DataGrid Name="tblTest" Grid.Column="2" IsReadOnly="True"
AutoGenerateColumns="True"
AutoGeneratingColumn="tblTest_AutoGeneratingColumn">
<DataGrid.Resources>
<Style TargetType="{x:Type DataGridCell}" x:Key="ColoredCellStyle">
<Setter Property="Background" Value="Cyan" />
<Style.Triggers>
<DataTrigger Binding="{Binding Tag.IsOn, RelativeSource={RelativeSource Self}}" Value="True">
<Setter Property="Background" Value="Red"/>
</DataTrigger>
</Style.Triggers>
</Style>
</DataGrid.Resources>
</DataGrid>
I'm using binding to Tag instead of DataContext, because DataContext is MyRow
object. In Tag
there will be MyCell
objects. It is achieved in event handler:
private void tblTest_AutoGeneratingColumn(object sender, DataGridAutoGeneratingColumnEventArgs e)
{
if (e.Column is DataGridTextColumn tc && tc.Binding is Binding binding)
{
// unique value for each column
var property = binding.Path.Path;
// DataGrid reference to get Resources
var dg = (DataGrid)sender;
// new cell style which inherits trigger from ColoredCellStyle and binds Tag to MyCell property
var cellStyle = new Style
{
TargetType = typeof(DataGridCell),
BasedOn = (Style)dg.Resources["ColoredCellStyle"],
Setters =
{
new Setter
{
Property = DataGridCell.TagProperty,
Value = new Binding(property)
}
}
};
tc.CellStyle = cellStyle;
};
}