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?
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
That Style targets elements of type TextBlock, and sets their FontFamily and FontSize properties. Notice that the Styles have keys named wpfStyle1, wpfStyle2.
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.
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.
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
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>
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With