views:

299

answers:

2

I have a WPF DataGrid which has an AlternatingRowBackground brush. It's configured to color every other row. I'd like to do something on mouse over that highlights the current row. However, the Style Trigger seems to be losing out to the AlternatingRowBackground brush. I get the desired row coloration on the mouse over... but only on the rows that are not painted with the AlternatingRowBackground brush.

Here is the Style in Windows.Resources:

<Window.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="/Skins/MainSkin.xaml" />
            </ResourceDictionary.MergedDictionaries>
            <Style TargetType="{x:Type DataGridRow}">
                <Style.Triggers>
                    <Trigger Property="IsMouseOver"
                             Value="True">
                        <Setter Property="Background"
                                Value="Red" />
                        <Setter Property="FontWeight"
                                Value="ExtraBold" />
                        <Setter Property="Height"
                                Value="20" />
                    </Trigger>
                </Style.Triggers>
            </Style>
        </ResourceDictionary>
    </Window.Resources>

And here is the DataGrid:

    <DataGrid Margin="25,15,25,0"
              VerticalAlignment="Top"
              ItemsSource="{Binding DocumentTypeList}"
              AutoGenerateColumns="False"
              Height="500"
              AlternationCount="2"
              FrozenColumnCount="2"
              AlternatingRowBackground="{DynamicResource AlternatingRow}">
        <DataGrid.Columns>
            <DataGridTextColumn Binding="{Binding Abbreviation}"
                                Header="Abbreviation" />
            <DataGridTextColumn Binding="{Binding Title}"
                                Header="Title" />

            <DataGridTextColumn Binding="{Binding Fee}"
                                Header="Fee" />
            <DataGridTextColumn Binding="{Binding SpecialInstructions}"
                                Header="Special Instructions" />
        </DataGrid.Columns>
    </DataGrid>

Is there a way to declare the absolute winner? Is the issue one of a hierarchy? It seems to me that the AlternatingRowBackground brush wins out because it is directly associated with the most specific part of the declaration.

Update: Here is the correct syntax, based upon @Val's guidance:

<Window.Resources>
    <ResourceDictionary>
        <ResourceDictionary.MergedDictionaries>
            <ResourceDictionary Source="/Skins/MainSkin.xaml" />
        </ResourceDictionary.MergedDictionaries>
        <Style TargetType="{x:Type DataGridRow}">
            <Style.Triggers>
                <Trigger Property="IsMouseOver"
                         Value="True">
                    <Setter Property="Background"
                            Value="Red" />
                    <Setter Property="FontWeight"
                            Value="ExtraBold" />
                    <Setter Property="Height"
                            Value="20" />
                </Trigger>
            </Style.Triggers>
        </Style>
        <Style TargetType="{x:Type DataGrid}">
            <Setter Property="AlternatingRowBackground" Value="{DynamicResource AlternatingRow}"/>
        </Style>
    </ResourceDictionary>
</Window.Resources>

And the DataGrid (minus the AlternatingRowBackground brush):

<DataGrid Margin="25,15,25,0"
              VerticalAlignment="Top"
              ItemsSource="{Binding DocumentTypeList}"
              AutoGenerateColumns="False"
              Height="500"
              AlternationCount="2"
              FrozenColumnCount="2">
        <DataGrid.Columns>
            <DataGridTextColumn Binding="{Binding Abbreviation}"
                                Header="Abbreviation" />
            <DataGridTextColumn Binding="{Binding Title}"
                                Header="Title" />

            <DataGridTextColumn Binding="{Binding Fee}"
                                Header="Fee" />
            <DataGridTextColumn Binding="{Binding SpecialInstructions}"
                                Header="Special Instructions" />
        </DataGrid.Columns>
    </DataGrid>
+3  A: 

What's worked for me in the past with this kind of thing, is to use a setter outside the triggers eg:

<Style TargetType="{x:Type DataGridRow}">
    <Style.Triggers>
        <Trigger Property="IsMouseOver"
                 Value="True">
             <Setter Property="Background"
                     Value="Red" />
             <Setter Property="FontWeight"
                     Value="ExtraBold" />
             <Setter Property="Height"
                     Value="20" />
        </Trigger>
    </Style.Triggers>
    <Setter Property="AlternatingRowBackground"
            Value="{DynamicResource AlternatingRow}"/>
</Style>

And them remove the property binding on the DataGrid itself. Although I usually do this with data triggers, and not usually with dynamic resource bindings. But still might be worth a shot

Val
@Val - you definitely me going in the right direction. I added the Setter property as you suggested... not to the {x:Type DataGridRow}, but as a separate style for {x:Type DataGrid}. That does the trick.
Mike L
Thank you! Not sure why defining the AlternatingRowBackground in a DataGrid Style instead of in DataGrid.AlternatingRowBackground directly allows the trigger to work, but I'm glad it does!
Sam Pearson
+4  A: 

There's two ways to do this, neither is particularly obvious. Since DataGridRow transfers (in code) the background property from the parent DataGrid to a local value in the row, as you noted it will take precedence over the value set by your trigger.

The first (and simplest) way is to not use the AlternatingRowBackground or RowBackground but instead use triggers to alternate the background color as Val suggested. His example is not complete though and will not work as-is. The correct style and usage would be as follows. Note that you need to set the AlternationCount on DataGrid or else the rows will never get alternating indexes.

<DataGrid AlternationCount="2">
    <DataGrid.RowStyle>
        <Style TargetType="DataGridRow">
            <Setter Property="Background" Value="White"/>
            <Setter Property="FontWeight" Value="Normal"/>
            <Style.Triggers>
                <Trigger Property="AlternationIndex" Value="1">
                    <Setter Property="Background" Value="Wheat"/>
                    <Setter Property="FontWeight" Value="Bold"/>
                </Trigger>
                <Trigger Property="IsMouseOver" Value="True">
                    <Setter Property="Background" Value="Khaki"/>
                </Trigger>
            </Style.Triggers>
        </Style>
    </DataGrid.RowStyle>
</DataGrid>

The second option is to use the VisualStateManager. This gives you far more control over the different visual states but it is more verbose. Fortunately it's quite easy to copy the default control template using Blend. Most of the following is unchanged except the Storyboard in the MouseOver state and I've set a background on the selectiveScrollingGrid.

Sorry for the wrap, but like I said, it's a bit more verbose.

<DataGrid AlternationCount="2" AlternatingRowBackground="Wheat">
  <DataGrid.RowStyle>
    <Style TargetType="DataGridRow">
      <Setter Property="Template">
        <Setter.Value>
          <ControlTemplate TargetType="DataGridRow">
            <Border x:Name="DGR_Border" 
                    Background="{TemplateBinding Background}"
                    BorderBrush="{TemplateBinding BorderBrush}"
                    BorderThickness="{TemplateBinding BorderThickness}"
                    SnapsToDevicePixels="True">
              <VisualStateManager.VisualStateGroups>
                <VisualStateGroup x:Name="CommonStates">
                  <VisualState x:Name="Normal"/>
                  <VisualState x:Name="Normal_AlternatingRow"/>
                  <VisualState x:Name="Unfocused_Editing"/>
                  <VisualState x:Name="Normal_Editing"/>
                  <VisualState x:Name="Unfocused_Selected"/>
                  <VisualState x:Name="Normal_Selected"/>
                  <VisualState x:Name="MouseOver_Unfocused_Editing"/>
                  <VisualState x:Name="MouseOver_Editing"/>
                  <VisualState x:Name="MouseOver_Unfocused_Selected"/>
                  <VisualState x:Name="MouseOver_Selected"/>
                  <VisualState x:Name="MouseOver">
                    <Storyboard Storyboard.TargetName="Highlight">
                      <ColorAnimation Duration="0" Storyboard.TargetProperty="Color" To="Khaki"/>
                    </Storyboard>
                  </VisualState>
                </VisualStateGroup>
              </VisualStateManager.VisualStateGroups>
              <SelectiveScrollingGrid x:Name="selectiveScrollingGrid">
                <SelectiveScrollingGrid.Background>
                  <SolidColorBrush x:Name="Highlight" Color="Transparent"/>
                </SelectiveScrollingGrid.Background>
                <SelectiveScrollingGrid.ColumnDefinitions>
                  <ColumnDefinition Width="Auto"/>
                  <ColumnDefinition Width="*"/>
                </SelectiveScrollingGrid.ColumnDefinitions>
                <SelectiveScrollingGrid.RowDefinitions>
                  <RowDefinition Height="*"/>
                  <RowDefinition Height="Auto"/>
                </SelectiveScrollingGrid.RowDefinitions>
                <DataGridCellsPresenter Grid.Column="1" ItemsPanel="{TemplateBinding ItemsPanel}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
                <DataGridDetailsPresenter Grid.Column="1" Grid.Row="1" SelectiveScrollingGrid.SelectiveScrollingOrientation="{Binding AreRowDetailsFrozen, ConverterParameter={x:Static SelectiveScrollingOrientation.Vertical}, Converter={x:Static DataGrid.RowDetailsScrollingConverter}, RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}" Visibility="{TemplateBinding DetailsVisibility}"/>
                <DataGridRowHeader Grid.RowSpan="2" SelectiveScrollingGrid.SelectiveScrollingOrientation="Vertical" Visibility="{Binding HeadersVisibility, ConverterParameter={x:Static DataGridHeadersVisibility.Row}, Converter={x:Static DataGrid.HeadersVisibilityConverter}, RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}"/>
              </SelectiveScrollingGrid>
            </Border>
          </ControlTemplate>
        </Setter.Value>
      </Setter>
    </Style>
  </DataGrid.RowStyle>
</DataGrid>
Josh Einstein
Thanks Josh. I really like the first option you describe. I wish I could split the accepted answer.
Mike L
LOL doesn't really matter but shouldn't the accepted answer at least compile? :)
Josh Einstein
Ha ha! Probably so... but I'd feel cruel taking away an answer from someone with 16 reputation versus your 17.3k... :)
Mike L
Haha, go ahead. The better answer should be the accepted answer after all.
Val