Controls created with XamlReader.Parse don't inherit styles - wpf

In my application the user can type in HTML, which then gets converted to XAML. I then parse the XAML using the XamlReader.Parse method and add it to a FlowDocument.
For example, let's suppose I have the XAML for a paragraph stored in a string, and then I parse it and add it to a FlowDocument like so:
var xaml = #"<Paragraph Style=""{DynamicResource Big}"" xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation"">My paragraph</Paragraph>";
var paragraph = (Paragraph)XamlReader.Parse(xaml);
MyDocument.Blocks.Add(paragraph);
Notice that the paragraph has a Style specified. That Style is defined in the FlowDocument's Resources.
<RichTextBox>
<FlowDocument x:Name="MyDocument">
<FlowDocument.Resources>
<Style TargetType="{x:Type Paragraph}">
<Setter Property="Foreground"
Value="Red" />
</Style>
<Style TargetType="{x:Type Paragraph}"
BasedOn="{StaticResource {x:Type Paragraph}}"
x:Key="Big">
<Setter Property="FontSize"
Value="24" />
</Style>
</FlowDocument.Resources>
</FlowDocument>
</RichTextBox>
You can see that I've defined two styles. The first is an implicit style, and the second extends the first using the BasedOn attribute. When I dynamically add the Paragraph to the FlowDocument it does pick up the 'Big' Style. However, there's a caveat, it does not pick up the Red Foreground color of the implicit style. How can I make it pick up both?
This only seems to be a problem when I parse the XAML. If I just instantiate a new Paragraph object and add it to the FlowDocument, it does indeed pick up both styles.

Related

Set default Style for FrameworkElement is not applied to derived class

I am a beginner in XAML. I create a new default style in a ResourceDictionary.
<Style TargetType="FrameworkElement" BasedOn="{StaticResource {x:Type FrameworkElement}}">
<Setter Property="Margin" Value="5"/>
<Setter Property="VerticalAlignment" Value="Center"/>
</Style>
Then I import it to UserControl.Resources.
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/PathToStyle.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
What I expect is that it will be applied to all FrameworkElements used in that UserControl, but it is not.
What infomation am I missing here?
The problem is because TargetType is specific.
A style which you do not give a x:Key to automatically is given a key which is equivalent to it's type.
Hence your style:
<Style TargetType="FrameworkElement"
Actually has an x:Key given to it which is
<Style x:Key="{x:Type FrameworkElement}"
https://learn.microsoft.com/en-us/dotnet/api/system.windows.style.targettype?redirectedfrom=MSDN&view=netcore-3.1#System_Windows_Style_TargetType
A piece of ui which is "looking" for a style looks for a matching key to it's type using that x:Key. It does not search up it's inheritance chain as well.
EDIT:
You can prove this:
In a new wpf app, add a frameworkelement to you mainwindow and a style targets frameworkelement.
<Window.Resources>
<Style TargetType="FrameworkElement">
<Setter Property="Height" Value="10"/>
</Style>
</Window.Resources>
<Grid>
<local:TestSubClass/>
</Grid>
and
public class TestSubClass : FrameworkElement
Spin it up and take a look at the live visual tree > Properties.
The frameworkelement fills the grid and has an actualheight matching the grid's height.
No style is set on it.
Hence this is the situation where no style is set and inheritance is not working as claimed in another post.
Change that to a regular frameworkelement:
<Window.Resources>
<Style TargetType="FrameworkElement">
<Setter Property="Height" Value="10"/>
</Style>
</Window.Resources>
<Grid>
<FrameworkElement/>
</Grid>
Spin it up with an f5 and take a look at the live visual tree.
There is a style in the properties.
Actualheight is 10.
What I'm expecting is it will apply to all FrameworkElement used in That UserControl
By all FrameworkElements you probably mean all FrameworkElements including its derivatives. Applying the Style as implicit style works perfectly if you target a distinct control like a Button. However, it may not work as you expect, if you target a base type of a control, like FrameworkElement, because each derivative of that base type can have their own style applied explicitly or implicitly that breaks your anticipated behavior by:
Not being based on the style of the base type
Not being based on a style that is transitively based on the base type style
Overriding the properties of the base style
The essential misunderstanding here is that all styles of derivatives of a base type will base their style on the style of said base type, but this does not apply in general.
You can check this yourself by extracting the Style of any control via Visual Studio or Blend. For example, let's look at the Style of a Button. As you can see, it is not even based on FrameworkElement, so it will not apply your base style. Even if it would, there is a chance that it will override the Margin property itself.
<Style x:Key="ButtonStyle" TargetType="{x:Type Button}">
<Setter Property="FocusVisualStyle" Value="{StaticResource FocusVisual}"/>
<Setter Property="Background" Value="{DynamicResource PrimaryHueMidBrush}"/>
<Setter Property="BorderBrush" Value="{DynamicResource PrimaryHueMidBrush}"/>
<!-- ...other setters. -->
</Style>
Apart from that, you have to define your base type style before any other styles of derivatives, because you can only use BasedOn with StaticResource. Consequently, if the style of a derived control is already defined before your style, your base style will not be applied.
What infomation am I missing here?
The fact that there are default styles defined for more derived types such as for example Button and Control and that these will take precedence over and be applied instead of your custom FrameworkElement style.
Your approach of defining a single Style for all FrameworkElements won't work. You'll need to define an implicit type for each derived FrameworkElement type.

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>

What is the most economical way to implement your own window border and title bar?

I am pretty new to WPF and am sitting here with my book trying to figure out the best approach to this application.
The title bar is not part of the client area so I am making my own title bar.
Which way would it be easiest to make this into some sort of resource to apply to all new windows I create?
<Application.Resources>
<Style x:Key="WindowTheme">
<Setter Property="Window.WindowStyle" Value="None"/>
</Style>
<!--Would I create a user control here for the title bar/border and title bar buttons? Or would it be a style?-->
</Application.Resources>
In WPF, there are two ways to use styles: Named styles and typed styles. A named style has an x:Key="..." attribute. A typed style doesn't have a name, but a TargetType="..." attribute (Rem: Named styles can and very often do have a TargetType as well, so named styles and unnamed styles would be more precise). Typed styles automatically get applied to all controls in the scope, which are of type TargetType (not a derived type).
<Style TargetType="{x:Type Window}">
<Setter Property="Background" Value="Blue" />
</Style>
To create your own window, you can set it's template property to a UserControl in the style:
<Style TargetType="{x:Type Window}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
The professional way to implement the control template is to implement it 'from scratch', this means not using a UserControl which derives from Window. To do this, you define the visual tree of the Window, and use the WPF feature TemplateParts to define what part of your control template is responsible for what functionality of the window.
Here is a tutorial which describes pretty exactly what you want to do:
CodeProject tutorial

How to apply WPF styles based on top of another style without using BasedOn property

I am styling CellValuePresenter (From Infragistics) to give different look to Gid Lines and have defined a style (gridLineStyle) and applied to the Grid's CellValuePresenterStyle Property.
I have discovered that there are columns for which custom templates are defined by templating CellValuePrenter and the grid lines are not visible (as expected). I can make it work by applying BasedOn property as in
<Style x:Key="gridLineStyle" TargetType="ig:CellValuePresenter">
<Setter Property="BorderThickness" Value="0,0,1,1"/>
<Setter Property="BorderBrush" Value="{Binding Path=BorderBrushForAllCells,RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type pwc:CarbonBlotter}}}"/>
</Style>
<Style x:Key="anotherColumnStyle" TargetType="{x:Type ig:CellValuePresenter}" BasedOn="{StaticResource gridLineStyle}">
<Setter Property="Template">
....
<pwc:BaseXamDataGrid>
<pwc:BaseXamDataGrid.FieldSettings>
<ig:FieldSettings CellValuePresenterStyle="{StaticResource gridLineStyle}"
...
But there are many styles with custom templates, and just wondering whether I can define a style without using BasedOn property and yet inheriting default style
You can find the complete CellValuePresenter style definition in your infragistics installation folder under DefaultStyles\DataPresenter\DataPresenterGeneric_Express.xaml
You can copy that style into your App.xaml under Application.Resources, modify it as you wish and that should become your new default style for CellValuePresenter.

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