WPF global font size - wpf

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)}

Related

WPF Styles based on parent styles

Suppose I have a WPF style for a container element such as a grid which applies styles to its child items automatically, like this:
<Window.Resources>
<Style TargetType="Grid" x:Key="FormStyle">
<Style.Resources>
<Style TargetType="Label">
<Setter Property="FontSize" Value="50"/>
</Style>
</Style.Resources>
</Style>
</Window.Resources>
How can I then override certian elements of that style within the grid itself? For example suppose I wanted one grid to have FormStyle but also have a blue label, like this (which doesnt work):
<!-- this works fine and Label size = 50 -->
<Grid Style="{StaticResource FormStyle}">
<Label Content="Blah"/>
</Grid>
<!-- But this doesnt, label is blue, but normal font size -->
<Grid Style="{StaticResource FormStyle}">
<Grid.Resources>
<Style TargetType="Label" BasedOn="{StaticResource {x:Type Label}}">
<Setter Property="Foreground" Value="Blue"/>
</Style>
</Grid.Resources>
<Label Content="Blah"/>
</Grid>
I am expecting the BasedOn={StaticResource {x:Type Label}} to refer to the current active style for Labels at the current scope - i.e. the label style within FormStyle. But it clearly doesnt and refers to the base outer label style.
If I do for instance this globally
<Style TargetType="Label">
<Setter Property="FontSize" Value="50"/>
</Style>
Then it is all fine.
I could of course just name the styles, but surly there must be an easier/less verbose way?
Thanks
Here is the lookup process for Static Resources:
The lookup process checks for the requested key within the resource
dictionary defined by the element that sets the property.
The lookup process then traverses the logical tree upward to the
parent element and its resource dictionary. This process continues
until the root element is reached.
App resources are checked. App resources are those resources within
the resource dictionary that is defined by the Application object
for your WPF app.
In your case to resolve BasedOn="{StaticResource {x:Type Label}}" WPF first looks in the ResourceDictionary defined inside of Grid, then in Window - the logical parent of the Grid - and it's Resources, and then finally in Application level resources. WPF will not find it anywhere - and defaults to the base style - due to the style being a nested style in FormStyle.
Read further about Static Resource Lookup Behavior on Docs.
To get the desired output, you could:
1) Move your Label style out of FormStyle and in to Window.Resoruces
2) Merge the Label style from FormStyle into the Label style defined in the Grid element.
<Grid Style="{StaticResource FormStyle}">
<Grid.Resources>
<Style TargetType="Label">
<Setter Property="Foreground" Value="Red"/>
<Setter Property="FontSize" Value="50"/>
</Style>
</Grid.Resources>
<Label Content="Blah"/>
</Grid>
3) Change your FormStyle not to have a nested style for Label, but to have setters for Label properties.
<Window.Resources>
<Style TargetType="Grid" x:Key="FormStyle">
<Setter Property="Label.FontSize" Value="50"/>
</Style>
</Window.Resources>

WPF Stackpanel spacing between custom controls

how is it possible to get a space between some custom controls inside a stackpanel? I did it right before with a Textbox, Button, and so on, but i cannot do it with a custom control.
That's the code i've got so far
<Grid>
<StackPanel x:Name="spTasks" CanVerticallyScroll="True">
<StackPanel.Resources>
<Style TargetType="local:SmartTaskOverview">
<Setter Property="Margin" Value="50,50,50,50" />
</Style>
</StackPanel.Resources>
</StackPanel>
</Grid>
Thanks for your help
FrameworkElements and their sub-classes don't just look for a resource using the controls type, they use the value of DefaultStyleKey. It's common practice for most sub-classes of Control (and some other FrameworkElements) to override the default value of this dependency property in the static constructor to be the type of the control, but sub-classes of UserControl usually don't bother.
static Foo()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(Foo), new FrameworkPropertyMetadata(typeof(Foor));
}
If you didn't do this in your SmartTaskOverview then it will be looking for its default style using typeof(UserControl) as the resource key and not typeof(SmartTaskOverview).
Note: The UserControl will require a control template to show its children, this is normally provided by the default style for UserControl but by changing the key it will find your default style instead. To resolve this, just base your style on the UserControl style.
<Style TargetType="local:SmartTaskOverview" BasedOn="{StaticResource {x:Type UserControl}}">
<Setter Property="Margin" Value="50,50,50,50" />
</Style>
Alternatively you could provide a simple template yourself.
<Style TargetType="local:SmartTaskOverview">
<Setter Property="Margin" Value="50,50,50,50" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:SmartTaskOverview}">
<ContentPresenter />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

Create Style for a StackPanel and its Contents

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>

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>

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

Resources