Question

The problem

I'm filling up a datagrid using an xmlprovider for the data. After the loading I'm trying to color "locked" columns and rows based on a list of "locked" rows & columns. "Locking" here is just a visual feedback for a certain row or column not being used for further calculations. While the loading of data works fine, I've tried different solutions to set the background property of CERTAIN rows AND columns by looping through all the cells using extensions from data grid extensions with mixed success.

I try to color the cells in the LOADED event, but that doesn't always work apparently. And there's no event that says: "your data has been loaded sir, want to do something now"?

So my last resort for coloring rows and columns is probably using style setters with a IMultiValueConverter class. But it's not clear for me how I can send the correct data to the converter.

The code

This is what I have for now:

public partial class MainWindow : Window
{
    public ObservableCollection<Person> MyDynData { get; set; }

    // property representing the locked rows
    public List<int> LockedRows { get; set; }
    // property representing the locked colums
    public List<int> LockedColumns { get; set; }

    public MainWindow()
    {
        InitializeComponent();

        MyDynData = Person.GetData();
        LockedRows = new List<int>{1,3};
        LockedColumns = new List<int>{0};

        DataContext = this;
    }
}

public class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public int Age { get; set; }

    public static ObservableCollection<Person>GetData()
    {
        return new ObservableCollection<Person>
        {
            new Person {Age = 19, FirstName = "John", LastName = "Gates"},
            new Person {Age = 25, FirstName = "Jill", LastName = "Vegas"},
            new Person {Age = 48, FirstName = "Bill", LastName = "Bates"},
            new Person {Age = 29, FirstName = "Agnes", LastName = "Henderson"},
            new Person {Age = 33, FirstName = "Jenny", LastName = "Giggles"}

        };
    }
}

The XAML:

<Window x:Class="DatagridColRowLocker.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:converters="clr-namespace:DatagridColRowLocker"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <Grid.Resources>
            <converters:CellBackgroundConverter x:Key="CellBackgroundConverter"/>
        </Grid.Resources>

        <DataGrid ItemsSource="{Binding Path=MyDynData}" AutoGenerateColumns="True">
            <DataGrid.Resources>
                <Style x:Key="LockedCellStyle" TargetType="DataGridCell">
                    <Style.Setters>
                        <Setter Property="Background">
                            <Setter.Value>
                                <MultiBinding Converter="{StaticResource CellBackgroundConverter}">
                                    <Binding> ??? the current cell</Binding>
                                    <Binding> ??? List of locked columns</Binding>
                                    <Binding> ??? list of locked rows</Binding>
                                </MultiBinding>
                            </Setter.Value>
                        </Setter>
                    </Style.Setters>
                </Style>
            </DataGrid.Resources>
        </DataGrid>
    </Grid>
</Window>

The cell background color converter:

class CellBackgroundConverter : IMultiValueConverter
{
    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        var cell = (DataGridCell)values[0];
        var lockedColumns = (List<int>)values[1];
        var lockedRows = (List<int>)values[2];

        // how to get the row index? 
        var isLocked = lockedColumns.Contains(cell.Column.DisplayIndex);

        return (isLocked) ? new SolidColorBrush(Colors.LightGray) : new SolidColorBrush(Colors.White);
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

Summary

  • How do I correctly bind my values in xaml for the multiconverter to work
  • If I pass a datagridcell to the converter, I can get the displaycolIndex but not the rowIndex, how does that work?
Was it helpful?

Solution

If you want the style to apply to all the cells then get rid of the x:Key on the style.

Here are the bindings you asked for:

<Style TargetType="DataGridCell">
    <Style.Setters>
        <Setter Property="Background">
            <Setter.Value>
                <MultiBinding Converter="{StaticResource CellBackgroundConverter}">
                    <Binding RelativeSource="{RelativeSource Self}" />
                    <Binding RelativeSource="{RelativeSource AncestorType=Window}" Path="DataContext.LockedColumns" />
                    <Binding RelativeSource="{RelativeSource AncestorType=Window}" Path="DataContext.LockedRows" />
                    <Binding RelativeSource="{RelativeSource AncestorType=DataGridRow}" />
                </MultiBinding>
            </Setter.Value>
        </Setter>
    </Style.Setters>
</Style>

And here are the changes to the converter:

public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
    var cell = (DataGridCell)values[0];
    var lockedColumns = (List<int>)values[1];
    var lockedRows = (List<int>)values[2];
    var row = (DataGridRow)values[3];

    // how to get the row index? 
    var isLocked = lockedColumns.Contains(cell.Column.DisplayIndex);
    if (!isLocked)
    {
        isLocked = lockedRows.Contains(row.GetIndex());
    }

    return (isLocked) ? new SolidColorBrush(Colors.LightGray) : new SolidColorBrush(Colors.White);
}

OTHER TIPS

In addition to the accepted answer, you might see a problem with styling when you select a row, as seen in the image below. The text on the right side of "Bill" is white and unreadable

enter image description here

In that case, you can fix this by adding extra resource settings for the datagrid:

<DataGrid.Resources>
    <SolidColorBrush x:Key="{x:Static SystemColors.HighlightTextBrushKey }" Color="Black"></SolidColorBrush>
    <SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" Color="Transparent"></SolidColorBrush>
</DataGrid.Resources>
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top