Column header background -- but not twice - wpf

I have the usual style:
<Style TargetType="{x:Type DataGrid}">
<Setter Property="ColumnHeaderStyle">
<Setter.Value>
<Style TargetType="{x:Type DataGridColumnHeader}">
<Setter Property="Background" Value="{x:Null}" /> <!-- this doesn't help, either -->
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type DataGridColumnHeader}">
<Border Background="SomeTransparentColor" CornerRadius="20" Margin="5" />
...
The problem is that the background is applied twice: once for all the headers together, for the complete header row, and once for each of the headers separately. Because I use a partly transparent color, this looks rather ugly.
How can I keep WPF from applying this style to the whole header row background as well?

The Default style of the DataGrid column Header puts one layout below of all column headers having the same style as the column headers. This base layer is added so that the header area visibly fills up the full horizontal space even if the sum of all column headers is smaller. The following style changes the approach but keeps the requirement and removes the base layer:
<Style TargetType="{x:Type DataGridColumnHeadersPresenter}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type DataGridColumnHeadersPresenter}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="1*" />
</Grid.ColumnDefinitions>
<ItemsPresenter />
<DataGridColumnHeader x:Name="PART_FillerColumnHeader"
Grid.Column="1"
IsHitTestVisible="False" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

Related

Apply base style of window to inherited window

I have defined a style for the Window class in my ResourceDictionary:
<Style x:Key="windowStyle"
TargetType="{x:Type Window}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Window}">
<Border Width="300" Height="300" BorderBrush="Red" BorderThickness="3">
<ContentPresenter />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Now I want a specialized window. To this end I inherit a new class from Window and add custom DependecyProperties etc.
My goal is to reuse the base style, ie. I pretty much only want to fill the ContentPresenter from the base style with a grid and add a new ContentPresenter in one of its cells, so I tried this:
<Style x:Key="customWindowStyle"
BasedOn="{StaticResource windowStyle}"
TargetType="{x:Type local:CustomWindow}">
<Setter Property="ContentTemplate">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:CustomWindow}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Image Grid.Column="0" Source="{TemplateBinding Source}" />
<ContentPresenter Grid.Column="1" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Which gives me an exception at runtime, since the ContentTemplate is of type DataTemplate as opposed to ControlTemplate. Of course if I Set the Template instead of ContentTemplate in the customWindowStyle, I lose the style of the base window.
Is there another way to achieve this goal?

WPF: How to use BasedOn Style

I am new to WPF and I created the following simple style example. But it doesn't work properly and button's content doesn't show although I can still click on it. Can anyone tell me why it is broken?
<Window.Resources>
<Style TargetType="{x:Type Button}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Border BorderBrush="Blue"
BorderThickness="5"
Background="Aqua"
Width="80"
Height="40">
<ContentPresenter></ContentPresenter>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="Grid" x:Name="GridWithMarginStyle">
<Setter Property="Margin" Value="12"></Setter>
</Style>
</Window.Resources>
<StackPanel>
<StackPanel.Resources>
<Style TargetType="{x:Type Button}" BasedOn="{StaticResource {x:Type Button}}">
<EventSetter Event="Button.Click" Handler="ButtonHandler" />
<Setter Property="Background" Value="Red"></Setter>
<Setter Property="Foreground" Value="White"></Setter>
</Style>
</StackPanel.Resources>
<Button Name="OkBtn">OK</Button>
<Button Name="CancelBtn" Click="CancelBtn_Click">Cancel</Button>
</StackPanel>
You are using the BasedOn property in the correct way. The problem is that your ContentPresenter is not binded to the control it renders (i.e. the button).
Just try to replace your ControlTemplate XAML with this one:
<ControlTemplate TargetType="{x:Type Button}">
<Border BorderBrush="Blue"
BorderThickness="5"
Background="Aqua"
Width="80"
Height="40">
<ContentPresenter Content="{TemplateBinding Content}" />
</Border>
</ControlTemplate>
By using TemplateBinding you can bind the ContentPresenter to the Content property of your templated control.

Gradient Border on Outer Edges of DataGridHeader

See the image below...
This is a screen shot taken from an application that was created in c++ using QT. I need to create a similar look by adding gradient borders to the outside vertical edges of my DataGrid header and I do not see a property of the DataGrid that will allow me to accomplish this. Using the DataGridColumnHeader style below, I have acheived the look I am after minus the border on the outside edges. How can I add these borders to the outside of the header only?
<Style TargetType="{x:Type DataGridColumnHeader}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="DataGridColumnHeader">
<Border BorderBrush="{StaticResource ContentPanelHeaderBackgroundBrush}"
BorderThickness="0,1,0,0"
CornerRadius="2,2,0,0">
<DockPanel Background="{StaticResource ContentPanelHeaderBackgroundBrush}" Width="auto">
<Border BorderThickness="0,1,0,0" DockPanel.Dock="Bottom" BorderBrush ="{StaticResource ContentPanelBottomBorderBrush}" />
<Grid Height="22">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<ContentPresenter VerticalAlignment="center" Margin="4" />
</Grid>
</DockPanel>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="Padding" Value="4,0,4,0" />
<Setter Property="Foreground" Value="{StaticResource FontActiveBrush}" />
<Setter Property="FontFamily" Value="{StaticResource MainFont}" />
<Setter Property="FontSize" Value="11" />
<Setter Property="SnapsToDevicePixels" Value="True" />
<Setter Property="HorizontalContentAlignment" Value="Left" />
<Setter Property="MinWidth" Value="0" />
<Setter Property="Height" Value="22" />
<Setter Property="Cursor" Value="Hand" />
</Style>
I would suggest using a Tool like Snoop to inspect the XAML-Tree at runtime. It seems to me that there is an additional item rendered somewhere in the tree having its default template applied. Since you don't seem to overwrite all default templates in the tree I guess you'll find the right element this way (just gotta look for an item with background or border being black).
You can find the tool here: http://snoopwpf.codeplex.com/

PasswordBox does not apply style

I have the following style definitions:
<!-- Border -->
<Style x:Key="MyControlBorder" TargetType="{x:Type Border}">
<Setter Property="BorderBrush" Value="DarkKhaki" />
<Setter Property="Background" Value="White" />
<Setter Property="BorderThickness" Value="1" />
<Setter Property="CornerRadius" Value="10" />
</Style>
<!-- TextBox -->
<Style x:Key="MyTextBox" TargetType="{x:Type TextBox}">
<Setter Property="Height" Value="30" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TextBoxBase}">
<Border Name="TextBoxBorder" Style="{StaticResource MyControlBorder}">
<ScrollViewer x:Name="PART_ContentHost"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!-- PasswordBox -->
<Style x:Key="MyPasswordBox" TargetType="{x:Type PasswordBox}">
<Setter Property="Height" Value="30" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Control}">
<Border Name="Border" Style="{StaticResource MyControlBorder}">
<ScrollViewer x:Name="PART_ContentHost" />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
And the following XAML code:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TextBox Grid.Row="0" Style="{StaticResource MyTextBox}" />
<PasswordBox Grid.Row="1" Style="{StaticResource MyPasswordBox}" />
</Grid>
Now I got this result:
The TextBox applies the style correctly, but why does the PasswordBox not apply the style?
If you wrap the Border in another Border everything just works as expected (I don't know why).
As a bonus, you can now have PasswordBoxes and TextBoxes "inherit" from the same Style, keeping things nice and DRY.
<!-- Border Style Definition -->
<Style x:Key="MyControlBorder" TargetType="Border">
<Setter Property="BorderBrush" Value="DarkKhaki" />
<Setter Property="Background" Value="White" />
<Setter Property="BorderThickness" Value="1" />
<Setter Property="CornerRadius" Value="10" />
</Style>
<!-- TextBox and PasswordBox Style -->
<Style x:Key="MyControlInputBox" TargetType="Control">
<Setter Property="Height" Value="30" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Control}">
<Border>
<Border Name="Border" Style="{StaticResource MyControlBorder}">
<ScrollViewer x:Name="PART_ContentHost" />
</Border>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!-- TextBox -->
<Style x:Key="MyTextBox" TargetType="{x:Type TextBox}" BasedOn="{StaticResource MyControlInputBox}" />
<!-- PasswordBox -->
<Style x:Key="MyPasswordBox" TargetType="{x:Type PasswordBox}" BasedOn="{StaticResource MyControlInputBox}" />
Somehow Border within ControlTemplate of PasswordBox does not take MyControlBorder style.
When you modify MyPasswordBox style like this... then it will work.
<Style x:Key="MyPasswordBox" TargetType="{x:Type PasswordBox}">
<Setter Property="Height" Value="30" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Control}">
<Border Name="Border" BorderBrush="DarkKhaki" Background="White" BorderThickness="1" CornerRadius="10">
<ScrollViewer x:Name="PART_ContentHost" />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
I know it's not the best solution... but I can't figure out why MyControlBorder is not applied. It doesn't even work when you get rid of MyTextBox style. Then you are left only with MyControlBorder and MyPasswordBox ...it does not work either.
The PasswordBox is strange in this regard, as it is explicitly implemented to show this behavior.
The default control template contains a Border, as you can see here:
<ControlTemplate TargetType="{x:Type PasswordBox}">
<Border x:Name="border" Background="{TemplateBinding Background}" BorderThickness="{TemplateBinding BorderThickness}" BorderBrush="{TemplateBinding BorderBrush}" SnapsToDevicePixels="True">
<ScrollViewer x:Name="PART_ContentHost" Focusable="false" HorizontalScrollBarVisibility="Hidden" VerticalScrollBarVisibility="Hidden"/>
</Border>
<!-- ...other markup, triggers. -->
</ControlTemplate>
The code for PasswordBox (taken from reference source) does the following:
OnApplyTemplate on line 473 is called when the control template is applied.
AttachToVisualTree on line 476 is called which is defined on line 1176.
SetRenderScopeToContentHost on line 1181 is called which is defined on line 956.
From line 1014 on it searches a Border up the visual tree beginning from the password content host. Note that it searches until it does not find a Border anymore, which means it stores the last found Border within its _border field.
In line 1200, if a Border was found, its Style property is set to null.
Consequently, whenever a control template is applied, the Style of the outermost Border is reset. This is still the same in .NET Core and >.NET , as you can see from the code on GitHub. This is the reason why #David Murdoch's answer with a redundant nested Border works, when the style is applied to the inner Border.
Now, what can do? You can either insert a dummy Border as a workaround or you have to set the CornerRadius directly to the Border inside your custom control template.
<ControlTemplate TargetType="{x:Type Control}">
<Border Name="Border" CornerRadius="10" Style="{StaticResource MyControlBorder}">
<ScrollViewer x:Name="PART_ContentHost" />
</Border>
</ControlTemplate>

WPF Template Inheritance

I created a button. My basic requirements are rounded thicker border, with more than one color (i.e. for Buy/Sell buttons)
I was hoping that i could create the template once, and than just override the border brush like this:
<Style x:Key="BorderButton">
<Setter Property="Control.Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border BorderThickness="2"
BorderBrush="Red"
CornerRadius="3"
Background="{x:Null}">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="GreenBorderButton" BasedOn="{StaticResource BorderButton}" TargetType="{x:Type Button}">
<Setter Property="BorderBrush" Value="Green" />
</Style>
but they both produce the same style.
Do i need to write out the whole template every time? seems like unnecessary code repetition (especially if 3-4 colors are desired). Hoping there is some way to inherit a template.
Your code is very close to working; the issue is that GreenBorderButton is applying the BorderBrush to the button itself, not the Border in the overridden Template.
To fix this, simply change the Border's BorderBrush to use the parent Button's BorderBrush. You can do this using a TemplateBinding like so:
<Style x:Key="BorderButton">
<Setter Property="Control.Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border x:Name="border"
BorderThickness="2"
BorderBrush="{TemplateBinding Property=BorderBrush}"
CornerRadius="3"
Background="{x:Null}">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Then, you can either use the same overridden styles like you have, or you could simply do:
<Button Style="{StaticResource BorderButton}" BorderBrush="Blue" Content="Blue" />

Resources