Create Style for a StackPanel and its Contents - wpf

I want to create a style for Stackpanels using a key. This style will be in a global ResourceDictionary for use everywhere in the application. But it should not only set the style of the Stackpanel, but also its content.
I want to use the stackpanel like this:
<StackPanel Style="{StaticResource GlobalStackPanelStyle}">
<Button>test</Button> <-- the button style should also be defined due to the Stackpanel style
</StackPanel>
How should the Resourcedictionary look like if for example all the containing buttons should be green?

You would have something like this in your resource dictionary:
<Style TargetType="StackPanel" x:Key="GlobalStackPanelStyle">
<Style.Resources>
<Style TargetType="Button">
<Setter Property="Background" Value="Green" />
</Style>
</Style.Resources>
</Style>

Related

Stop child from inheriting parent Style in TabControls

In my WPF application i have a TabControl that i am binding to a style i created:
On my View:
<TabControl Grid.Row="6" Style="{DynamicResource SideBarTabControl}">
On a separate ResourceDictionary:
<Style x:Key="SideBarTabControl" TargetType="{x:Type TabControl}" BasedOn="{StaticResource {x:Type TabControl}}" >
<Setter Property="FontSize" Value="{DynamicResource TitleFontSize}"/>
</Style>
So far so good, things work as expected. The problem is that now all the children of this TabControl, such as a ListView inside a TabItem, is also getting the same FontSize as the TabControl, instead of the default.
I thought that by specifying TargetType="{x:Type TabControl}" i would stop the style from being applied to children of different types. What i'm looking for is to actually stop it from affecting EVERYTHING BUT the component that explicitly inherited the style. So how can this be done? I think i am missing something simple...
If i override the font size in my ListView it works, but this means i have to do it for every child, which might become very cumbersome.
I have read this and other questions but i can't find the answer i'm looking for:
Is it possible to set a style in XAML that selectively affects controls?
This is working for me. The part that's doing the work is TabControl.ItemContainerStyle. It applies a font size only to the header content.
<TabControl>
<TabControl.ItemContainerStyle>
<Style
TargetType="TabItem"
BasedOn="{StaticResource {x:Type TabItem}}"
>
<Setter Property="HeaderTemplate">
<Setter.Value>
<DataTemplate>
<ContentControl
TextElement.FontSize="20"
Content="{Binding Header, RelativeSource={RelativeSource AncestorType=TabItem}}"
/>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</TabControl.ItemContainerStyle>
<TabItem Header="Foo">
<Label Content="Bar" />
</TabItem>
<TabItem Header="Baz">
<Label Content="Bar" />
</TabItem>
</TabControl>
You cannot stop it, it's not the style causing this unwanted trickle-down effect you want rid of; it's just how WPF controls work.
What you will have to do to stop this is write another style for your tab items to intercept the one being inherited from the TabControl.
I suggest writing this style inside your existing TabControl style, inside the Style.Resources tag like so:
<Style x:Key="SideBarTabControl" TargetType="{x:Type TabControl}" BasedOn="{StaticResource {x:Type TabControl}}" >
<Style.Resources>
<Style TargetType="{x:Type TabItem}">
<Setter Property="FontSize" Value="9001"/>
<!-- Any other setters you want for TabItems -->
</Style>
</Style.Resources>
<Setter Property="FontSize" Value="{DynamicResource TitleFontSize}"/>
</Style>
By making a style inside your other style's resources, it will be carried with it, and by not specifying any x:Key for the TabItem style - it will apply it to any TabItem not ordered to have a specific style, becoming the default style for any TabItem you make inside the TabControl now.

How to extend instead of overriding WPF Styles

I want to use custom theme in my application and as far as I know I can accomplish this by using resource dictionary and referencing it in App.xaml. Styles would override the defaults like this:
<Style TargetType="{x:Type Label">
<Setter Property="Foreground" Value="Green" />
</Style>
Now as I guess the default Label style is overriden with same values but all my label fonts are green. The problem starts when I want to style one label somewhere again. When I want to change some other property in my Grid like this
<Grid.Resources>
<Style TargetType="{x:Type Label">
<Setter Property="FontSize" Value="28" />
</Style>
</Grid.Resources>
All labels inside my grid are losing their foreground color and have default one again (didn't I override defaults in previous step?). After some tries I found out that to do this properly i have to add another property to Style declaration BasedOn={StaticResource {x:Type Label}}" and it works. This is kind of weird for me because now I will have to repeat same BasedOn code in whole app and this is not how styling works - this should be done automatically! For example in HTML + CSS styles are inherited and merged and in WPF they are replaced...
Notice that when I don't use any styles controls still get their look from somehwere (System Themes?). How can I tell them to look for defaults somewhere else so without any additional code on styles they will think that they should be green by default?
Is there any way I can automate setting BasedOn property? Or maybe there is a better to do this overally?
I had the same problem. I used Zack's answer and improved it like following so if you don't specify a style the overridden default is still taken in account. It's basically what you would have done but just once in the ResourceDictionary.
<Window x:Class="TestWpf.RandomStuffWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Random Stuff Window">
<Window.Resources>
<ResourceDictionary>
<!-- Default Label style definition -->
<Style TargetType="{x:Type Label}">
<Setter Property="Foreground" Value="Green" />
</Style>
<!-- Extending default style -->
<Style TargetType="{x:Type Label}"
x:Key="LargeGreenForegroundLabel"
BasedOn="{StaticResource {x:Type Label}}">
<Setter Property="FontSize" Value="28" />
</Style>
</ResourceDictionary>
</Window.Resources>
<StackPanel>
<Button Click="Button_Click">Click</Button>
<Label Content="GreenForegroundLabel" /> <!-- Uses default style -->
<Label Style="{StaticResource LargeGreenForegroundLabel}"
Content="LargeGreenForegroundLabel" />
</StackPanel>
</Window>
Wpf has different levels of styles, that are applied in order of global > local. A style set directly on a control will override a style set globally, like in your example. I was trying to find a list of all the different places that a control looks for its styles but I cannot find one at the moment. As far as I know, you will have to use the BasedOn property to inherit a style and not completely override the properties of that style with the style you set locally.
Here is an example of a resource dictionary that has styles based on another style, so that you don't have do repeat the BasedOn binding over and over, you can just set the style on the specific element you want to have that style.
<Window x:Class="TestWpf.RandomStuffWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Random Stuff Window">
<Window.Resources>
<ResourceDictionary>
<Style TargetType="{x:Type Label}"
x:Key="GreenForegroundLabel">
<Setter Property="Foreground" Value="Green" />
</Style>
<Style TargetType="{x:Type Label}"
x:Key="LargeGreenForegroundLabel"
BasedOn="{StaticResource GreenForegroundLabel}">
<Setter Property="FontSize" Value="28" />
</Style>
</ResourceDictionary>
</Window.Resources>
<StackPanel>
<Button Click="Button_Click">Click</Button>
<Label Style="{StaticResource GreenForegroundLabel}"
Content="GreenForegroundLabel" />
<Label Style="{StaticResource LargeGreenForegroundLabel}"
Content="LargeGreenForegroundLabel" />
</StackPanel>
</Window>

How to define all textblock elements the same color

We are using global styles definitions for most of the types. We define then in the app.xaml file. When using TextBlock it is a problem to define a foreground color because it changes all the controls using TextBlock (Button's content color for example).
How can we define a global style which will act only on specific TextBlock usages?
current problematic usage:
<Style TargetType={x:Type TextBlock}>
<Setter Property="Foreground" Value="Red"/>
</Style>
Since I don't think there is a way to differentiate “your” TextBlocks and those that are part of other controls, your options are quite limited.
You could create named Style and add Style="{StaticResource coloredTextBlock}" or Foreground="{StaticResource textBlockColor}" to all TextBlocks. This would be quite tedious and non-DRY.
You could create your own type that inherits from TextBlock and style that. This has some of the disadvantages of the above solution (you have to remember doing that). But it has much less repetition.
This is because ContentPresenter creates a TextBlock for a string content, and since that TextBlock isn't in the visual tree, it will lookup to Application level resource. And if you define a style for TextBlock at Application level, then it will be applied to these TextBlock within ControlControls.
A workaround is to define a DataTemplate for System.String, where we can explicitly use a default TextBlock to display the content. You can place that DataTemplate in the same dictionary you define the TextBlock style so that this DataTemplate will be applied to whatever ContentPresenter effected by your style.
Add this to your Application resources and it should work for you -
<DataTemplate DataType="{x:Type system:String}">
<TextBlock Text="{Binding}">
<TextBlock.Resources>
<Style TargetType="{x:Type TextBlock}"/>
</TextBlock.Resources>
</TextBlock>
</DataTemplate>
Declare a namespace in your xaml, if not referred already -
xmlns:system="clr-namespace:System;assembly=mscorlib"
EDIT : Check this sample where its working..
<Style TargetType="{x:Type TextBlock}">
<Setter Property="Foreground" Value="Red"/>
</Style>
<DataTemplate DataType="{x:Type system:String}">
<TextBlock Text="{Binding}">
<TextBlock.Resources>
<Style TargetType="{x:Type TextBlock}"/>
</TextBlock.Resources>
</TextBlock>
</DataTemplate>
<Style TargetType="{x:Type Button}">
<Setter Property="Foreground" Value="Yellow"/>
</Style>
<Style TargetType="{x:Type Label}">
<Setter Property="Foreground" Value="Blue"/>
</Style>
Just provide a x:key in the style, like:
<Style x:Key="stRedTextBlock" TargetType={x:Type TextBlock}>
<Setter Property="Foreground" Value="Red"/>
</Style>
and mention the key in the TextBlock control style, where ever you require this particular TextBlock style, like:
<TextBlock Name="textBlock1" Style="{StaticResource stRedTextBlock}" />

Why does x:Key unapply my TabItem Style

I am trying to make a custom style for a TabItem Header. I got it to work by accident.
this fails:
<Style TargetType="{x:Type TabItem}" x:Name="TabHeader3" x:Key="test">
but this works
<Style TargetType="{x:Type TabItem}" x:Name="TabHeader3">
What's going on?
The first Style you have defined is an "explicit" Style, so you must explicitly use it like so:
<TabItem Style="{StaticResource test}" />
The second Style you have defined is an "implicit" Style. So it will be applied to all TabItem controls below it in the visual/logical tree, or to all TabItem controls if it's defined in the application resources.
Your second Style is equivalent to:
<Style TargetType="{x:Type TabItem}" x:Name="TabHeader3" x:Key="{x:Type TabItem}">
So the key is the Type of the object to which it should be applied.
If a TabItem has a Style explicitly defined (like I show above), then any implicit Styles will not be used. Also, if you have two implicit Styles defined, then the closest one wins. So here:
<Window>
<Window.Resources>
<Style TargetType="{x:Type TabItem}">
<Setter Property="Background" Value="Red" />
</Style>
</Window.Resources>
<Grid>
<Grid.Resources>
<Style TargetType="{x:Type TabItem}">
<Setter Property="Background" Value="Blue" />
</Style>
</Grid.Resources>
...
<TabItem ... />
...
</Grid>
</Window>
The Blue Style will take precedence over the Red Style.
Finally, you generally don't need to include x:Name on your Styles.
If you add the style to a resource dictionary without a key then the style gets applied to all TabItems that are within the scope of the resource dictionary by default. If you add a Key to the style then you need to manually set the Style

WPF global font size

I'm creating a WPF app and I would like to know the best way to be able to change the font size for every element in the ui. Do I create a resource dictionary and set Styles to set the font size for all the controls I use?
What is the best practice?
I'd do it this way:
<Window.Resources>
<Style TargetType="{x:Type Control}" x:Key="baseStyle">
<Setter Property="FontSize" Value="100" />
</Style>
<Style TargetType="{x:Type Button}" BasedOn="{StaticResource baseStyle}"></Style>
<Style TargetType="{x:Type Label}" BasedOn="{StaticResource baseStyle}"></Style>
<Style TargetType="{x:Type TextBox}" BasedOn="{StaticResource baseStyle}"></Style>
<Style TargetType="{x:Type ListView}" BasedOn="{StaticResource baseStyle}"></Style>
<!-- ComboBox, RadioButton, CheckBox, etc... -->
</Window.Resources>
That way, if I want to change ALL the controls, I'd just have to change the "baseStyle" style, the rest would just inherit from it. (That's what BasedOn property those, you can also extend the base style if you create other setters inside of the inherited style)
FontSizeProperty is inherited from Parent Control. So you just need to change FontSize of your main window.
If you don't need dynamic behaviour this should work:
Add a style for Window to your ResourceDictionary
<Style TargetType="{x:Type Window}">
<Setter Property="FontSize" Value="15" />
</Style>
Apply the style to your main form (will not be applied implicit because its a derived type)
Style = (Style)FindResource(typeof (Window));
<Window> has a property FontSize.
So you can set desired fontsize in element if you want to change the fontsize in all the elements within that window.
<Window FontSize="12">
</Window>
Another option is to define the FontFamily and FontSize as resources.
<FontFamily x:Key="BaseFontFamily">Calibri</FontFamily>
<sys:Double x:Key="BaseFontSize">12</sys:Double>
That way you can use them in your setters.
Application.Current.MainWindow.FontSize = _appBodyFontSize;
This way you can change the Font Size at run time also.
TextElement.FontSize is an inherit property, which means you can simply set the font size at root element, and all the children elements will use that size (as long as you don't change them manually)
For any styles in WPF, you should have a separate resource dictionary that contains the styles for your app.
If you want to have a single Font Size that's reused throughout the app then just create a style for that font size. You can either give it a unique name/key to use explicitly or you can set a targetType that will transcend throughout the app.
Explicit Key:
<Style
x:Key="MyFontSize"
TargetType="TextBlock">
<Setter
Property="FontSize"
Value="10" />
</Style>
<Control
Style="{StaticResource MyFontSize}" />
*Note this style can be used with controls that have contentPresenters
For all textblocks in the app:
<Style
TargetType="TextBlock">
<Setter
Property="FontSize"
Value="10" />
</Style>
<TextBlock
Text="This text will be size 10" />
If you need to programmatically change global FontSize, not statically (XAML), to be applied once for all your windows, you can do:
TextElement.FontSizeProperty.OverrideMetadata(
typeof(TextElement),
new FrameworkPropertyMetadata(16.0));
TextBlock.FontSizeProperty.OverrideMetadata(
typeof(TextBlock),
new FrameworkPropertyMetadata(16.0));
This values are applied to any TextBlock, Labels and almost any text in any windows, whereas it has not a explicit FontSize defined. But this does not affect for TextBox, you have to write a similar code for it or any other special controls.
To dynamically change the font size globally with ctrl-mousewheel:
XAML:
<Window Name="MainWindow" ... PreviewMouseWheel="MainWindow_PreviewMouseWheel">
code behind:
private void MainWindow_PreviewMouseWheel(object sender, MouseWheelEventArgs e)
{
if ((Keyboard.Modifiers & ModifierKeys.Control) != 0)
{
if (e.Delta > 0)
++mainCtrl.FontSize;
if (e.Delta < 0 && mainCtrl.FontSize > 1)
--mainCtrl.FontSize;
}
}
Using Resources in XAML is the way to go. Although there are many great answers to this question, I would like to add my two cents to the SCOPE of the Resource.
For Global accessibility in all of the Windows and User Controls of the Project, you can have your resource in the App.xaml file
<Application.Resources>
<Style TargetType="{x:Type Control}" x:Key="GlobalFontSize">
<Setter Property="FontSize" Value="28"/>
</Style>
</Application.Resources>
For accessibility at a Window level, you can have your resource in your xaml file for Window
<Window.Resources>
<Style TargetType="{x:Type Control}" x:Key="GlobalFontSize">
<Setter Property="FontSize" Value="28"/>
</Style>
</Window.Resources>
You could even have it at a Control level, for example
<DockPanel.Resources>
<Style TargetType="{x:Type Control}" x:Key="GlobalFontSize">
<Setter Property="FontSize" Value="28"/>
</Style>
</DockPanel.Resources>
Let's have some BLACK MAGIC things:
Add a double resource into your Application resource
<Application.Resources>
<sys:Double xmlns:sys="clr-namespace:System;assembly=mscorlib" x:Key="GlobalFontSize">12</sys:Double>
</Application.Resources>
Add a static property in your App class
public static double GlobalFontSize
{
get => (double)Current.Resources["GlobalFontSize"];
set => Current.Resources["GlobalFontSize"] = value;
}
Use this resource any where you want by DynamicResource
FontSize="{DynamicResource GlobalFontSize}"
Access property App.GlobalFontSize in any way to change value, binding is okay!
App.GlobalFontSize = 20;
//Or
{Binding Path=(local:App.GlobalFontSize)}

Resources