WPF - combine/link multiple styles into one - wpf

In my WPF application I want to style a data grid. For this I need several separate styles (sub styles), since they target different controls. Now I want to combine those into one style (main style) to set it in xml via <DataGrid Style="{StaticResource MyDataGridStyle}">
Pseudo code for sub styles:
<ResourceDictionary>
<!-- sub styles -->
<Style x:Key="RowStyle" TargetType="Row">...</Style>
<Style x:Key="ColumnStyle" TargetType="Column">...</Style>
<Style x:Key="ColumnHeaderStyle" TargetType="ColumnHeader">...</Style>
<Style x:Key="SomeInnerElementStyle" TargetType="SomeInnerElement">...</Style>
</ResourceDictionary>
Currently I'm combining the styles via
<!-- inside the ResourceDictionary -->
<!-- main style -->
<Style x:Key="MyDataGridStyle" TargetType="DataGrid">
<Style.Resources>
<Style BasedOn="{StaticResource RowStyle}" TargetType="Row" />
... all other sub styles go here
</Style.Resources>
</Style>
Is there a more elegant way of including the sub styles in my main style? The BasedOn attribute forces me to specify the TargetType again, which is a pain and also I do not want to include "a new style based on the sub style" but just "the sub style".

Set the corresponding Style properties:
<Style x:Key="MyDataGridStyle" TargetType="DataGrid">
<Setter Property="RowStyle" Value="{StaticResource RowStyle}" />
<Setter Property="ColumnStyle" Value="{StaticResource ColumnStyle}" />
<Setter Property="ColumnHeaderStyle" Value="{StaticResource ColumnHeaderStyle}" />
</Style>

Related

How to make an exception for Application.Resources style in a specific Grid.Resources

I am doing small WPF app for my own using Visual Studio, C#, .NET Standard and WPF in this specific project.
I have defined style for all TextBlocks and TextBoxes in Applications.Resources like below.
<Application.Resources>
<Style TargetType="TextBox">
<Setter Property="FontSize" Value="10"/>
</Style>
<Style TargetType="TextBlock">
<Setter Property="FontSize" Value="10"/>
</Style>
</Application.Resources>
Then in main window I have a grid which contains some buttons.
<Grid>
<Grid.Resources>
<Style TargetType="Button">
<Setter Property="FontSize" Value="50" />
</Style>
<Style TargetType="TextBlock">
<Setter Property="FontSize" Value="50"/>
</Style>
</Grid.Resources>
<Button Grid.Column="0" Content="DASHBOARD" Command="local:CustomCommands.ShowDashboard"/>
</Grid>
I would like to set for the textblocks/textboxes in this specific buttons a wider font.
I tried for many different syntax but could not manage it. I tried also do define x:Key for this style in Grid.Resources and use it in this specific Button control. This wasn't work either.
Can anyone let me know which way should I let know my application that text in this buttons would have bigger font size?
The TextBlock created for string contents by the ContentPresenter inside the Button template doesn't apply the locally-defined resources, i.e. those in your Grid.
The easiest way to solve your problem would be to explicitly define a TextBlock as the Button's content.
<Grid>
<Grid.Resources>
<Style TargetType="TextBlock">
<Setter Property="FontSize" Value="50"/>
</Style>
</Grid.Resources>
<Button Grid.Column="0" Command="local:CustomCommands.ShowDashboard">
<TextBlock Text="DASHBOARD" />
</Button>
</Grid>

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 overriding ListViewItem style when use Material Design In XAML Toolkit

I installed Material Design In XAML Toolkit to my project. I have ListView which contains within itself GridView (with GridViewColumns) and i want to override styles for each row in this table. But in each case i lose styles from Material Design In XAML Toolkit.
I tried do several things:
1) Override existing styles based on target type:
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem" BasedOn="{StaticResource {x:Type ListViewItem}}">
<Setter Property="Background" Value="Green" />
</Style>
</ListView.ItemContainerStyle>
I got overriding styles, but in this case i lose type recognition in GridView (Columns contains correct headers, but values contains call result ToString() method my model)
2) I used concrete style from Material Design In XAML Toolkit - MaterialDesignGridViewItem:
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem" BasedOn="{StaticResource MaterialDesignGridViewItem">
<Setter Property="Background" Value="Green" />
</Style>
</ListView.ItemContainerStyle>
In this case i got work solution (it would seem), but when i do adding triggers instead , i lose material styles (got only color, without animations).
3) In other cases i lose all material styles and go back to wpf default styles.
Hope on our help.
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem" BasedOn="{StaticResource MaterialDesignListBoxItem">
<Setter Property="Background" Value="Green" />
</Style>
</ListView.ItemContainerStyle>
Instead of using MaterialDesignGridViewItem, your extended style should be based on MaterialDesignListBoxItem.
The same works for other items. This helped me with TreeViewItem's that used to be in the style of MaterialDesign, but were also overwritten until I added the BasedOn property.
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}" BasedOn="{StaticResource MaterialDesignTreeViewItem}">
<EventSetter Event="TreeViewItem.DragOver" Handler="treeView_DragOver"/>
<EventSetter Event="TreeViewItem.Drop" Handler="treeView_Drop"/>
<EventSetter Event="TreeViewItem.MouseMove" Handler="treeView_MouseMove"/>
<EventSetter Event="TreeViewItem.MouseLeftButtonDown" Handler="treeView_MouseDown"/>
<EventSetter Event="TreeViewItem.MouseRightButtonDown" Handler="treeView_MouseRightDown"/>
</Style>
</TreeView.ItemContainerStyle>
Please note a "}" is missing after "MaterialDesignListBoxItem", so that:
<Style TargetType="ListViewItem" BasedOn="{StaticResource MaterialDesignListBoxItem}">

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>

WPF - Use control inheritance for standard-styled controls?

I have a WPF application with many windows and user controls, and I'd like to implement standard styles for certain controls that appear throughout the application. As an example, say I need two standard TextBlocks throughout the application: one for large headings, one for small headings. And the only difference between them is the font size, say 36 and 24 respectively. All other properties (color, fontfamily, etc.) could be set by a TextBlock template or global TargetType="{x:Type TextBlock}" styles.
Of course I could create two global named styles that just set the font size and apply those staticresource styles liberally throughout the XAML to my TextBlocks, or at the highest possible level above the TextBlocks that would not interfere with other TextBlocks. But as an alternative, which would remove the requirement for setting the Style tag in many places, is inheriting from TextBlock is a good way to go?
TextBlock controls:
class TextBlockLargeHeading : TextBlock
{
public TextBlockLargeHeading()
{ }
}
class TextBlockSmallHeading : TextBlock
{
public TextBlockSmallHeading()
{ }
}
Global resource:
<Application.Resources>
<Style TargetType="TextBlock">
<Setter Property="Foreground" Value="Red" />
</Style>
<Style TargetType="MyApp:TextBlockLargeHeading" BasedOn="{StaticResource {x:Type TextBlock}}" >
<Setter Property="FontSize" Value="36" />
</Style>
<Style TargetType="MyApp:TextBlockSmallHeading" BasedOn="{StaticResource {x:Type TextBlock}}" >
<Setter Property="FontSize" Value="24" />
</Style>
</Application.Resources>
Then, to use them anywhere, simply reference the custom textblocks:
<StackPanel>
<MyApp:TextBlockLargeHeading Text="Large" />
<MyApp:TextBlockSmallHeading Text="Small" />
</StackPanel>
Which would create two Red TextBlocks with the appropriate font sizes.
Is this a reasonable approach? Are there any gotcha's if I've got 100's of instances of these, maintainability-wise or otherwise? Is there a better (safer or less code/XAML) approach? Perhaps using User Controls instead?
Thanks!
There's no reason to do all that. Create your styles and use them directly.
....
<Style x:Key="DefaultTextBlockStyle" TargetType="TextBlock">
<Setter Property="Foreground" Value="Red" />
<Setter Property="FontSize" Value="24" />
</Style>
<Style x:Key="LargeTextBlockStyle" TargetType="TextBlock" BasedOn="{StaticResource DefaultTextBlockStyle}">
<Setter Property="FontSize" Value="36" />
</Style>
<!-- Style applies to all TextBoxes -->
<Style TargetType="{x:Type TextBlock}" BasedOn="{StaticResource DefaultTextBlockStyle}" />
...
<StackPanel>
<TextBlock Text="Large" Style="{StaticResource LargeTextBlockStyle}"/>
<TextBlock Text="Small"/>
</StackPanel>

Resources