How do I apply styles to different `TextBlock` objects? - wpf

Here is my Application ResourceDictionary
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:CABI_PO_Manager.Themes">
<Style TargetType="TextBlock">
<Setter Property="HorizontalAlignment" Value="Center" />
<Setter Property="FontFamily" Value="Comic Sans MS"/>
<Setter Property="FontSize" Value="14"/>
</Style>
<Style BasedOn="{StaticResource {x:Type TextBlock}}"
TargetType="TextBlock"
x:Key="YellowTextBlock">
<Setter Property="FontSize" Value="16"/>
<Setter Property="Foreground" Value="#d8b243"/>
</Style>
<Style BasedOn="{StaticResource {x:Type TextBlock}}"
TargetType="TextBlock"
x:Key="GreenTextBlock">
<Setter Property="FontSize" Value="18"/>
<Setter Property="Foreground" Value="Green"/>
</Style>
<Style BasedOn="{StaticResource {x:Type TextBlock}}"
TargetType="TextBlock"
x:Key="RedTextBlock">
<Setter Property="FontSize" Value="14"/>
<Setter Property="Foreground" Value="#a01e21"/>
</Style>
</ResourceDictionary>
I want to have some default styles for a TextBlock which works
<Style TargetType="TextBlock">
<Setter Property="HorizontalAlignment" Value="Center" />
<Setter Property="FontFamily" Value="Comic Sans MS"/>
<Setter Property="FontSize" Value="14"/>
</Style>
I found somewhere to use x:Key but I can't get it to work.
I will have several TextBlock's, How do I identify a TextBlock in the UI XAML as Red, Yellow or Green TextBlock and apply that style to them? This isn't recognized x:Key="GreenTextBlock"
<TextBlock x:Key="GreenTextBlock" Grid.Column="1" Margin="0,10,0,0" TextWrapping="Wrap" Text="PO Manager" VerticalAlignment="Top" TextAlignment="Center" FontWeight="ExtraBold"/>

If u want apply style on for example all TextBlocks in application, just use style without x:key defined e.g
<Style TargetType="{x:Type TextBlock}">
<Setter Property="HorizontalAlignment" Value="Center" />
<Setter Property="FontFamily" Value="Comic Sans MS"/>
<Setter Property="FontSize" Value="14"/>
</Style>
When you are applying a style, use TargetType="{x:Type TextBlock}" instead of TargetType="TextBlock"
When do you use want to base on other style use
BasedOn="{StaticResource StyleUWantToBaseOn}"
where StyleUWantToBaseOn is style with x:Key property
And when you want to apply a specific style on lets say textblock you want to use Style property e.g:
<TextBlock Style="{StaticResource GreenTextBlock}" Grid.Column="1" />

Related

Xaml Styles inheritance not working as expected

I am having an issue trying to make use of the BasedOn attribute of Xaml styles. I created a fairly simple style as an example.
The project consists of two main files. MainWindow.xaml and SquareButtonStyle.xaml. SquareButtonStyle makes use of the FontAwesome.WPF project to quickly get some images.
MainWindow is basically a series of buttons with an image inside it and some text.
The goal of this example is to have three different button sizes. A "medium" size, and two variants, "small" and "large", based on the medium size.
The problem is most easily seen with the large variant. The center TextAlignment, Margin and Red text color set from the Medium style, which the large is based on, is lost. It seems that instead of inheriting the style and only override the specific property I am instead inheriting the default style and overriding the property.
What am I doing wrong? It seems the only way to correct this is to not bother with BasedOn, and just make three separate styles with the full list of properties being set. Is there a way to get around this?
SquareButtonStyle.xaml
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style TargetType="Button" x:Key="SquareButtonMediumStyle">
<Setter Property="Margin" Value="5" />
<Setter Property="Width" Value="80" />
<Setter Property="Height" Value="80" />
<Style.Resources>
<Style TargetType="StackPanel">
<Setter Property="Orientation" Value="Vertical" />
<Style.Resources>
<Style TargetType="TextBlock">
<Setter Property="FontSize" Value="12" />
<Setter Property="TextAlignment" Value="Center" />
<Setter Property="Foreground" Value="Red"></Setter>
<Setter Property="TextWrapping" Value="Wrap" />
</Style>
</Style.Resources>
</Style>
</Style.Resources>
</Style>
<Style BasedOn="{StaticResource SquareButtonMediumStyle}" TargetType="Button" x:Key="SquareButtonSmallStyle">
<Setter Property="Width" Value="55" />
<Setter Property="Height" Value="55" />
<Style.Resources>
<Style TargetType="StackPanel">
<Style.Resources>
<Style TargetType="TextBlock">
<Setter Property="FontSize" Value="10" />
</Style>
</Style.Resources>
</Style>
</Style.Resources>
</Style>
<Style BasedOn="{StaticResource SquareButtonMediumStyle}" TargetType="Button" x:Key="SquareButtonLargeStyle">
<Setter Property="Width" Value="100" />
<Setter Property="Height" Value="100" />
<Style.Resources>
<Style TargetType="StackPanel">
<Style.Resources>
<Style TargetType="TextBlock">
<Setter Property="FontSize" Value="14" />
</Style>
</Style.Resources>
</Style>
</Style.Resources>
</Style>
</ResourceDictionary>
MainWindow.xaml
<Window x:Class="XamlStyleExample.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"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="SquareButtonStyle.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
<StackPanel>
<WrapPanel>
<WrapPanel.Resources>
<Style BasedOn="{StaticResource SquareButtonMediumStyle}" TargetType="Button" />
</WrapPanel.Resources>
<Button>
<StackPanel>
<TextBlock>Button 1</TextBlock>
</StackPanel>
</Button>
<Button>
<StackPanel>
<TextBlock>Button 2</TextBlock>
</StackPanel>
</Button>
<Button>
<StackPanel>
<TextBlock>Button 3</TextBlock>
</StackPanel>
</Button>
</WrapPanel>
<WrapPanel>
<WrapPanel.Resources>
<Style BasedOn="{StaticResource SquareButtonSmallStyle}" TargetType="Button" />
</WrapPanel.Resources>
<Button>
<StackPanel>
<TextBlock>Button 1</TextBlock>
</StackPanel>
</Button>
<Button>
<StackPanel>
<TextBlock>Button 2</TextBlock>
</StackPanel>
</Button>
<Button>
<StackPanel>
<TextBlock>Button 3</TextBlock>
</StackPanel>
</Button>
</WrapPanel>
<WrapPanel>
<WrapPanel.Resources>
<Style BasedOn="{StaticResource SquareButtonLargeStyle}" TargetType="Button" />
</WrapPanel.Resources>
<Button>
<StackPanel>
<TextBlock>Button 1</TextBlock>
</StackPanel>
</Button>
<Button>
<StackPanel>
<TextBlock>Button 2</TextBlock>
</StackPanel>
</Button>
<Button>
<StackPanel>
<TextBlock>Button 3</TextBlock>
</StackPanel>
</Button>
</WrapPanel>
</StackPanel>
</Window>
Update
Based Upon a few answers I came up with a solution, but I don't particularly like it. It seems messy.
I now have two keyed styles. One for the StackPanel and one for the TextBlock.
MainWindow.Xaml stays the same but SquaredButtonStyle.xaml now has:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style TargetType="TextBlock" x:Key="SpecialTextBlockStyle">
<Setter Property="TextAlignment" Value="Center" />
<Setter Property="Foreground" Value="Red" />
<Setter Property="TextWrapping" Value="Wrap" />
</Style>
<Style TargetType="StackPanel" x:Key="MediumNestedStackPanel">
<Style.Resources>
<Style TargetType="TextBlock" BasedOn="{StaticResource SpecialTextBlockStyle}" />
</Style.Resources>
</Style>
<Style TargetType="Button" x:Key="SquareButtonMediumStyle">
<Setter Property="Margin" Value="5" />
<Setter Property="Width" Value="80" />
<Setter Property="Height" Value="80" />
<Style.Resources>
<Style TargetType="StackPanel" BasedOn="{StaticResource MediumNestedStackPanel}">
</Style>
</Style.Resources>
</Style>
<Style BasedOn="{StaticResource SquareButtonMediumStyle}" TargetType="Button" x:Key="SquareButtonSmallStyle">
<Setter Property="Width" Value="55" />
<Setter Property="Height" Value="55" />
<Style.Resources>
<!--
Implicit stack panel style in the scope of SquareButtonMediumStyle
inherits from MediumNestedStackPanel, adds stuff
-->
<Style TargetType="StackPanel" BasedOn="{StaticResource MediumNestedStackPanel}">
<Style.Resources>
<Style TargetType="TextBlock" BasedOn="{StaticResource SpecialTextBlockStyle}" >
<Setter Property="FontSize" Value="10" />
</Style>
</Style.Resources>
</Style>
</Style.Resources>
</Style>
<Style BasedOn="{StaticResource SquareButtonMediumStyle}" TargetType="Button" x:Key="SquareButtonLargeStyle">
<Setter Property="Width" Value="100" />
<Setter Property="Height" Value="100" />
<Style.Resources>
<Style TargetType="StackPanel" BasedOn="{StaticResource MediumNestedStackPanel}">
<Style.Resources>
<Style TargetType="TextBlock" BasedOn="{StaticResource SpecialTextBlockStyle}">
<Setter Property="FontSize" Value="14" />
</Style>
</Style.Resources>
</Style>
</Style.Resources>
</Style>
</ResourceDictionary>
Those nested StackPanel styles aren't going to be involved in the BasedOn inheritance for their containing styles. If you want to do that, you'll have to set up a separate inheritance hierarchy for the nested styles:
<Style TargetType="StackPanel" x:Key="MediumNestedStackPanel">
<Setter Property="Orientation" Value="Vertical" />
<Style.Resources>
<Style TargetType="fa:ImageAwesome">
<Setter Property="Height" Value="35" />
<Setter Property="Margin" Value="5" />
</Style>
<Style TargetType="TextBlock">
<Setter Property="FontSize" Value="12" />
<Setter Property="TextAlignment" Value="Center" />
<Setter Property="Foreground" Value="Red"></Setter>
<Setter Property="TextWrapping" Value="Wrap" />
</Style>
</Style.Resources>
</Style>
<Style TargetType="Button" x:Key="SquareButtonMediumStyle">
<Setter Property="Margin" Value="5" />
<Setter Property="Width" Value="80" />
<Setter Property="Height" Value="80" />
<Style.Resources>
<!--
Implicit stack panel style in the scope of SquareButtonMediumStyle
inherits from MediumNestedStackPanel, adds nothing
-->
<Style
TargetType="StackPanel" BasedOn="{StaticResource MediumNestedStackPanel}"
/>
</Style.Resources>
</Style>
<Style BasedOn="{StaticResource SquareButtonMediumStyle}" TargetType="Button" x:Key="SquareButtonSmallStyle">
<Setter Property="Width" Value="55" />
<Setter Property="Height" Value="55" />
<Style.Resources>
<!--
Implicit stack panel style in the scope of SquareButtonMediumStyle
inherits from MediumNestedStackPanel, adds stuff
-->
<Style TargetType="StackPanel" BasedOn="{StaticResource MediumNestedStackPanel}">
<Style.Resources>
<Style TargetType="fa:ImageAwesome" BasedOn="{StaticResource {x:Type fa:ImageAwesome}}">
<Setter Property="Height" Value="20" />
</Style>
<Style TargetType="TextBlock" BasedOn="{StaticResource {x:Type TextBlock}}">
<Setter Property="FontSize" Value="10" />
</Style>
</Style.Resources>
</Style>
</Style.Resources>
</Style>
<Style BasedOn="{StaticResource SquareButtonMediumStyle}" TargetType="Button" x:Key="SquareButtonLargeStyle">
<Setter Property="Width" Value="100" />
<Setter Property="Height" Value="100" />
<Style.Resources>
<Style TargetType="StackPanel" BasedOn="{StaticResource MediumNestedStackPanel}">
<Style.Resources>
<Style TargetType="fa:ImageAwesome" BasedOn="{StaticResource {x:Type fa:ImageAwesome}}">
<Setter Property="Height" Value="60" />
<Setter Property="Margin" Value="5" />
</Style>
<Style TargetType="TextBlock" BasedOn="{StaticResource {x:Type TextBlock}}">
<Setter Property="FontSize" Value="14" />
</Style>
</Style.Resources>
</Style>
</Style.Resources>
</Style>
Instead of defining an implicit TextBlock style inside the Button style(s), you should set the FontSize, Foreground, ... properties of the Button element itself:
<Style TargetType="Button" x:Key="SquareButtonMediumStyle">
<Setter Property="Margin" Value="5" />
<Setter Property="Width" Value="80" />
<Setter Property="Height" Value="80" />
<Setter Property="FontSize" Value="12" />
<Setter Property="TextBlock.TextAlignment" Value="Center" />
<Setter Property="Foreground" Value="Red"></Setter>
<Setter Property="TextBlock.TextWrapping" Value="Wrap" />
<Style.Resources>
<Style TargetType="StackPanel">
<Setter Property="Orientation" Value="Vertical" />
</Style>
</Style.Resources>
</Style>
<Style BasedOn="{StaticResource SquareButtonMediumStyle}" TargetType="Button" x:Key="SquareButtonLargeStyle">
<Setter Property="Width" Value="100" />
<Setter Property="Height" Value="100" />
<Setter Property="FontSize" Value="14" />
</Style>
Your current approach of using implicit styles for the TextBlocks won't work as you have already discovered. Please refer to the following link for more information about why:
Wpf, style is not being applied

Default style for TextBlock not being picked up after applying a style key

I have a <ResourceDictionary> containing this:
<Style TargetType="TextBlock">
<Setter Property="FontFamily" Value="..\..\Fonts\#Roboto"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="TextWrapping" Value="Wrap"/>
</Style>
This works fine.
Now I've added another style:
<Style x:Key="MyText" TargetType="{x:Type TextBlock}">
<Setter Property="Foreground" Value="Red"/>
<Setter Property="FontWeight" Value="SemiBold"/>
</Style>
However, after changing my <TextBlock> to <TextBlock Style="{StaticResource MyText}"/> - only the styles within the 2nd style block are picked up. E.g. the FontFamily, FontSize and TextWrapping are now ignored.
How can I have a default for a TextBlock, and then add to it? I don't want to add an x:Key to the 'default' style as this is in use throughout the system already.
I think you just need to base your keyed style on the type. See example below.
Note the BasedOn="{StaticResource {x:Type TextBlock}}"
<Window.Resources>
<Style TargetType="TextBlock">
<Setter Property="FontSize" Value="14"/>
<Setter Property="Foreground" Value="Green" />
<Setter Property="TextWrapping" Value="Wrap"/>
</Style>
<Style x:Key="MyText" TargetType="TextBlock">
<Setter Property="Foreground" Value="Red"/>
<Setter Property="FontWeight" Value="SemiBold"/>
</Style>
<Style x:Key="MyAppendedStyles" TargetType="TextBlock" BasedOn="{StaticResource {x:Type TextBlock}}">
<Setter Property="Foreground" Value="Red"/>
<Setter Property="FontWeight" Value="SemiBold"/>
</Style>
</Window.Resources>
<StackPanel>
<TextBlock Text="Hello" />
<TextBlock Style="{StaticResource MyText}" Text="Style Key" />
<TextBlock Style="{StaticResource MyAppendedStyles}" Text="Style Key" />
</StackPanel>

Style inheritance issue mixing types

I'm trying to factorize some styles with common properties.
So I'm using style inheritance but seems like it's not fully implemented.
Say I have these two styles:
<Style TargetType="ProgressBar">
<Setter Property="Height" Value="50"></Setter>
<Setter Property="Margin" Value="10"></Setter>
</Style>
<Style TargetType="Rectangle">
<Setter Property="Height" Value="50"></Setter>
<Setter Property="Margin" Value="10"></Setter>
</Style>
Here is my first try:
<Style x:Key="BaseStyle" TargetType="FrameworkElement">
<Setter Property="Height" Value="50"></Setter>
<Setter Property="Margin" Value="10"></Setter>
</Style>
<Style BasedOn="{StaticResource BaseStyle}" TargetType="ProgressBar"></Style>
<Style BasedOn="{StaticResource BaseStyle}" TargetType="Rectangle"></Style>
It's running but the Visual Studio designer is not happy and it displays this error:
Can only base on a Style with target type that is base type of this style's target type.
AFAIK if I trust MSDN both ProgressBar and Rectangle should inherit from FrameworkElement.
Here is the full code:
<StackPanel>
<StackPanel.Resources>
<!--<Style TargetType="ProgressBar">
<Setter Property="Height" Value="50"></Setter>
<Setter Property="Margin" Value="10"></Setter>
</Style>
<Style TargetType="Rectangle">
<Setter Property="Height" Value="50"></Setter>
<Setter Property="Margin" Value="10"></Setter>
</Style>-->
<Style x:Key="BaseStyle" TargetType="FrameworkElement">
<Setter Property="Height" Value="50"></Setter>
<Setter Property="Margin" Value="10"></Setter>
</Style>
<Style BasedOn="{StaticResource BaseStyle}" TargetType="ProgressBar"></Style>
<Style BasedOn="{StaticResource BaseStyle}" TargetType="Rectangle"></Style>
</StackPanel.Resources>
<ProgressBar Value="50"></ProgressBar>
<ProgressBar IsIndeterminate="True"></ProgressBar>
<Rectangle Width="100" Fill="BlueViolet"></Rectangle>
</StackPanel>
Note that the exact same code is working fine with WPF both in the designer and at runtime.
Is this a VS bug or a WinRT limitation?
Am I doing something wrong?

Can I override/replace a property of a child-item in a default style

The default wpf MenuItem (on Menu) is constructed out of controls approx. like this:
grid; outer-rectangle; bg-rectangle; inner-rectangle; dockpanel; popup.
The dockpanel in turn consists of:
contentpresenter[icon]; path; contentpresenter[text]
The contentpresenter[text] consists of a TextBlock control.
What I want to achieve is to define a Style, as simple as possible, to change the VerticalAlignment property of this TextBlock, but only for the TextBlock in MenuItem, not in general.
<Style x:Key ="TextBlockCenterStyle" TargetType="{x:Type TextBlock}">
<Setter Property="VerticalAlignment" Value="Center" />
</Style>
<Style TargetType="MenuItem">
<Setter Property="FontSize" Value="11" />
<Setter Property="ItemContainerStyle" Value="TextBlockCenterStyle" />
<Style.Resources>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="VerticalAlignment" Value="Center" />
</Style>
</Style.Resources>
</Style>
I tried Style.Resources and ItemContainerStyle.
Cannot get it to work. ItemContainerStyle throws TargetInvocationException (from NullReferenceException) at run-time.
When it is possible it should be a general solution, something like FindChildControl?!
Did you try ItemContainerStyle?
Something like:
<MenuItem ItemContainerStyle = {StaticResource MyItemContainerStyle}../>
Then the MyItemContainerStyle have your
<Style x:Key ="MyItemContainerStyle" TargetType="{x:Type TextBlock}">
<Setter Property="VerticalAlignment" Value="Center" />
</Style>
=============== answer after EDIT ======================
try this:
<Style TargetType="MenuItem">
<Setter Property="FontSize" Value="11" />
<Setter Property="ItemContainerStyle" Value="{StaticResource TextBlockCenterStyle}" />
<Style.Resources>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="VerticalAlignment" Value="Center" />
</Style>
</Style.Resources>
</Style>

Override style in style

Is it possible to override styles in other styles. My best description will be some non working code:
<Style x:Key="SpecialFont" TargetType="Label">
<Setter Property="Foreground" Value="Red" />
<Setter Property="FontSize" Value="28" />
</Style>
<Style TargetType="GroupBox">
<Setter Property="GroupBox.Resources">
<Setter.Value>
<Style x:Key="SpecialFont" TargetType="Label">
<Setter Property="FontSize" Value="74" />
</Style>
</Setter.Value>
</Setter>
</Style>
The idea is that I will define a style to my "special text", the font which by default is red and have a size at 28, but if the label is placed in a groupbox it should have the size at 74, but maintain the red color. How is this possible? I would prefer to have to same style-key in my xaml, and not create a style based on another, e.g. SpecialFontBig based on SpecialFont.
Edit:
Okay... Another explanation.
I want result like this:
<Style x:Key="BaseFont" TargetType="Label">
<Setter Property="Foreground" Value="White" />
</Style>
<Style x:Key="Font1" TargetType="Label" BasedOn="{StaticResource BaseFont}">
<Setter Property="FontSize" Value="10" />
</Style>
<Style x:Key="Font2" TargetType="Label" BasedOn="{StaticResource BaseFont}">
<Setter Property="FontSize" Value="20" />
</Style>
<Style x:Key="Font3" TargetType="Label" BasedOn="{StaticResource BaseFont}">
<Setter Property="FontSize" Value="30" />
</Style>
<Style x:Key="Font1Red" TargetType="Label" BasedOn="{StaticResource Font1}">
<Setter Property="Foreground" Value="Red" />
</Style>
<Style x:Key="Font2Red" TargetType="Label" BasedOn="{StaticResource Font2}">
<Setter Property="Foreground" Value="Red" />
</Style>
<Style x:Key="Font3Red" TargetType="Label" BasedOn="{StaticResource Font3}">
<Setter Property="Foreground" Value="Red" />
</Style>
Where FontX is used outside my groupboxes, and FontXRed is used inside them. Is it possible to overrule this foreground, without making a lot of FontXRed styles? For example something like:
<Style x:Key="BaseFont" TargetType="Label">
# IF INSIDE A GROUPBOX
<Setter Property="Foreground" Value="Red" />
# ELSE
<Setter Property="Foreground" Value="White" />
</Style>
Styles can be based on other styles -> http://msdn.microsoft.com/en-us/library/system.windows.style.basedon.aspx
<Style x:Key="Style1">
<Setter Property="Control.Background" Value="Yellow"/>
</Style>
<Style x:Key="Style2" BasedOn="{StaticResource Style1}">
<Setter Property="Control.Foreground" Value="Blue"/>
</Style>
I have created a new GroupBox, which solved my problem okay:
class MyGroupBox : GroupBox
{
public MyGroupBox()
{
var newForegroundSetter = new Setter(ForegroundProperty, Brushes.Black);
var stylesToUpdate = new List<string>
{
"TextBlockShared",
"SmallFontTextBlock",
"MediumFontTextBlock",
"LargeFontTextBlock",
"FontControlShared",
"SmallFontControl",
"SmallFontHeaderControl",
"MediumFontControl",
"MediumFontHeaderControl",
"LargeFontControl",
"LargeFontHeaderControl",
"SmallButton",
"MediumButton",
"LargeButton",
};
foreach (var styleKey in stylesToUpdate)
{
var existingStyle = FindResource(styleKey) as Style;
if (existingStyle == null) continue;
var newStyle = new Style(existingStyle.TargetType, existingStyle);
newStyle.Setters.Add(newForegroundSetter);
Resources.Add(styleKey, newStyle);
}
}
}
In case someone stumbles upon this, here is the trick that usually works for me.
Basically, I define a style inside another styles resources. I usually base the inner style on a key-referenced style to use it in other places, but you could place a plain simple style in there as well.
<Style x:Key="SpecialFont" TargetType="Label">
<Setter Property="Foreground" Value="Red" />
<Setter Property="FontSize" Value="28" />
</Style>
<Style TargetType="GroupBox">
<Style.Resources>
<Style TargetType="Label"
BasedOn="{StaticResource SpecialFont}" />
</Style.Resources>
</Style>

Resources