Binding TexBox Name to TextBlock Name, Placeholder - wpf

Short background on the problem: I am trying to make a readable, xaml only TextBox Placeholder, which is packed to a ResourceDictionary.
At this point - I have made a well working prototype, which looks and used on the page like this:
<Grid>
<TextBox Style="{StaticResource SearchBox}"
Width="325"
x:Name="UsarioDisponiblesSearch"/>
<TextBlock IsHitTestVisible="False"
Text="Search..."
VerticalAlignment="Center"
HorizontalAlignment="Left"
Margin="5,0,0,0"
Foreground="{StaticResource WhiteFadedBrush}">
<TextBlock.Style>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="Visibility" Value="Collapsed"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Text, ElementName=UsarioDisponiblesSearch}" Value="">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
<Image Source="/img/search.png" Height="15" HorizontalAlignment="Right" Margin="0,0,5,0" />
</Grid>
Grid, that holds the SearchBox consist of 3 elements:
TextBox - which will receive search string;
TextBlock - which actually holds the PlaceHolder. It disapears, as soon as ElementName of TextBox is not an empty string;
Image - custom icon on the right from the SearchBox.
What I want to achieve is a look like this on the page:
<Grid>
<TextBox Style="{StaticResource SearchBox}"
Width="325"
x:Name="UarioDisponiblesSearch"/>
<TextBlock Style="{StaticResource PlaceHolder}"
x:Name="{Binding Text, ElementName=UarioDisponiblesSearch}" />
</Grid>
And like around this styles, described in the ResourceDictionary:
<Style x:Key="SearchBox" TargetType="{x:Type TextBox}">
<Setter Property="Background" Value="Transparent" />
<Setter Property="BorderThickness" Value="0" />
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="HorizontalAlignment" Value="Left" />
<Setter Property="Margin" Value="5,0,0,0" />
<Setter Property="Foreground" Value="{StaticResource WhiteBrush}" />
</Style>
<Style x:Key="Placeholder" TargetType="{x:Type TextBlock}">
<Setter Property="IsHitTestVisible" Value="False" />
<Setter Property="Text" Value="Search..." />
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="HorizontalAlignment" Value="Center" />
<Setter Property="Margin" Value="5,0,0,0" />
<Setter Property="Foreground" Value="{StaticResource WhiteFadedBrush}" />
<Setter Property="Visibility" Value="Collapsed"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Text, RelativeSource={RelativeSource Self}}" Value="">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>
The main obstacle on my way there is the Binding, because I am not really good still in understanding how does it work, and the fact, that at this point three elements will have to share the same Name property (Which I suppose is a pretty huge obstacle as well).
I am not sticking to exactly this construction but I want it to be reusable, useful for the community and neat looking.

This XAML will give a templated TextBox that will display a given message if the Text is empty on the TextBox and it does not have focus. So when you click it the message disappears, and if you enter text (or have bound text) the message will also disappear.
You can do any further fancy bindings and such if you want to make it more re-usable. I.E. you could templatebind the DisplayText.Text to something to allow for dynamic messages.
When I was doing a quick search as I was styling, I stumbled on This, which is almost the same thing. So it would be worth giving fair reference. I prefer to use keys and compartmentalize my templates and styles. In the event I want a slightly different style elsewhere, then i can just do a BasedOn Style (which i do often). It is really 6 of one and half a dozen of another (although I didn't test the linked code).
<ControlTemplate x:Key="SearchMessageTextBoxControlTemplate" TargetType="{x:Type TextBox}">
<Grid>
<Grid x:Name="SearchTextGrid">
<ScrollViewer x:Name="PART_ContentHost" Background="{TemplateBinding Background}" />
<TextBlock x:Name="DisplayText" Text="Type Your Search..."
HorizontalAlignment="Center"
VerticalAlignment="Center"
Opacity="0.5"
Visibility="Hidden"
FontSize="{TemplateBinding FontSize}"/>
</Grid>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsFocused" Value="True">
<Setter TargetName="DisplayText" Property="Visibility" Value="Hidden"/>
</Trigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsFocused" Value="False"/>
<Condition Property="Text" Value=""/>
</MultiTrigger.Conditions>
<Setter TargetName="DisplayText" Property="Visibility" Value="Visible"/>
</MultiTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
<Style x:Key="WaterMarkMessageTextBoxStyle" TargetType="{x:Type TextBox}">
<Setter Property="FontSize" Value="20"/>
<Setter Property="Background" Value="DarkSlateGray"/>
<Setter Property="CaretBrush" Value="White"/>
<Setter Property="Foreground" Value="#F2FFE5"/>
<Setter Property="FontWeight" Value="Normal"/>
<Setter Property="BorderThickness" Value="2"/>
<Setter Property="HorizontalContentAlignment" Value="Center"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="TextWrapping" Value="Wrap"/>
<Setter Property="Template" Value="{StaticResource SearchMessageTextBoxControlTemplate}"/>
</Style>
You would use it as follows:
<TextBox Style="{StaticResource WaterMarkMessageTextBoxStyle}"/>

Related

How to set WPF Button TextBlock style from another resource?

I'm setting in a WPF application the style of a button with this:
<Style TargetType="{x:Type Button}">
<Setter Property="ToolTipService.InitialShowDelay" Value="0"/>
<Setter Property="Height" Value="22" />
<Setter Property="Margin" Value="1" />
<Setter Property="Padding" Value="0" />
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
Padding="{TemplateBinding Padding}"
BorderThickness="1">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" Margin="1 -1 1 0"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="Background" Value="{StaticResource NotSelectedButton}" />
<Setter Property="Foreground" Value="{StaticResource ActiveFg}" />
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="{StaticResource Selected}" />
<Setter Property="Foreground" Value="{StaticResource SelectedFg}" />
</Trigger>
<Trigger Property="IsMouseOver" Value="False">
<Setter Property="Background" Value="{StaticResource NotSelectedButton}" />
<Setter Property="Foreground" Value="{StaticResource ActiveFg}" />
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Background" Value="{StaticResource NotSelectedButton}" />
<Setter Property="Foreground" Value="{StaticResource InactiveFg}" />
<!--<Setter Property="Foreground" Value="{StaticResource NotSelected}" />-->
</Trigger>
</Style.Triggers>
</Style>
However I want the TextBlock in the ContentPresenter to use a named TextBlock style defined as such:
<Style x:Key="ButtonText" TargetType="{x:Type TextBlock}">
<Setter Property="ToolTipService.InitialShowDelay" Value="0"/>
<Setter Property="Width" Value="auto" />
<Setter Property="MinWidth" Value="20" />
<Setter Property="TextAlignment" Value="Left" />
<Setter Property="Padding" Value="0 0 0 0" />
<Setter Property="Margin" Value="1 0 0 0" />
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="HorizontalAlignment" Value="Center" />
</Style>
To test this style, just a Button in a UserControl or Window suffices: <Button>, and the style definitions can go in a resource dictionary (of UserControl or Window).
How can I do this ?
Trivial Solution via Direct Assignment
You can set the style directly by assigning a TextBlock as Content of each Button.
<Button>
<TextBlock Text="Test" Style="{StaticResource ButtonText}"/>
</Button>
Support Text As Content Only
If you are sure that you only put strings as content into your button, you can assign a ContentTemplate that contains a TextBlock to the ContentPresenter. This will not work for other content types in the sense that it would not display them as usual, but just their type name in the TextBlock.
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" Margin="1 -1 1 0">
<ContentPresenter.ContentTemplate>
<DataTemplate>
<TextBlock Style="{StaticResource ButtonText}" Text="{Binding}"/>
</DataTemplate>
</ContentPresenter.ContentTemplate>
</ContentPresenter>
Support All Content Types
If you want to support any content type as content of your button, you can define the data template from above in the ContentPresenter.Resources. Then, the ContentPresenter will only apply it, if the Content is a string. For all other content types it will work as usual.
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" Margin="1 -1 1 0">
<ContentPresenter.Resources>
<DataTemplate DataType="{x:Type system:String}">
<TextBlock Style="{StaticResource ButtonText}" Text="{Binding}"/>
</DataTemplate>
</ContentPresenter.Resources>
</ContentPresenter>

How can I highlight the cell which contains the error?

I have a DataGrid with four columns for which I have defined a style as well as a triggered style for the case the user enters an invalid value.
<Style TargetType="{x:Type DataGridRow}">
<Setter Property="FontSize" Value="16"/>
<Setter Property="FontFamily" Value="ArialMT"/>
<Setter Property="Height" Value="24"/>
<Setter Property="VerticalAlignment" Value="Center"/>
<Setter Property="ValidationErrorTemplate">
<Setter.Value>
<ControlTemplate>
<Grid>
<Ellipse Width="12" Height="12" Fill="Red" Stroke="Black" StrokeThickness="0.5"/>
<TextBlock FontWeight="Bold" Padding="4,0,0,0" Margin="0" VerticalAlignment="Top" Foreground="White" Text="!" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="true">
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="BorderBrush" Value="Red"/>
<Setter Property="IsEnabled" Value="True" />
<Setter Property="ToolTip" Value="{Binding RelativeSource={x:Static RelativeSource.Self}, Path=(Validation.Errors)[0].ErrorContent}"/>
</Trigger>
</Style.Triggers>
</Style>
This works well and the complete DataGridRow is marked as faulty because I used this in the XAML:
<DataGrid.RowValidationRules>
<local:CycleValidationRule ValidationStep="UpdatedValue" />
</DataGrid.RowValidationRules>
Now I want to highlight the DataGridCell with the invalid value explictely additionally (setting the background colour). Hence, I defined another style:
<Style x:Key="cycleErrStyle" TargetType="{x:Type TextBlock}">
<Setter Property="Background" Value="Magenta"/>
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="true" >
<Setter Property="Background" Value="Red" />
<Setter Property="Foreground" Value="White" />
</Trigger>
</Style.Triggers>
</Style>
but this doesn't work.
When I set <Trigger Property="Validation.HasError" Value="false" > to false, the style affects. It seems as if the Validation.HasError property has been reset after validation for the grid's row.
In the XAML I defined this:
<DataGridTextColumn x:Name="TagCycle" Header="Cycle" Binding="{Binding Mode=TwoWay, Path=RawTag.Cycle, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}"
ElementStyle="{StaticResource ResourceKey=cycleErrStyle}" />
How can I highlight the invalid cell additionally to marking the row as faulty?
You could use a DataTrigger that binds to the Validation.HasError attached property of the parent DataGridRow:
<Style x:Key="cycleErrStyle" TargetType="{x:Type TextBlock}">
<Setter Property="Background" Value="Magenta"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=(Validation.HasError), RelativeSource={RelativeSource AncestorType=DataGridRow}}" Value="true" >
<Setter Property="Background" Value="Red" />
<Setter Property="Foreground" Value="White" />
</DataTrigger>
</Style.Triggers>
</Style>

WPF Tab Styling

I am trying to style my WPF TabControl. I basically want to get the tab control to have a transparent background, with a white border and text. I want the selected tab to have a White Background and Transparent Text (or any colour text!). Essentially a 2 colour tab.
However, I cannot override the selected tab appearance - this shows as white. And my child textboxes are taking the style of the TabItem font. Note, in the screen shot my labels have their own style set so do not take the TabItem font.
I have the following XAML in place to do this. Ideally I want to create the styles so that I can reuse across the application.
Resource Dictionary
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008">
<Style x:Key="Tabs" TargetType="TabControl">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="BorderBrush" Value="White"/>
</Style>
<Style x:Key="TabItemStyle" TargetType="TabItem">
<Setter Property="Foreground" Value="White" />
<Setter Property="VerticalAlignment" Value="Top" />
<Setter Property="FontSize" Value="16" />
<Setter Property="Background" Value="Transparent"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsSelected, RelativeSource={RelativeSource AncestorType=TabItem}}" Value="True">
<Setter Property="Foreground" Value="Red"/>
<Setter Property="Background" Value="White"/>
</DataTrigger>
<DataTrigger Binding="{Binding IsSelected, RelativeSource={RelativeSource AncestorType=TabItem}}" Value="False">
<Setter Property="Foreground" Value="LightGray"/>
<Setter Property="Background" Value="Transparent"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ResourceDictionary>
Then the XAML MarkUp
<TabControl Style="{StaticResource Tabs}">
<TabItem Header="General" Style="{StaticResource TabItemStyle}">...</TabItem>
<TabItem Header="Details" Style="{StaticResource TabItemStyle}">...</TabItem>
<TabItem Header="Info" Style="{StaticResource TabItemStyle}">...</TabItem>
<TabItem Header="More Stuff..." Style="{StaticResource TabItemStyle}">...</TabItem>
</TabControl>
How can I style my tabs to be and prevent the children from sizing?
Your DataTriggers don't work. To fix it change RelatveSource to Self
Binding="{Binding Path=IsSelected, RelativeSource={RelativeSource Self}}"
However I would suggest to change them to Triggers like so:
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Foreground" Value="Red"/>
<Setter Property="Background" Value="White"/>
</Trigger>
<Trigger Property="IsSelected" Value="False">
<Setter Property="Foreground" Value="LightGray"/>
<Setter Property="Background" Value="Transparent"/>
</Trigger>
</Style.Triggers>
You should create control template for TabItem.
This sample change TabItem background to Transparent and Text color to White.
You can use own color schema.
<Window.Resources>
<Style TargetType="TabControl">
<Setter Property="Background"
Value="Transparent" />
<Setter Property="BorderBrush"
Value="White" />
</Style>
<Style TargetType="{x:Type TabItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TabItem}">
<Grid>
<Border Name="Border"
Margin="0,0,-4,0"
Background="{x:Static Brushes.White}"
BorderBrush="{x:Static Brushes.White}"
BorderThickness="1,1,1,1"
CornerRadius="2,12,0,0">
<ContentPresenter x:Name="ContentSite"
Margin="12,2,12,2"
HorizontalAlignment="Center"
VerticalAlignment="Center"
ContentSource="Header"
RecognizesAccessKey="True" />
</Border>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected"
Value="True">
<Setter Property="Panel.ZIndex"
Value="100" />
<Setter TargetName="Border"
Property="Background"
Value="{x:Static Brushes.Transparent}" />
<Setter TargetName="Border"
Property="BorderThickness"
Value="1,1,1,0" />
<Setter Property="TextBlock.Foreground"
Value="White" />
<!--<Setter Property="TextBlock.Foreground"
Value="Transparent" />-->
</Trigger>
<Trigger Property="IsEnabled"
Value="False">
<Setter TargetName="Border"
Property="Background"
Value="{x:Static Brushes.White}" />
<Setter TargetName="Border"
Property="BorderBrush"
Value="{x:Static Brushes.White}" />
<Setter Property="Foreground"
Value="{x:Static Brushes.White}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid Background="SkyBlue">
<TabControl Margin="20">
<TabItem Header="TabItem #1">
<TextBox>Tab Item #1 content</TextBox>
</TabItem>
<TabItem Header="TabItem #2">
<TextBox>Tab Item #1 content</TextBox>
</TabItem>
<TabItem Header="TabItem #3">
<TextBox>Tab Item #1 content</TextBox>
</TabItem>
</TabControl>
</Grid>

XAML/WPF focus textblock

Are textblocks focusable in WPF? I want to change the background color of the textblock if it is currently the one focused, but I want to do it in XAML.
This is what I have now. It is a bunch of textboxes in a Stackpanel. I can get the XAML to target the non focus or base state, but when I try to add a trigger, the background does not change on focus. Code is below:
<Style x:Key="QueueListTextBlocks" TargetType="TextBlock">
<Setter Property="Background" Value="#027802"></Setter>
<Setter Property="Foreground" Value="White"></Setter>
<Setter Property="Padding" Value="10,5"></Setter>
<Setter Property="Margin" Value="5,2,5,0"></Setter>
<Setter Property="FontSize" Value="14"></Setter>
<Setter Property="Focusable" Value="true"/>
<Setter Property="Cursor" Value="Hand"></Setter>
<!-- Trigger-->
<Style.Triggers>
<!--Does not pick up a IsFucused State--Alternative?-->
<Trigger Property="IsFocused" Value="True">
<Setter Property="Background" Value="Blue"></Setter>
<Setter Property="FontSize" Value="18"></Setter>
<Setter Property="Foreground" Value="Orange"></Setter>
</Trigger>
</Style.Triggers>
<!--<Setter Property="Background" Value="White" />-->
</Style>
I tried your style and it works perfectly.
TextBlocks in my window change their look just pressing the TAB key.
I'm using .NET 4.0 framework.
This is the XAML of my window:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<Style TargetType="TextBlock" x:Key="TextBlockStyle">
<Setter Property="Background" Value="#027802"></Setter>
<Setter Property="Foreground" Value="White"></Setter>
<Setter Property="Padding" Value="10,5"></Setter>
<Setter Property="Margin" Value="5,2,5,0"></Setter>
<Setter Property="FontSize" Value="14"></Setter>
<Setter Property="Focusable" Value="true"/>
<Setter Property="Cursor" Value="Hand"></Setter>
<!-- Trigger-->
<Style.Triggers>
<!--Does not pick up a IsFucused State-Alternative?-->
<Trigger Property="IsFocused" Value="True">
<Setter Property="Background" Value="Blue"></Setter>
<Setter Property="FontSize" Value="18"></Setter>
<Setter Property="Foreground" Value="Orange"></Setter>
</Trigger>
</Style.Triggers>
<!--<Setter Property="Background" Value="White" />-->
</Style>
</Window.Resources>
<StackPanel Orientation="Vertical">
<TextBlock Text="One" Style="{StaticResource TextBlockStyle}" />
<TextBlock Text="Two" Style="{StaticResource TextBlockStyle}" />
<TextBlock Text="Three" Style="{StaticResource TextBlockStyle}" />
<TextBlock Text="Four" Style="{StaticResource TextBlockStyle}" />
<TextBlock Text="Five" Style="{StaticResource TextBlockStyle}" />
</StackPanel>
</Window>
I hope it helps

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?
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>
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.

Resources