Why doesn't my TextBlock/TextBox apply values from a Base Style? - wpf

It's not uncommon for me to write something like below for styling a data entry form, but my problem is that TextBox and TextBlock don't seem to implement the Setters that are in the BaseElementStyle. Usually I need to define them separately.
Why is this? And is there a way around it?
I am guessing it has to do with the fact they are usually used in other control templates (for example TextBlock is used in most controls and TextBox is used in DatePickers and ComboBoxes)
<Style x:Key="BaseElementStyle" TargetType="{x:Type FrameworkElement}">
<Setter Property="Margin" Value="5" />
<Setter Property="VerticalAlignment" Value="Center" />
</Style>
<Style TargetType="{x:Type TextBlock}" BasedOn="{StaticResource BaseElementStyle}" />
<Style TargetType="{x:Type TextBox}" BasedOn="{StaticResource BaseElementStyle}" />
<Style TargetType="{x:Type Label}" BasedOn="{StaticResource BaseElementStyle}" />
<Style TargetType="{x:Type ComboBox}" BasedOn="{StaticResource BaseElementStyle}" />
<Style TargetType="{x:Type DatePicker}" BasedOn="{StaticResource BaseElementStyle}" />
<Style TargetType="{x:Type CheckBox}" BasedOn="{StaticResource BaseElementStyle}" />

I would like to suggest the two possible workarounds. It seems that each of Key and Type can be used but both of them cannot be used together as your question case, x:Key="BaseElementStyle" TargetType="{x:Type FrameworkElement}".
using x:Key
<Style x:Key="BaseElementStyle">
<Setter Property="FrameworkElement.Margin" Value="5" />
<Setter Property="FrameworkElement.VerticalAlignment" Value="Center" />
</Style>
<Style TargetType="{x:Type TextBlock}" BasedOn="{StaticResource BaseElementStyle}" />
<Style TargetType="{x:Type TextBox}" BasedOn="{StaticResource BaseElementStyle}" />
<Style TargetType="{x:Type Label}" BasedOn="{StaticResource BaseElementStyle}" />
<Style TargetType="{x:Type ComboBox}" BasedOn="{StaticResource BaseElementStyle}" />
<Style TargetType="{x:Type DatePicker}" BasedOn="{StaticResource BaseElementStyle}" />
<Style TargetType="{x:Type CheckBox}" BasedOn="{StaticResource BaseElementStyle}" />
using x:Type
<Style TargetType="{x:Type FrameworkElement}">
<Setter Property="Margin" Value="5" />
<Setter Property="VerticalAlignment" Value="Center" />
</Style>
<Style TargetType="{x:Type TextBlock}" BasedOn="{StaticResource {x:Type FrameworkElement}}" />
<Style TargetType="{x:Type TextBox}" BasedOn="{StaticResource {x:Type FrameworkElement}}" />
<Style TargetType="{x:Type Label}" BasedOn="{StaticResource {x:Type FrameworkElement}}" />
<Style TargetType="{x:Type ComboBox}" BasedOn="{StaticResource {x:Type FrameworkElement}}" />
<Style TargetType="{x:Type DatePicker}" BasedOn="{StaticResource {x:Type FrameworkElement}}" />
<Style TargetType="{x:Type CheckBox}" BasedOn="{StaticResource {x:Type FrameworkElement}}" />

Also keep in mind that WPF considers a ControlTemplate to be an inflation boundary and does NOT apply default styles inside of templates. The exception to the rule: anything that inherits from Control WILL BE inflated with the default style. Since TextBlock inherits from FrameworkElement and not from Control, if you use of it inside of a ControlTemplate you will also have to apply it's style manually. This is true for both TextBlocks that are added by hand, or by TextBlocks added by WPF for string Content. A quick example:
<Window x:Class="ImplicitStyles.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">
<StackPanel>
<StackPanel.Resources>
<Style x:Key="BaseElementStyle">
<Setter Property="FrameworkElement.Tag" Value="Hello World" />
</Style>
<Style TargetType="{x:Type Button}" BasedOn="{StaticResource BaseElementStyle}" />
<Style TargetType="{x:Type TextBlock}" BasedOn="{StaticResource BaseElementStyle}" />
<!-- Default style for TextBlock will not be applied, Tag will be null -->
<ControlTemplate x:Key="MyContentControlTemplateOne" TargetType="{x:Type ContentControl}">
<Border BorderBrush="Red" BorderThickness="2">
<TextBlock Text="{Binding RelativeSource={RelativeSource Self}, Path=Tag}" />
</Border>
</ControlTemplate>
<!-- Default style for Button will be applied, Tag will be Hello World -->
<ControlTemplate x:Key="MyContentControlTemplateTwo" TargetType="{x:Type ContentControl}">
<Border BorderBrush="Red" BorderThickness="2">
<Button Content="{Binding RelativeSource={RelativeSource Self}, Path=Tag}" />
</Border>
</ControlTemplate>
</StackPanel.Resources>
<ContentControl Template="{StaticResource MyContentControlTemplateOne}" />
<ContentControl Template="{StaticResource MyContentControlTemplateTwo}" />
</StackPanel>
</Window>
For more information, see this blog post:
http://blogs.msdn.com/b/wpfsdk/archive/2009/08/27/implicit-styles-templates-controls-and-frameworkelements.aspx

Related

Using the same ControlTemplate in different styles and override a property

For two TabItems I have two Styles, both use the same ControlTemplate. Now I want styleTabB to show a yellow underline instead of green, but still using the ControlTemplate. How can I modify the Style to accomplish this?
<Window x:Class="WpfApplication1.MainWindow"
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"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApplication1"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525" FontSize="16">
<Window.Resources>
<ControlTemplate x:Key="ctrlTemplate" TargetType="{x:Type TabItem}">
<Grid>
<TextBlock Name="tbTabItemHeaderText"
Text="{TemplateBinding Header}"
Grid.Column="0"
Background="Thistle"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Margin="3,0,0,3">
<TextBlock.TextDecorations>
<TextDecorationCollection>
<TextDecoration Location="Underline"
PenThicknessUnit="Pixel"
PenOffsetUnit="Pixel"
PenOffset="2">
<TextDecoration.Pen>
<Pen Brush="Green" Thickness="4" />
</TextDecoration.Pen>
</TextDecoration>
</TextDecorationCollection>
</TextBlock.TextDecorations>
</TextBlock>
</Grid>
</ControlTemplate>
<!-- Style Tab A -->
<Style x:Key="styleTabA" TargetType="{x:Type TabItem}">
<Setter Property="Template" Value="{StaticResource ctrlTemplate}" />
</Style>
<!-- Style Tab B -->
<Style x:Key="styleTabB" TargetType="{x:Type TabItem}">
<Setter Property="Template" Value="{StaticResource ctrlTemplate}" />
</Style>
</Window.Resources>
<Grid>
<TabControl Name="tabControl">
<TabItem Name="tabItem_1" Header="--- Tab A ---" Style="{StaticResource styleTabA}"/>
<TabItem Name="tabItem_2" Header="--- Tab B ---" Style="{StaticResource styleTabB}" />
</TabControl>
</Grid>
</Window>
UPDATE
I tried the proposal of Chris W., but no underline at all is shown:
<ControlTemplate x:Key="ctrlTemplate" TargetType="{x:Type TabItem}">
...
<TextDecoration.Pen>
<Pen Brush="{TemplateBinding Tag}" Thickness="4" />
</TextDecoration.Pen>
...
</ControlTemplate>
<!--Style Tab A-->
<Style x:Key="styleTabA" TargetType="{x:Type TabItem}">
<Setter Property="Tag" Value="Green" />
<Setter Property="Template" Value="{StaticResource ctrlTemplate}" />
</Style>
<!--Style Tab B-->
<Style x:Key="styleTabB" TargetType="{x:Type TabItem}">
<Setter Property="Tag" Value="Yellow" />
<Setter Property="Template" Value="{StaticResource ctrlTemplate}" />
</Style>
You can set the style of the children within the child by targetting particular types. Here all Pen are updated with Yellow color.
<Style x:Key="styleTabB" TargetType="{x:Type TabItem}">
<Style.Resources>
<Style TargetType="{x:Type Pen}">
<Setter Property="Brush" Value="Yellow"></Setter>
</Style>
</Style.Resources>
<Setter Property="Template" Value="{StaticResource ctrlTemplate}" />
</Style>
#CarbineCoder is correct for most instances but for your instance you're right and the error you received from his would be expected since Pen isn't a TargetType. However if we tweak it just a little to hit the actual FrameworkElement of which TextDecorations is a property let's try this...and read the whole thing since the first snippet is just an example explanation.
<Style x:Key="styleTabB" TargetType="{x:Type TabItem}">
<Style.Resources>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="TextDecorations">
<Setter.Value>
<TextDecorationCollection>
<TextDecoration>
<TextDecoration.Pen>
<Pen Brush="Yellow"/>
</TextDecoration.Pen>
</TextDecoration>
</TextDecorationCollection>
</Setter.Value>
</Setter>
</Style>
</Style.Resources>
<Setter Property="Template" Value="{StaticResource ctrlTemplate}" />
</Style>
** *Except that's not going to work because you're hoping for something that you've already provided an explicit property value for to inherit from parent, and it doesn't work that way. How about instead we bring in the handy Tag property (which I abuse all the time) to piggy back in our value and allow a way to talk to our buddy on the inside of that bugger by making some quick edits to your ControlTemplate like;
<!-- In your STYLE Template you would want to add a default setter of;
<Setter Property="Tag" Value="Green"/>
-->
<ControlTemplate x:Key="ctrlTemplate" TargetType="{x:Type TabItem}">
<Grid Margin="0,0,0,0">
<TextBlock Name="_tbTabItemHeaderText"
Text="{TemplateBinding Header}"
Grid.Column="0"
Background="Thistle"
VerticalAlignment="Center"
Margin="3,0,0,3">
<TextBlock.TextDecorations>
<TextDecorationCollection>
<TextDecoration Location="Underline"
PenThicknessUnit="Pixel"
PenOffsetUnit="Pixel"
PenOffset="2">
<TextDecoration.Pen>
<Pen Brush="{TemplateBinding Tag}" Thickness="4" />
</TextDecoration.Pen>
</TextDecoration>
</TextDecorationCollection>
</TextBlock.TextDecorations>
</TextBlock>
</Grid>
</ControlTemplate>
Now we should be able to hit like;
<!-- Style Tab A
: This guy should just keep it green IF you applied the default setter mentioned above to the STYLE template -->
<Style x:Key="styleTabA" TargetType="{x:Type TabItem}">
<Setter Property="Template" Value="{StaticResource ctrlTemplate}" />
</Style>
<!-- Style Tab B
: This guy should turn it Yellow -->
<Style x:Key="styleTabB" TargetType="{x:Type TabItem}">
<Setter Property="Tag" Value="Yellow"/>
<Setter Property="Template" Value="{StaticResource ctrlTemplate}" />
</Style>
I didn't have time to test, but it seems like this should work fine for your scenario. Hope it helps.
I made a little change on Chris W.'s solution and now it's working:
<ControlTemplate x:Key="ctrlTemplate" TargetType="{x:Type TabItem}">
...
<TextDecoration.Pen>
<!--Changed next line from <Pen Brush="{TemplateBinding Tag}" Thickness="4" /> to:-->
<Pen Brush="{Binding Path=Tag, RelativeSource={RelativeSource TemplatedParent}}" Thickness="4" />
</TextDecoration.Pen>
...
</ControlTemplate>
<!--Style Tab A-->
<Style x:Key="styleTabA" TargetType="{x:Type TabItem}">
<Setter Property="Tag" Value="Green" />
<Setter Property="Template" Value="{StaticResource ctrlTemplate}" />
</Style>
<!--Style Tab B-->
<Style x:Key="styleTabB" TargetType="{x:Type TabItem}">
<Setter Property="Tag" Value="Yellow" />
<Setter Property="Template" Value="{StaticResource ctrlTemplate}" />
</Style>

wpf ToolTip and Style

How I can add ToolTip to this code ?
<Style TargetType="{x:Type ListBoxItem}">
</Style>
You can set ToolTip property in a style the same way you set any other property.
Sample code as below
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="ToolTip" Value="ToolTip Value" />
</Style>
For a more complex ToolTip use
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="ToolTip">
<Setter.Value>
<!--Content Here-->
<!--<Grid> or <StackPanel> or <ContentPresenter>...-->
</Setter.Value>
</Setter>
</Style>

WPF- Why aren't these styles working?

I am trying to set a global style for multiple control derived types by putting this in my app.xaml:
<Style TargetType="{x:Type Control}">
<Setter Property="Background" Value="{Binding BackgroundBrush, Source={x:Static m:Settings.Instance}, UpdateSourceTrigger=PropertyChanged}" />
<Setter Property="Foreground" Value="{Binding ForegroundBrush, Source={x:Static m:Settings.Instance}, UpdateSourceTrigger=PropertyChanged}" />
<Setter Property="BorderBrush" Value="{Binding ForegroundBrush, Source={x:Static m:Settings.Instance}, UpdateSourceTrigger=PropertyChanged}" />
<Setter Property="UseLayoutRounding" Value="True" />
</Style>
<Style TargetType="{x:Type Window}" BasedOn="{StaticResource {x:Type Control}}" />
<Style TargetType="{x:Type Button}" BasedOn="{StaticResource {x:Type Control}}" />
Right now the window style only works in the visual studio design window and the button style doesn't work at all. What have I done wrong?
I've found sometimes that BasedOn is rather particular.
If you assign a key then it tends to work more often.
I'm not sure if the value bindings are causing your issue as i didn't make and external static class to use.
<Grid.Resources>
<Style x:Key="simpleStyle" TargetType="{x:Type Control}">
<Setter Property="Background" Value="Blue" />
<Setter Property="Foreground" Value="Yellow" />
<Setter Property="BorderBrush" Value="CornflowerBlue" />
</Style>
<Style TargetType="{x:Type Control}" BasedOn="{StaticResource simpleStyle}" />
<Style TargetType="{x:Type Window}" BasedOn="{StaticResource simpleStyle}" />
<Style TargetType="{x:Type Button}" BasedOn="{StaticResource simpleStyle}" />
</Grid.Resources>
<Button Height="50" Width="100">
Hello
</Button>

Applying a style to all derived classes in WPF

I want to apply a style to all classes derived from Control. Is this possible with WPF?
The following example does not work. I want the Label, TextBox and Button to have a Margin of 4.
<Window x:Class="WeatherInfo.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Wetterbericht" Height="300" Width="300">
<Window.Resources>
<Style TargetType="Control">
<Setter Property="Margin" Value="4"/>
</Style>
</Window.Resources>
<Grid>
<StackPanel Margin="4" HorizontalAlignment="Left">
<Label>Zipcode</Label>
<TextBox Name="Zipcode"></TextBox>
<Button>get weather info</Button>
</StackPanel>
</Grid>
</Window>
Here's one solution:
<Window.Resources>
<Style TargetType="Control" x:Key="BaseStyle">
<Setter Property="Margin" Value="4"/>
</Style>
<Style BasedOn="{StaticResource BaseStyle}" TargetType="Button" />
<Style BasedOn="{StaticResource BaseStyle}" TargetType="Label" />
<Style BasedOn="{StaticResource BaseStyle}" TargetType="TextBox" />
</Window.Resources>
<Grid>
<StackPanel Margin="4" HorizontalAlignment="Left">
<Label>Zipcode</Label>
<TextBox Name="Zipcode"></TextBox>
<Button>get weather info</Button>
</StackPanel>
</Grid>
This is not possible in WPF. You have a couple of options to help you out:
Create one style based on another by using the BasedOn attribute.
Move the common information (margin, in this case) into a resource and reference that resource from each style you create.
Example of 1
<Style TargetType="Control">
<Setter Property="Margin" Value="4"/>
</Style>
<Style TargetType="TextBox" BasedOn="{StaticResource {x:Type Control}}">
</Style>
Example of 2
<Thickness x:Key="MarginSize">4</Thickness>
<Style TargetType="TextBox">
<Setter Property="Margin" Value="{StaticResource MarginSize}"/>
</Style>

WPF UserControl Style

I want to set the background property of all the usercontrols of my project.
I tried with
<style TargetType={x:Type UserControl}>
<setter property="Background" Value="Red" />
</style>
It compiles but didn't work.
¿Any Idea?
Thanks!
You can only set a a style to a specific class, so this will work (create a UserControl object, not very useful):
<Window.Resources>
<Style TargetType="{x:Type UserControl}">
<Setter Property="Background" Value="Red" />
</Style>
</Window.Resources>
<Grid>
<UserControl Name="control" Content="content"></UserControl>
</Grid>
But this doesn't (Create a class derived from UserControl):
<Window.Resources>
<Style TargetType="{x:Type UserControl}">
<Setter Property="Background" Value="Red" />
</Style>
</Window.Resources>
<Grid>
<l:MyUserControl Name="control" Content="content"></l:MyUserControl>
</Grid>
What you can do is either explicitly set the style using the Style property:
<Window.Resources>
<Style TargetType="{x:Type UserControl}" x:Key="UCStyle">
<Setter Property="Background" Value="Red" />
</Style>
</Window.Resources>
<Grid>
<l:MyUserControl Name="control" Content="content" Style="{StaticResource UCStyle}"></l:MyUserControl>
</Grid>
or create a style for each derived class, you can use BasedOn to avoid duplicating the style content:
<Window.Resources>
<Style TargetType="{x:Type UserControl}" x:Key="UCStyle">
<Setter Property="Background" Value="Red" />
</Style>
<Style TargetType="{x:Type l:MyUserControl}" BasedOn="{StaticResource UCStyle}" />
</Window.Resources>
<Grid>
<l:MyUserControl Name="control" Content="content"></l:MyUserControl>
</Grid>
I think you're missing some double quotes:
Try this:
<Window.Resources>
<Style TargetType="{x:Type UserControl}">
<Setter Property="Background" Value="Red" />
</Style>
</Window.Resources>
<Grid>
<UserControl Name="control" Content="content"></UserControl>
</Grid>

Resources