Florian Koch
Florian Koch

Reputation: 1502

WPF DataGrid StyleSelector for Rows

I'm trying to use an ItemContainerStyleSelector to display different row styles in a datagrid, based on the type of the object defining the row (ItemsSource is a collection of IGridItems, there are GridItem and GridSeparator which should get different styles). my problem was, that the SelectStyle of my StyleSelector was never called. Now I found out (here) the problem could be an inherited style (MahApps Metro library redefines the default styles of all standard controls), which probably already sets an ItemContainerStyle.

So now my question is: Is there a way to still use my StyleSelector, so that I have the inherited style as base for the selected styles? And if not, how do I achieve a different style just for some rows based on their object type?

EDIT:
Manually setting the ItemContainerStyle to null didn't have an effect, SelectStyle of my StyleSelector is still never called.

EDIT2:
Since I don't get System.Windows.Data Error: 24 : Both 'ItemContainerStyle' and 'ItemContainerStyleSelector' are set; 'ItemContainerStyleSelector' will be ignored. like Grx70 asked, I assume that the ItemContainerStyle is not the problem, like I initially thought.

jstreet pointed out, that it's related to MahApps.Metro, though... (see his comment)


My current implementation:

<DataGrid ItemsSource="{Binding Items}" ItemContainerStyleSelector="{StaticResource StyleSelector}">

The Syle selector:

public class GridRowStyleSelector : StyleSelector
{
    private readonly ResourceDictionary _dictionary;

    public GridRowStyleSelector()
    {
        _dictionary = new ResourceDictionary
        {
            Source = new Uri(@"pack://application:,,,/myApp;component/View/GridResources.xaml")
        };
    }

    public override Style SelectStyle(object item, DependencyObject container)
    {
        string name = item?.GetType().Name;
        if (name != null && _dictionary.Contains(name))
        {
            return (Style)_dictionary[name];
        }
        return null;
    }
}

GridResources.xaml with test values:

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <Style x:Key="GridItem" TargetType="DataGridRow">
        <Setter Property="BorderThickness" Value="3"/>
    </Style>
    <Style x:Key="GridSeparator"  TargetType="DataGridRow">
        <Setter Property="BorderBrush" Value="Red"/>
    </Style>
</ResourceDictionary>

Upvotes: 0

Views: 4513

Answers (3)

Grx70
Grx70

Reputation: 10349

I think I've found the culprit. Turns out that the proper way to handle row styles in DataGrid is through RowStyle and RowStyleSelector properties and not ItemContainerStyle and ItemContainerStyleSelector. They work, but only until you explicitly use RowStyle or RowStyleSelector. That's exactly what MahApps Metro does - it sets RowStyle value (I believe through overriding DataGrid default style). Then I think ItemContainerStyle is set internally by DataGrid (some testing revealed that ItemContainerStyle was set despite explicitly setting it to null).

So to sum up, this should do the trick for you:

<DataGrid ItemsSource="{Binding Items}"
          RowStyle="{x:Null}"
          RowStyleSelector="{StaticResource StyleSelector}">
    (...)
</DataGrid>

Also, to modify MahApps row style rather than discard it completely you should base your styles on the MahApps one:

<Style x:Key="GridItem" TargetType="DataGridRow"
       BasedOn="{StaticResource MetroDataGridRow}">
    <Setter Property="BorderThickness" Value="3"/>
</Style>
<Style x:Key="GridSeparator" TargetType="DataGridRow"
       BasedOn="{StaticResource MetroDataGridRow}">
    <Setter Property="BorderBrush" Value="Red"/>
</Style>

Upvotes: 5

Manolo
Manolo

Reputation: 164

The constructor of the GridRowStyleSelector class is static and private. Try removing the 'static' keywords from this class, and make the constructor public.

Upvotes: 0

Clemens
Clemens

Reputation: 128060

You can override the default ItemContainerStyle by explicitly setting it to null:

<DataGrid ItemsSource="{Binding Items}"
          ItemContainerStyle="{x:Null}"
          ItemContainerStyleSelector="{StaticResource StyleSelector}">

Upvotes: 1

Related Questions