Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Need help with a custom Style in WPF

I'm attempting to build some simple custom styles for the WPF Toolkit DataGrid control.

I have a style for the overall DataGrid and a style for the DataGridColumnHeader. I am not setting any control templates, only basic properties.

Here is what my sample DataGrid looks like with my custom styling applied:

alt text http://img86.imageshack.us/img86/43/datagridcustomstyle.jpg

The header has the gradient blue background, bold text, padding, etc. I want, but two things have disappeared: the separators between the column headers and the sort arrow for the ID column (this column currently has a descending sort on it).

Why would the separators and sort arrow disappear if I have not messed with any control templates?

I even tried explicitly setting the SeparatorBrush to Black and the SeparatorVisibility to Visible, but this had no effect.

Here is what my sample DataGrid looks like if I revert back to the default styling:

alt text http://img42.imageshack.us/img42/6533/datagriddefaultstyle.jpg

The separators and sort arrow are back, so it is definitely my style that is making the difference.

Here is my custom DataGridColumnHeader style

<Style
    x:Key="DataGrid_ColumnHeaderStyle"
    TargetType="wt:DataGridColumnHeader">
    <Setter
        Property="Padding"
        Value="5,2,5,2" />
    <Setter
        Property="HorizontalContentAlignment"
        Value="Stretch" />
    <Setter
        Property="VerticalContentAlignment"
        Value="Stretch" />
    <Setter
        Property="FontWeight"
        Value="Bold" />
    <Setter
        Property="BorderBrush"
        Value="{StaticResource Media_RaisedBorderBrush}" />
    <Setter
        Property="Background"
        Value="{StaticResource Media_RaisedBackgroundBrush}" />
    <Setter
        Property="Foreground"
        Value="{StaticResource Media_RaisedForegroundBrush}" />
    <Setter
        Property="SeparatorBrush"
        Value="Black" />
    <Setter
        Property="SeparatorVisibility"
        Value="Visible" />
</Style>

Here is my custom DataGrid style

<Style
    x:Key="DataGrid_Style"
    TargetType="wt:DataGrid">
    <Setter
        Property="ColumnHeaderStyle"
        Value="{StaticResource DataGrid_ColumnHeaderStyle}" />
    <Setter
        Property="RowBackground"
        Value="{StaticResource Media_OddRowBackgroundBrush}" />
    <Setter
        Property="AlternatingRowBackground"
        Value="{StaticResource Media_EvenRowBackgroundBrush}" />
    <Setter
        Property="HorizontalGridLinesBrush"
        Value="White" />
    <Setter
        Property="VerticalGridLinesBrush"
        Value="LightGray" />
    <Setter
        Property="AutoGenerateColumns"
        Value="False" />
    <Setter
        Property="CanUserAddRows"
        Value="False" />
    <Setter
        Property="CanUserDeleteRows"
        Value="False" />
    <Setter
        Property="CanUserReorderColumns"
        Value="True" />
    <Setter
        Property="CanUserResizeColumns"
        Value="True" />
    <Setter
        Property="CanUserResizeRows"
        Value="False" />
    <Setter
        Property="CanUserSortColumns"
        Value="True" />
    <Setter
        Property="IsReadOnly"
        Value="True" />
</Style>

Here is the markup for my sample DataGrid

<wt:DataGrid
    Style="{StaticResource DataGrid_Style}"
    Margin="0,5,0,0"
    ItemsSource="{Binding Source={StaticResource Main_ContactData}, XPath=//Contacts/*}">
    <wt:DataGrid.Columns>
        <wt:DataGridTextColumn
            Binding="{Binding XPath=@Letter}"
            Header="ID" />
        <wt:DataGridTextColumn
            Binding="{Binding XPath=@Name}"
            Header="Name" />
        <wt:DataGridTextColumn
            Binding="{Binding XPath=@IsSaved}"
            Header="Saved?" />
        <wt:DataGridTextColumn
            Binding="{Binding XPath=@IsBackedUp}"
            Header="Backed Up?" />
    </wt:DataGrid.Columns>
</wt:DataGrid>

Is this a bug?

If not, can you please advise me on how to modify my styles so that I don't lose the separator lines and sort arrow?

Edit

I tried adding BasedOn attributes per @Aran's suggestion (below), but this didn't seem to have any effect. Anyone have any other thoughts?

like image 616
devuxer Avatar asked Oct 14 '09 19:10

devuxer


People also ask

How to create a style for a control in WPF?

How to create a style for a control (WPF .NET) 1 Create a style. You can think of a Style as a convenient way to apply a set of property values to one or more elements. You can use a style on any ... 2 Apply a style implicitly. 3 Apply a style explicitly. 4 Apply a style programmatically. 5 Extend a style. More items

What is wpfstyle1?

That Style targets elements of type TextBlock, and sets their FontFamily and FontSize properties. Notice that the Styles have keys named wpfStyle1, wpfStyle2.

What is WPF and how do I use it?

With Windows Presentation Foundation (WPF), you can customize an existing control's appearance with your own reusable style. Styles can be applied globally to your app, windows and pages, or directly to controls. The Desktop Guide documentation for .NET 6 and .NET 5 (including .NET Core 3.1) is under construction.

What is the difference between resource and style in WPF?

Here we may have a question like we had a concept called resources in WPF then why it needs another concept called styles in order to do the same work. The answer for your question is that a resource is used to target the properties of more than one type of control where as style can define properties only for a single type of control at a time.


2 Answers

Thanks to @Aran's answer as well as a couple posts I found in Codeplex Discussions (see http://wpf.codeplex.com/Thread/View.aspx?ThreadId=65069), I managed to come up with a set of styles that allows the background of the header row to be set without giving up functionality (sort arrows, separators, etc.).

The one exception to this is the "cell selection" feature. DataGrid has a SelectionUnit property, which can be set to Cell, FullRow, or CellOrRowHeader, but it doesn't seem to work. If I set it to FullRow (which is supposed to only select the whole row no matter where you click on that row), it still visually selects individual cells when you click on them. The image below shows an example of a DataGrid (with no styling applied) where SelectionUnit="FullRow". As you can see, it sure looks like there is a cell selected (the G row of the Name column). Since I don't currently need cell selection, I designed my styles to suppress this dark black border that appears around the cell when it's clicked.

alt text http://img80.imageshack.us/img80/4757/datagriddefaultcellsele.jpg

Here is an example of a styled DataGrid sorted descending by ID. As you can see, the down arrow appears next to the ID label, and the column is appropriately auto-sized to make room for the sort arrow.

alt text http://img377.imageshack.us/img377/3836/datagridcustomfixed.jpg

Here is another example of a styled DataGrid. This one is sorted ascending by "Saved?". Notice that the heading is centered, yet it still leaves enough room on either side for the sort arrow.

alt text http://img203.imageshack.us/img203/5140/datagridcustomfixed2.jpg


Here are the styles necessary to achieve this look.

Brushes

There are a number of brush settings in these styles, all of the form Property="{StaticResource Media_...}". I'm going to leave out the definitions for now because it would take me some time to compile them all, but if anyone is interested in duplicating the exact colors I used, leave a comment.

Sort Arrow Style

(the arrows themselves are contained in the Column Header Style)

<Style
    x:Key="DataGrid_ArrowStyle"
    TargetType="Polygon">
    <Setter
        Property="Grid.Column"
        Value="1" />
    <Setter
        Property="HorizontalAlignment"
        Value="Right" />
    <Setter
        Property="VerticalAlignment"
        Value="Bottom" />
    <Setter
        Property="StrokeThickness"
        Value="1" />
    <Setter
        Property="Stroke"
        Value="{StaticResource Media_BrightGraphicBorderBrush}" />
    <Setter
        Property="Fill"
        Value="{StaticResource Media_BrightGraphicBackgroundBrush}" />
    <Setter
        Property="Visibility"
        Value="Hidden" />
</Style>

Column Header Thumb Style

(the thumbs themselves are contained in the Column Header Style)

<Style
    x:Key="DataGrid_ColumnHeaderThumbStyle"
    TargetType="Thumb">
    <Setter
        Property="Width"
        Value="8" />
    <Setter
        Property="Background"
        Value="{StaticResource Media_RaisedSeparatorBackgroundBrush}" />
    <Setter
        Property="Cursor"
        Value="SizeWE" />
    <Setter
        Property="Template">
        <Setter.Value>
            <ControlTemplate
                TargetType="Thumb">
                <Border
                    Padding="{TemplateBinding Padding}"
                    Background="Transparent">
                    <Border
                        Padding="0,2,0,2">
                        <Rectangle
                            HorizontalAlignment="Center"
                            Width="2"
                            Fill="{TemplateBinding Background}" />
                    </Border>
                </Border>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

Column Header Style

(refers to the Sort Arrow Style and the Column Header Thumb Style defined above)

<Style
    x:Key="DataGrid_ColumnHeaderStyle"
    TargetType="wt:DataGridColumnHeader">
    <Setter
        Property="Padding"
        Value="5,2,3,3" />
    <Setter
        Property="HorizontalContentAlignment"
        Value="Stretch" />
    <Setter
        Property="VerticalContentAlignment"
        Value="Stretch" />
    <Setter
        Property="FontWeight"
        Value="Bold" />
    <Setter
        Property="BorderBrush"
        Value="{StaticResource Media_RaisedBorderBrush}" />
    <Setter
        Property="Background"
        Value="{StaticResource Media_RaisedBackgroundBrush}" />
    <Setter
        Property="Foreground"
        Value="{StaticResource Media_RaisedForegroundBrush}" />
    <Setter
        Property="SeparatorBrush"
        Value="{StaticResource Media_RaisedBorderBrush}" />
    <Setter
        Property="SeparatorVisibility"
        Value="Visible" />
    <Setter
        Property="Template">
        <Setter.Value>
            <ControlTemplate
                TargetType="wt:DataGridColumnHeader">
                <Grid>
                    <wt:DataGridHeaderBorder
                        Name="HeaderBorder"
                        BorderThickness="{TemplateBinding BorderThickness}"
                        Padding="{TemplateBinding Padding}"
                        BorderBrush="{TemplateBinding BorderBrush}"
                        Background="{TemplateBinding Background}"
                        SortDirection="{TemplateBinding SortDirection}"
                        IsClickable="{TemplateBinding CanUserSort}"
                        IsHovered="{TemplateBinding IsMouseOver}"
                        IsPressed="{TemplateBinding IsPressed}"
                        SeparatorVisibility="{TemplateBinding SeparatorVisibility}"
                        SeparatorBrush="{TemplateBinding SeparatorBrush}">
                        <Grid>
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition
                                    Width="*" />
                                <ColumnDefinition
                                    Width="12" />
                            </Grid.ColumnDefinitions>
                            <ContentPresenter
                                Name="HeaderContent"
                                Grid.Column="0"
                                HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
                                VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
                                SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
                                ContentStringFormat="{TemplateBinding ContentStringFormat}"
                                ContentTemplate="{TemplateBinding ContentTemplate}"
                                Content="{TemplateBinding Content}" />
                            <Polygon
                                Name="UpArrow"
                                Style="{StaticResource DataGrid_ArrowStyle}"
                                Margin="0,0,0,2"
                                Points="0,0 8,0 4,-6 0,0" />
                            <Polygon
                                Name="DownArrow"
                                Margin="0,0,0,1"
                                Style="{StaticResource DataGrid_ArrowStyle}"
                                Points="0,0 8,0 4,5 0,0" />
                        </Grid>
                    </wt:DataGridHeaderBorder>
                    <Thumb
                        x:Name="PART_LeftHeaderGripper"
                        Style="{StaticResource DataGrid_ColumnHeaderThumbStyle}"
                        Margin="-4,0,0,0"
                        HorizontalAlignment="Left" />
                    <Thumb
                        x:Name="PART_RightHeaderGripper"
                        Style="{StaticResource DataGrid_ColumnHeaderThumbStyle}"
                        Margin="0,0,-4,0"
                        HorizontalAlignment="Right"></Thumb>
                </Grid>
                <ControlTemplate.Triggers>
                    <MultiTrigger>
                        <MultiTrigger.Conditions>
                            <Condition
                                Property="CanUserSort"
                                Value="True" />
                            <Condition
                                Property="IsMouseOver"
                                Value="True" />
                        </MultiTrigger.Conditions>
                        <Setter
                            TargetName="HeaderBorder"
                            Property="TextBlock.Foreground"
                            Value="{StaticResource Media_MousedOverForegroundBrush}" />
                    </MultiTrigger>
                    <MultiTrigger>
                        <MultiTrigger.Conditions>
                            <Condition
                                Property="CanUserSort"
                                Value="True" />
                            <Condition
                                Property="IsPressed"
                                Value="True" />
                        </MultiTrigger.Conditions>
                        <Setter
                            TargetName="HeaderBorder"
                            Property="BorderBrush"
                            Value="{StaticResource Media_PressedBorderBrush}" />
                        <Setter
                            TargetName="HeaderBorder"
                            Property="Background"
                            Value="{StaticResource Media_PressedBackgroundBrush}" />
                        <Setter
                            TargetName="HeaderBorder"
                            Property="TextBlock.Foreground"
                            Value="{StaticResource Media_PressedForegroundBrush}" />
                    </MultiTrigger>
                    <Trigger
                        Property="SortDirection"
                        Value="Ascending">
                        <Setter
                            TargetName="UpArrow"
                            Property="Visibility"
                            Value="Visible" />
                    </Trigger>
                    <Trigger
                        Property="SortDirection"
                        Value="Descending">
                        <Setter
                            TargetName="DownArrow"
                            Property="Visibility"
                            Value="Visible" />
                    </Trigger>
                    <Trigger
                        Property="CanUserSort"
                        Value="False">
                        <Setter
                            TargetName="HeaderContent"
                            Property="Grid.ColumnSpan"
                            Value="2" />
                    </Trigger>
                    <MultiTrigger>
                        <MultiTrigger.Conditions>
                            <Condition
                                Property="HorizontalContentAlignment"
                                Value="Center" />
                            <Condition
                                Property="CanUserSort"
                                Value="True" />
                        </MultiTrigger.Conditions>
                        <Setter
                            TargetName="HeaderContent"
                            Property="Grid.ColumnSpan"
                            Value="2" />
                        <Setter
                            TargetName="HeaderContent"
                            Property="Margin"
                            Value="12,0,12,0" />
                    </MultiTrigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

Centered Column Header Style

(use this to center a header; will automatically make room for a sort arrow if the column is sortable)

<Style
    x:Key="DataGrid_CenteredColumnHeaderStyle"
    TargetType="wt:DataGridColumnHeader"
    BasedOn="{StaticResource DataGrid_ColumnHeaderStyle}">
    <Setter
        Property="HorizontalContentAlignment"
        Value="Center" />
</Style>

Wrapping Column Header Style

(use this to allow the text for a header to word-wrap--only works if you set the width manually or the user manually shrinks the column)

<Style
    x:Key="DataGrid_WrappingColumnHeaderStyle"
    TargetType="wt:DataGridColumnHeader"
    BasedOn="{StaticResource DataGrid_ColumnHeaderStyle}">
    <Setter
        Property="VerticalContentAlignment"
        Value="Top" />
    <Setter
        Property="ContentTemplate">
        <Setter.Value>
            <DataTemplate>
                <TextBlock
                    TextWrapping="WrapWithOverflow"
                    Text="{TemplateBinding Content}" />
            </DataTemplate>
        </Setter.Value>
    </Setter>
</Style>

Cell Style

<Style
    x:Key="DataGrid_CellStyle"
    TargetType="wt:DataGridCell">
    <Setter
        Property="Padding"
        Value="5,2,5,2" />
    <Setter
        Property="BorderThickness"
        Value="1" />
    <Setter
        Property="BorderBrush"
        Value="Transparent" />
    <Setter
        Property="Background"
        Value="Transparent" />
    <Setter
        Property="Template">
        <Setter.Value>
            <ControlTemplate
                TargetType="wt:DataGridCell">
                <Border
                    BorderThickness="{TemplateBinding BorderThickness}"
                    Background="{TemplateBinding Background}"
                    BorderBrush="{TemplateBinding BorderBrush}"
                    SnapsToDevicePixels="True"
                    Padding="{TemplateBinding Padding}">
                    <ContentPresenter
                        HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
                        VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
                        SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
                        ContentTemplate="{TemplateBinding ContentTemplate}"
                        ContentStringFormat="{TemplateBinding ContentStringFormat}"
                        Content="{TemplateBinding Content}" />
                </Border>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
    <Style.Triggers>
        <Trigger
            Property="IsSelected"
            Value="True">
            <Setter
                Property="BorderBrush"
                Value="{DynamicResource {x:Static SystemColors.HighlightBrushKey}}" />
            <Setter
                Property="Background"
                Value="{DynamicResource {x:Static SystemColors.HighlightBrushKey}}" />
            <Setter
                Property="Foreground"
                Value="Black" />
        </Trigger>
        <Trigger
            Property="IsKeyboardFocusWithin"
            Value="True">
            <Setter
                Property="BorderBrush"
                Value="{DynamicResource {x:Static SystemColors.HighlightBrushKey}}" />
            <Setter
                Property="Background"
                Value="{DynamicResource {x:Static SystemColors.HighlightBrushKey}}" />
            <Setter
                Property="Foreground"
                Value="Black" />
            <!--<Setter
                Property="BorderBrush"
                Value="{DynamicResource {ComponentResourceKey ResourceId=FocusBorderBrushKey, TypeInTargetAssembly=wt:DataGrid}}" />-->
        </Trigger>
    </Style.Triggers>
</Style>

Centered Cell Style

(use this to center the contents of a cell)

<Style
    x:Key="DataGrid_CenteredCellStyle"
    TargetType="wt:DataGridCell"
    BasedOn="{StaticResource DataGrid_CellStyle}">
    <Setter
        Property="HorizontalContentAlignment"
        Value="Center" />
</Style>

Data Grid Style

(establishes a set of default values for a number of properties, including the Column Header Style and Cell Style, which are defined above)

<Style
    x:Key="DataGrid_Style"
    TargetType="wt:DataGrid">
    <Setter
        Property="ColumnHeaderStyle"
        Value="{StaticResource DataGrid_ColumnHeaderStyle}" />
    <Setter
        Property="CellStyle"
        Value="{StaticResource DataGrid_CellStyle}" />
    <Setter
        Property="RowBackground"
        Value="{StaticResource Media_OddRowBackgroundBrush}" />
    <Setter
        Property="AlternatingRowBackground"
        Value="{StaticResource Media_EvenRowBackgroundBrush}" />
    <Setter
        Property="HorizontalGridLinesBrush"
        Value="LightGray" />
    <Setter
        Property="VerticalGridLinesBrush"
        Value="LightGray" />
    <Setter
        Property="SelectionMode"
        Value="Single" />
    <Setter
        Property="SelectionUnit"
        Value="FullRow" />
    <Setter
        Property="AutoGenerateColumns"
        Value="False" />
    <Setter
        Property="CanUserAddRows"
        Value="False" />
    <Setter
        Property="CanUserDeleteRows"
        Value="False" />
    <Setter
        Property="CanUserReorderColumns"
        Value="True" />
    <Setter
        Property="CanUserResizeColumns"
        Value="True" />
    <Setter
        Property="CanUserResizeRows"
        Value="False" />
    <Setter
        Property="CanUserSortColumns"
        Value="True" />
    <Setter
        Property="IsReadOnly"
        Value="True" />
    <Setter
        Property="HeadersVisibility"
        Value="Column" />
</Style>

Example DataGrid using these styles

(note: requires backing data--an XML file--to work)

<wt:DataGrid
    Style="{StaticResource DataGrid_Style}"
    Margin="0,5,0,0"
    ItemsSource="{Binding Source={StaticResource Main_ContactData}, XPath=//Contacts/*}">
    <wt:DataGrid.Columns>
        <wt:DataGridTextColumn
            Binding="{Binding XPath=@Letter}"
            Header="ID" />
        <wt:DataGridTextColumn
            Binding="{Binding XPath=@Name}"
            Header="Name" />
        <wt:DataGridTextColumn
            HeaderStyle="{StaticResource DataGrid_CenteredColumnHeaderStyle}"
            CellStyle="{StaticResource DataGrid_CenteredCellStyle}"
            Binding="{Binding XPath=@IsSaved}"
            Header="Saved?" />
        <wt:DataGridTextColumn
            HeaderStyle="{StaticResource DataGrid_CenteredColumnHeaderStyle}"
            CellStyle="{StaticResource DataGrid_CenteredCellStyle}"
            Binding="{Binding XPath=@IsBackedUp}"
            Header="Backed Up?" />
    </wt:DataGrid.Columns>
</wt:DataGrid>
like image 117
devuxer Avatar answered Sep 21 '22 07:09

devuxer


try putting

<Style
  x:Key="DataGrid_Style"
  TargetType="wt:DataGrid"
  BasedOn="{StaticResource {x:Type wt:DataGrid}}">

to base your style on the current datagrid one, and the same for the headers

<Style
  x:Key="DataGrid_ColumnHeaderStyle"
  TargetType="wt:DataGridColumnHeader"
  BasedOn="{StaticResource {x:Type wt:DataGridColumnHeader}}">

scratch the above - i pulled your style down to the offending element

   <Style
     TargetType="wt:DataGridColumnHeader"
     BasedOn="{StaticResource {x:Type wt:DataGridColumnHeader}}">
     <Setter
        Property="Background"
        Value="Blue" />
  </Style>

if you set the background (or the border brush ) you're screwed.

I found this link on code project, to back this up -

"The style of the column header can easily be modified via the ColumnHeaderStyle of the DataGrid. However, if you modify the background colour of the column header, you will find that the sort arrows disappear! This is because the arrows are not part of the ColumnHeader template; instead, they are added programmatically."

he has a style that re-adds the sort indicators.

i have had a look at the code for DataGridHeaderBorder (which is the border of the datagridrowheader) which does not have its own control template, it simply derives from border. As well as the seperators being added programatically (the separators are just rectangles see line 1199 of DataGridHeaderBorder.cs) the sort indicators are. the brief look at the code that i had would suggest they should still get drawn but they dont, a step thru of the code is in order.

The solution is to override the control template i think, and add them yourself, the link on code project will get you started.

like image 37
Aran Mulholland Avatar answered Sep 21 '22 07:09

Aran Mulholland