WPF DataGrid structure - changing cells based on value

I have bound an Object to DataGridTextColumn

and would like to reference one of its properties from the corresponding CellStyle. I assumed that each cell of this column would contain an instance MyObject

. However, I can't seem to find an object reference from DataGridCell

(I used a trivial converter to set a breakpoint and have been looking for an object for a long time DataGridCell

).

I am looking for MyObject.IsEnabled property and would like to reference this in the Path-Property marked with ??? in the code below. Any suggestions?

<DataGridTextColumn Binding="{Binding MyObject}">
    <DataGridTextColumn.CellStyle>
        <Style TargetType="DataGridCell">
            <Style.Triggers>
                <DataTrigger Path="???" Binding="{Binding RelativeSource={RelativeSource Self}, PresentationTraceSources.TraceLevel=High,Converter={StaticResource debugger}}"  Value="False">
                    <!-- some setters -->
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </DataGridTextColumn.CellStyle>
</DataGridTextColumn>

      

EDIT:

Since I want to apply this style to all cells in my DataGrid later, it is important to find the object bound to the cell via RelativeSource

instead of adding the hard-coded to MyObject

.

Decision

Thanks to the contribution of antiocol, I was able to find a solution for my case that could possibly be adapted to similar problems.

Since the problem is that we don't have access to Cell values ​​or CellModel

from CellStyle

, we use the attached property in DataGridCell

to store the integer CellModel

in there. From there, we can bind any available Property DataGridCell

to any property of ours CellModel

.

code for attached property:

public static class DataGridUtils
{
public static CellModel GetCellModel(DependencyObject obj)
{
    return (CellModel)obj.GetValue(CellModelProperty);
}
public static void SetCellModel(DependencyObject obj, CellModel value)
{
    obj.SetValue(CellModelProperty, value);
}
public static readonly DependencyProperty CellModelProperty =
    DependencyProperty.RegisterAttached("CellModel", typeof(CellModel), typeof(DataGridUtils), new UIPropertyMetadata(null));

      

We need to set this property in every cell in our DataGrid. I haven't found a good solution for this in XAML, so for now I have installed it in the converter before I get the information. (suggestions for improvement appreciated)

Converter

public class CellToEnabledConverter : IMultiValueConverter
{
    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        var cell = values[0] as DataGridCell;
        DataGridTextColumn column = cell.Column as DataGridTextColumn;
        //you should probably check if column is null after casting.
        Binding b = column.Binding as Binding;

        //Any suggestions on how to create a Binding to the parent object properly? 
        //I needed this workaround since I bind `MyObject.Value` to the `DataGridTextColumn`, 
        //but need a reference to `MyObject` here.
        Binding b1 = new Binding(b.Path.Path.Split('.')[0]){ Source = cell.DataContext };

        cell.SetBinding(DataGridUtils.CellModelProperty, b1);
        CellModel c = DataGridUtils.GetCellModel(cell);

        return c.IsEnabled;
    }

      

Now we can define a global Style

in XAML and apply it to everything DataGrid

instead of a single column.

<Window.Resources>
    <converter:CellToEnabledConverter x:Key="CellToEnabledConverter" />

    <Style x:Key="DataGridCellStyle" TargetType="DataGridCell">
        <Style.Triggers>
            <DataTrigger Value="False">
                <DataTrigger.Binding>
                    <!--This Converter works only on DataGridTextColumns with this minimal example!-->
                    <Binding Converter="{StaticResource CellToEnabledConverter}">
                        <Binding RelativeSource="{RelativeSource Self}" />
                    </Binding>
                </DataTrigger.Binding>
                <!--Setters-->
            </DataTrigger>
        </Style.Triggers>
    </Style>
</Window.Resources>

<DataGrid CellStyle="{StaticResource DataGridCellStyle}">
    <DataGridTextColumn Binding="{Binding MyObject.Value}"/>
</DataGrid>

      

Since I found several comments on the net that say "styling a cell based on its value is simply not possible with the current one DataGrid

", I hope this workaround helps someone get out.

+3


source to share


3 answers


I tried another solution for your problem.

<Style x:Key="DataGridCellStyle" TargetType="{x:Type DataGridCell}">
    <Style.Triggers>
        <DataTrigger Binding="{Binding Path=., RelativeSource={RelativeSource Self},  Converter={StaticResource DataGridCellToBooleanConverter}, ConverterParameter=IsEnabled}" Value="True">
            <Setter Property="Background" Value="Yellow"/>
        </DataTrigger>
    </Style.Triggers>
</Style>

<DataGrid CellStyle="{StaticResource DataGridCellStyle}">
   <DataGrid.Columns>
        <DataGridTextColumn Header="MyObject1" Binding="{Binding MyObject1}" />
        <DataGridTextColumn Header="MyObject2" Binding="{Binding MyObject2}" />
   </DataGrid.Columns>
</DataGrid>

      

On the other hand, I'm assuming ItemsSource

yours DataGrid

is a collection of objects Element

(or something similar of course)

public class Element
{
    public string Name { get; set; }
    public MyObject MyObject1 { get; set; }
    public MyObject MyObject2 { get; set; }
}
public class MyObject
{
    public string Name { get; set; }
    public bool IsEnabled { get; set; }
    public override string ToString()
    {
        return Name;
    }
}

      

Finally, the converter:



public class DataGridCellToBooleanConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        string propertyToAppend = parameter as string;
        var cell = value as DataGridCell;
        var column = cell.Column as DataGridTextColumn;
        Binding b = column.Binding as Binding;
        Binding b1 = new Binding(string.Join(".", b.Path.Path, propertyToAppend)) { Source = cell.DataContext };
        CheckBox dummy = new CheckBox();
        dummy.SetBinding(CheckBox.IsCheckedProperty, b1);
        return dummy.IsChecked;
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

      

The tricky part is using ConverterParameter

to pass the name of the property you want to bind to DataTrigger

.

PS You can use reflection instead of the hacky element CheckBox dummy

, but that works for me.

Hope it helps

+1


source


The only thing I can think of is to switch each one DataGridTextColumn

to DataGridTemplateColumn

and inside each CellTemplate

add some ContentControl whose DataContext is bound to a property that goes into that column.

This way you can create a reusable styling for that ContentControl as it DataGridCell

didn't help you: P



<Style x:Key="MyObjectStyle" TargetType="{x:Type ContentControl}">
    <Setter Property="ContentTemplate">
        <Setter.Value>
            <DataTemplate>
                <!-- your TextBlock and stuff -->
            </DataTemplate>
        </Setter.Value>
    </Setter>
        <Style.Triggers>
            <DataTrigger Binding="{Binding IsEnabled, PresentationTraceSources.TraceLevel=High, Converter={StaticResource debugger}}"  Value="False">
                <!-- some setters -->
            </DataTrigger>
        </Style.Triggers>
</Style>

<DataGridTemplateColumn>
    <DataGridTemplateColumn.CellTemplate>
        <DataTemplate>
            <ContentControl Content="{Binding MyObject}"
                            Style="{StaticResource MyObjectStyle}" />
        </DataTemplate>
    </DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>

      

0


source


First, you must correct the syntax of your DataTrigger, something like this:

<DataGridTextColumn Binding="{Binding MyObject}">
    <DataGridTextColumn.CellStyle>
        <Style TargetType="DataGridCell">
            <Style.Triggers>
                <DataTrigger Binding="{Binding MyObject.IsEnabled, PresentationTraceSources.TraceLevel=High, Converter={StaticResource debugger}}"  Value="False">
                    <!-- some setters -->
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </DataGridTextColumn.CellStyle>
</DataGridTextColumn>

      

Notice the Binding DataTrigger property. In this property you need to write Path

in your case IsEnabled

due to the fact that DataContext

in each DataGridCell

will be an instance of an object MyObject

. You don't need to use RelativeSource

because yours Binding

points to MyObject

, as said earlier.

If you want to create this style as a resource, you can do this:

<Style x:Key="cellStyle" TargetType="DataGridCell">
    <Style.Triggers>
        <DataTrigger Binding="{Binding Path=MyObject.IsEnabled, PresentationTraceSources.TraceLevel=High, Converter={StaticResource debugger}}"  Value="False">
            <!-- some setters -->
        </DataTrigger>
    </Style.Triggers>
</Style>

      

-1


source







All Articles