How to inherit XAML style and override property of child element? - wpf

we just got started with XAML and are still fighting with basic issues:
Coming from CSS we'd like to define a generic button style with custom control template and then have a second style inherit everything from the first style using "basedon. This second style should then override properties such e.g. "foreground color" (which works) but also properties of child elements within our custom template such as the "background color" of e.g. an included border element etc. (which doesn't work).
What's the general approach to go about things like this? How far can we go with cascading styles?
Cheers!

You can use an inherited style with no key reference:
<Grid>
<Grid.Resources>
<!-- Definition of default appearance of a button -->
<Style TargetType="Button" x:Key="Default">
<Setter Property="Background" Value="Red"></Setter>
<Setter Property="FontFamily" Value="Segoe Black" />
<Setter Property="HorizontalAlignment" Value="Center" />
<Setter Property="FontSize" Value="32pt" />
<Setter Property="Foreground" Value="#777777" />
</Style>
<!-- Define general style that based is base on the default style of the button without a key reference-->
<Style TargetType="Button" BasedOn="{StaticResource Default}"/>
<!-- In sub style override the properties you need -->
<Style BasedOn="{StaticResource Default}" TargetType="Button" x:Key="SubButton" >
<Setter Property="FontSize" Value="8pt" />
</Style>
</Grid.Resources>
<Button Content="Main" Height="51" HorizontalAlignment="Left" Margin="154,72,0,0" Name="button1" VerticalAlignment="Top" Width="141" />
<Button Content="Sub" Style="{StaticResource SubButton}" Height="51" HorizontalAlignment="Left" Margin="154,162,0,0" Name="button2" VerticalAlignment="Top" Width="141" />
</Grid>

I have (In my WPF application) default (base) styles defined in ResourceDictionary in App.xaml (in startup project). For example for button as follows.
<Style TargetType="Button">
<Setter Property="Margin" Value="5"/>
<Setter Property="FontWeight" Value="DemiBold"/>
<Setter Property="FontSize" Value="16"/>
</Style>
In all views I use (by default) this general style (automatically inherited)! When I need to change or add some property in default style (defined in App.xaml) I create new style based on default style.
<Style TargetType="Button" BasedOn="{StaticResource {x:Type Button}}">
<!-- change -->
<Setter Property="Margin" Value="10" />
<!-- add -->
<Setter Property="Foreground" Value="Red" />
</Style>
When I need hide or strongly redefined default style (in some view) I create new style (based on nothing).
<Style TargetType="Button"/>
You can, of course, continues in inheritance in App.xaml or in specific view. You can based new named style on default style and use new style by name. For example RedButton and GreenButton style.
<Style TargetType="Button" BasedOn="{StaticResource {x:Type Button}}" x:Key="RedButton">
<Setter Property="Foreground" Value="Red" />
</Style>
<Style TargetType="Button" BasedOn="{StaticResource {x:Type Button}}" x:Key="GreenButton">
<Setter Property="Foreground" Value="Green" />
</Style>
Etc...
NOTE: instead define your style in App.xaml you can use standalone library (dll) with styles only and ResourceDictionary from your library to App.xaml ResourceDictionary.MergedDictionaries.

The standard approach to making a customizable control template is to use TemplateBinding in the template to bind to properties of the control, and then to set those properties in the child styles.
For example, this creates a button template with a Border control, and binds the Background of the Border to the Background property of the Button. By setting the Background property of the Button in other styles, it changes the Background property of the Border.
<StackPanel>
<StackPanel.Resources>
<Style x:Key="BaseButtonStyle" TargetType="Button">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Background="{TemplateBinding Background}">
<ContentPresenter/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="BlueButtonStyle" TargetType="Button"
BasedOn="{StaticResource BaseButtonStyle}">
<Setter Property="Background" Value="Blue"/>
</Style>
<Style x:Key="RedButtonStyle" TargetType="Button"
BasedOn="{StaticResource BaseButtonStyle}">
<Setter Property="Background" Value="Red"/>
</Style>
</StackPanel.Resources>
<Button Style="{StaticResource RedButtonStyle}">Red</Button>
<Button Style="{StaticResource BlueButtonStyle}">Blue</Button>
</StackPanel>
Many of the properties on Control are intended to be used in control templates, and won't affect other behavior if they are changed. They are BorderBrush, BorderThickness, Background, Padding, HorizontalContentAlignment, and VerticalContentAlignment.

Related

TargetType does not match type of element

I have a simply buttonStyle defined for TargetType of Button; but setting the style to button gives an exeption.
<Window>
<Window.Resources>
<Style x:Key="buttonStyle" TargetType="{x:Type Button}">
<Setter Property="Background" Value="Magenta"/>
</Style>
</Window.Resources>
<StackPanel Orientation="Horizontal">
<Button Content="1" FocusVisualStyle="{StaticResource buttonStyle}"/>
</StackPanel>
</Window>
Additional information: 'Button' TargetType does not match type of
element 'Control'.
Further, setting the TargetType as Control removes the run-time error, but visual style of button doesn't change when it gets Focus.
Style works when set as Button.Style
Edit
I have two specific questions:
I agree to the fact that FocusVisualStyle is a property of FrameworkElement and FrameworkContentElement, but why is there an error setting it on button, despite the fact that Style is a namedstyle and not a typedstyle ?
Why does FocusVisualStyle don't get rendered on the Button? Is the Button.FocusVisualStyle over-ridden internally by any higher priority value like Templates, Triggers or Template Triggers ?
a FocusVisualStyle allows you to provide visual feedback to the user when a control is focused. For example, adding a Rectangle which looks like a border of the control.
A Style is the look and feel of the control itself. It's all explained here.
FocusVisualStyle is not the style for the Button itself, it's the style for when the Button is focused.
See here for more information.
I think what you are after is a Trigger.
<Style x:Key="buttonStyle" TargetType="{x:Type Button}">
<Style.Triggers>
<Trigger Property="IsFocused" Value="True">
<Setter Property="Background" Value="Magenta"/>
</Trigger>
</Style.Triggers>
</Style>
Then, you can set the Style of your Button, like so:
<Button Style="{StaticResource buttonStyle}" ... />
You should use it like this
<Style x:Key="buttonStyle" TargetType="{x:Type Button}">
<Style.Triggers>
<Trigger Property="IsFocused" Value="True">
<Setter Property="Background" Value="Magenta" />
</Trigger>
</Style.Triggers>
</Style>
and set it like this:
<Button Content="1" Style="{StaticResource buttonStyle}"/>
To see this better:
<Style x:Key="buttonStyle" TargetType="{x:Type Button}">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="Magenta" />
</Trigger>
</Style.Triggers>
</Style>
From MSDN
The focus visual style feature provides a common "object model" for
introducing visual user feedback based on keyboard navigation to any
UI element. This is possible without applying a new template to the
control, or knowing the specific template composition.
To illustrate the difference between button and focus visual style, consider this:
<Style x:Key="rectFocusVisual" TargetType="{x:Type Rectangle}">
<Setter Property="Margin" Value="2" />
<Setter Property="SnapsToDevicePixels" Value="true" />
<Setter Property="Stroke" Value="Red" />
<Setter Property="StrokeThickness" Value="1" />
<Setter Property="StrokeDashArray" Value="1 3" />
</Style>
<Style x:Key="focusVisual">
<Setter Property="Control.Template">
<Setter.Value>
<ControlTemplate>
<Rectangle Style="{StaticResource rectFocusVisual}" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="button" TargetType="{x:Type Button}">
<Setter Property="Background" Value="Green" />
</Style>
and:
<Button
Content="I am a green button with red focus rectangle"
FocusVisualStyle="{StaticResource focusVisual}"
Style="{StaticResource button}" />
<ComboBox FocusVisualStyle="{StaticResource focusVisual}" />
The difference will be apparent when the button is given focus. The button should appear green and if focused will show a red focus rectangle. If the combo box gets focus, it will have a similar red rectangle.
While the button style can only be used for Button elements, the focusVisual style can be used for any element that supports it. This way a consistent focus style can be used for any element, regardless of its underlying control type.

Change a Border Background inside a BasedOn ControlTemplate

Here is the XAML in my application resources that globally changes all of the Button controls in the application to look and behave like I want:
<Style TargetType="{x:Type Button}" x:Key="MyButtonStyle">
<Setter Property="SnapsToDevicePixels" Value="true"/>
<Setter Property="OverridesDefaultStyle" Value="true"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border x:Name="Border" CornerRadius="0" BorderThickness="0"
Background="CornflowerBlue" BorderBrush="CornflowerBlue">
<ContentPresenter Margin="2" HorizontalAlignment="Center" VerticalAlignment="Center" RecognizesAccessKey="True" />
</Border>
<ControlTemplate.Triggers>
<!-- a bunch o' triggers here -->
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
On one of my application's user controls, I would like to change some properties of this button. Here is some XAML that I am using in the UserControl.Resources section now to do this:
<Style x:Key="SpecialButton" TargetType="Button" BasedOn="{StaticResource MyButtonStyle}">
<Setter Property="Width" Value="20" />
<Setter Property="Visibility" Value="Collapsed" />
<Setter Property="Content" Value=">" />
<Setter Property="Border" Value="#eeeeee" />
<Setter Property="Border.Background" Value="#eeeeee" />
</Style>
The Button controls on my UserControl that I assign the style to of SpecialButton have the correct width, visibility, and content, but these last two attempts do not work. How would I go about changing the background color of the Border with a name of "Border" from the application resource in this SpecialButton style?
What you can do is use TemplateBinding to set the Background property on the control, in the base style. Also in the base style, set the Background color to the default "CornflowerBlue".
<Setter Property="Background" Value="CornflowerBlue" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border x:Name="Border" Background="{TemplateBinding Background}"
Now you can overwrite the Background in the derived style:
<Style x:Key="SpecialButton" TargetType="Button" BasedOn="{StaticResource MyButtonStyle}">
<Setter Property="Background" Value="#eeeeee" />
(Note that, if you want to use other properties that aren't defined on the Button control -- or, say you wanted to use multiple background colors -- then you'd have to create your own control that inherits Button, and expose the new properties as Dependency Properties.)

Not apply styles defined

i created a ResourceDictionary , and defined a style for Windows
<Style TargetType="{x:Type Window}" x:Key="WindowDefaultStyle">
<Setter Property="FontFamily" Value="Tahoma" />
<Setter Property="FlowDirection" Value="RightToLeft" />
<Setter Property="FontSize" Value="11" />
</Style>
<!-- Window file -->
<Window Style="{DynamicResource ResourceKey=WindowDefaultStyle}">
apply style in design but when run program not apply.:(
Note: I have updated my code so other people can simply use it.
Try setting the x:Keyon the Style along with TargetType like this -
<Style x:Key="{x:Type Window}" TargetType="{x:Type Window}">
<Setter Property="FontFamily" Value="Tahoma" />
<Setter Property="FlowDirection" Value="RightToLeft" />
<Setter Property="FontSize" Value="11" />
</Style>
Edit:
You need to explicitly apply style to your window by giving your style some key. For reference please see these links -
WPF window style not being applied
How to set default WPF Window Style in app.xaml?

WPF Add a Border to a TextBlock

Is it possible to add a border to a textblock. I need it to be added in the setter property below code:
<Style x:Key="notCalled" TargetType="{x:Type TextBlock}">
<Setter Property="Margin" Value="2,2,2,2" />
<Setter Property="Background" Value="Transparent" />
</Style>
You need to wrap your TextBlock in a Border. Example:
<Border BorderThickness="1" BorderBrush="Black">
<TextBlock ... />
</Border>
Of course, you can set these properties (BorderThickness, BorderBrush) through styles as well:
<Style x:Key="notCalledBorder" TargetType="{x:Type Border}">
<Setter Property="BorderThickness" Value="1" />
<Setter Property="BorderBrush" Value="Black" />
</Style>
<Border Style="{StaticResource notCalledBorder}">
<TextBlock ... />
</Border>
A TextBlock does not actually inherit from Control so it does not have properties that you would generally associate with a Control. Your best bet for adding a border in a style is to replace the TextBlock with a Label
See this link for more on the differences between a TextBlock and other Controls

Apply a style to TextBlocks within a ContentPresenter in Silverlight

If I have the following style defined:
<UserControl.Resources>
<Style TargetType="TextBlock" x:Key="ProblemStyle">
<Setter Property="FontSize" Value="40"/>
<Setter Property="FontWeight" Value="Bold"/>
</Style>
</UserControl.Resources>
Then when I have a ContentPresenter data bound to a string, in WPF I can get it to style the text as required with the following XAML:
<ContentPresenter Content="{Binding Problem}">
<ContentPresenter.Resources>
<Style TargetType="TextBlock" BasedOn="{StaticResource ProblemStyle}" />
</ContentPresenter.Resources>
</ContentPresenter>
However, in Silverlight, this doesn't work. Is there a way that works for both?
Use the TextElement Attached property. You will not be able to set a style, but most of the properties that effects the Textblock are there..
<ContentPresenter x:Name="ContentPresenter"
ContentSource="Header"
HorizontalAlignment="Left"
TextElement.FontFamily="Segoe UI"
TextElement.FontSize="12"
TextElement.FontWeight="Bold"
TextElement.Foreground="White"
RecognizesAccessKey="True" />
First:
Make sure that your style "ProblemStyle" is being loaded before the application tries to render the ContentPresenter. In Silverlight, the order the styles are defined makes a difference, and if it has not been loaded first then it may not be reading anything.
Ok, I am going to run on some assumptions here, the first one being that you are using a ContentControl to display something and that the ContentPresenter is inside of this control.
But why not create a Style for the ContentControl?
<Style x:key="ProblemStyle" TargetType="ContentControl">
<Setter Property="FontSize" Value="40"/>
<Setter Property="FontWeight" Value="Bold"/>
</Style>
Then your ContentControl would have the Style set to the StaticResource of "ProblemStyle".
Since by default the template of a ContentControl has the ContentPresenter - or you can define the ContentPresenter template in the style as well:
<Style x:key="ProblemStyle" TargetType="ContentControl">
<Setter Property="FontSize" Value="40"/>
<Setter Property="FontWeight" Value="Bold"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ContentControl">
<Border>
<ContentPresenter Content="{TemplateBinding Content}"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
The template there is just as a placeholder to give an idea of where it would/could be located.

Resources