Xaml Styles inheritance not working as expected - wpf

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

Related

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

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" />

WPF ToolTip does not show part of the text

ToolTip reserves space for text but does not display it.
So displayed on Win XP and Win 7
Should be like that
ToolTip style:
<Style x:Key="{x:Type ToolTip}" TargetType="ToolTip">
<Setter Property="Padding" Value="5" />
<Setter Property="BorderThickness" Value="1" />
<Setter Property="BorderBrush" Value="{x:Static common:WPFColors.NESTED_FORMS_BORDER_BRUSH}"/>
<Setter Property="Background" Value="{x:Static settings:UIGeneratorBackgrounds.DEFAULT_BACKGROUND_WPF_BRUSH}"/>
<Setter Property="FontSize" Value="13" />
<Setter Property="Foreground" Value="{x:Static common:WPFColors.DESCRIPTION_FOREGROUND_BRUSH}" />
<Setter Property="HorizontalContentAlignment" Value="Left" />
<Setter Property="TextBlock.TextWrapping" Value="WrapWithOverflow" />
<Style.Resources>
<Style TargetType="ContentPresenter">
<Style.Resources>
<Style TargetType="TextBlock">
<Setter Property="TextWrapping" Value="{Binding RelativeSource={RelativeSource FindAncestor,AncestorType={x:Type ToolTip}},Path=(TextBlock.TextWrapping),Mode=OneWay}"/>
</Style>
</Style.Resources>
</Style>
</Style.Resources>
</Style>
XAML:
<grid:GridCell ToolTip="{Binding Path=PaymentTypeToolTip,Mode=OneWay}">...</grid:GridCell>
But everything works if I write like this:
<grid:GridCell>
<grid:GridCell.ToolTip>
<ToolTip Content="{Binding Path=PaymentTypeToolTip,Mode=OneWay}">
<ToolTip.Style>
<Style TargetType="ToolTip" BasedOn="{StaticResource {x:Type ToolTip}}">
<Setter Property="TextBlock.TextWrapping" Value="NoWrap"/>
</Style>
</ToolTip.Style>
</ToolTip>
</grid:GridCell.ToolTip>
...
</grid:GridCell>
This error appeared only on WinXP and Win7, only on one window, on one control, which works fine in other places.
Why could this be? Writing style to ToolTip and TextWrapping = NoWrap is not an option everywhere.

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>

how to use an application resource in user control xaml

I have a app.xml with some resources in it such as:
<Application.Resources>
<con:EnumToVisibilityConverter x:Key="EnumToVisibilityConverter" />
<con:NegateBoolConverter x:Key="NegateBoolConverter" />
<Style TargetType="FrameworkElement" x:Key="BaseStyle">
<Setter Property="HorizontalAlignment" Value="Stretch" />
<Setter Property="VerticalAlignment" Value="Stretch" />
<Setter Property="Margin" Value="3"/>
<Setter Property="Width" Value="120"/>
<Setter Property="Height" Value="25" />
</Style>
<Style TargetType="CheckBox" BasedOn="{StaticResource BaseStyle}">
</Style>
<Style TargetType="DatePicker" BasedOn="{StaticResource BaseStyle}">
</Style>
<Style TargetType="TextBox" BasedOn="{StaticResource BaseStyle}">
</Style>
<Style TargetType="Label" BasedOn="{StaticResource BaseStyle}">
</Style>
<Style TargetType="Button" BasedOn="{StaticResource BaseStyle}">
<Setter Property="Width" Value="75" />
<Setter Property="Height" Value="23" />
<Setter Property="Margin" Value="5,2,2,5" />
<Setter Property="HorizontalAlignment" Value="Left" />
<Setter Property="VerticalAlignment" Value="Top" />
</Style>
</Application.Resources>
I want to use them in my user control xaml as follow:
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="App.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
Since my user control xaml is in View directory, the above mentione syntax is not accepted by system and complained that it can not find view/app.xaml. How can I add path to the Source so it can find it?
You don't need to do that. Application resources are available all over the app, so all you need to do is either this in xaml:
Style="{StaticResource BaseStyle}"
Or this in C# (inside your control's code):
Style baseStyle = (Style)this.FindResource("BaseStyle");

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>

Resources