XAML Style only applied after second instance when using MergedDictionaries - c#

I've observed that when defining a custom button style inside a MergedDictionaries tag, the style is only applied on the second (and all others) instance of the control.
It does not happen when I omit the MergedDictionaries and proceeding ResourceDictionary tag.
Oddly, the Visual Studio (2015) designer shows my expected appearance - it's only at runtime that the error occurs.
<Window x:Class="WpfApplication5.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary>
<Style x:Key="mybutton" TargetType="{x:Type Button}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Border BorderBrush="Red" BorderThickness="1" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style BasedOn="{StaticResource mybutton}" TargetType="{x:Type Button}" />
</ResourceDictionary>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
<StackPanel>
<Button Height="100" />
<Button Height="50" />
</StackPanel>
</Window>
Note this is a completely blank template other than the XAML above:

You're not using the MergedDictionaries right, according to this link:
https://msdn.microsoft.com/en-us/library/system.windows.resourcedictionary.mergeddictionaries.aspx?f=255&MSPPError=-2147217396
"Dictionaries are merged by adding a ResourceDictionary to the generic collection referenced by MergedDictionaries. A merged ResourceDictionary does not have resource elements defined within it in markup. Instead, the merged dictionary is a ResourceDictionary with no markup child elements defined (or with no elements added through code), but with a URI specified for Source. "
You can easily write the styles like this:
<Window x:Class="WpfApplication5.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Window.Resources>
<Style x:Key="mybutton" TargetType="{x:Type Button}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Border BorderBrush="Red" BorderThickness="1" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style BasedOn="{StaticResource mybutton}" TargetType="{x:Type Button}" />
</Window.Resources>
<StackPanel>
<Button Height="100" />
<Button Height="50" />
</StackPanel>
</Window>

Related

Bind to a property in a ResourceDictionary

I have a Templates.xaml file in my project which contains the ResourceDictionary of styles.
I want to bind Direction of a DropShadowEffect to MyNamespace.MyPage.LightDirection. Currently I did this:
// MyPage.xaml.cs
/// <summary>
/// Direction of light source for shadows and gradients.
/// </summary>
public static double LightDirection => 45;
<!--Templates.xaml-->
<Style x:Key="ControlButtons" TargetType="{x:Type Button}">
<Setter Property="Effect">
<Setter.Value>
<DropShadowEffect ShadowDepth="4" Direction="{Binding LightDirection}" Color="Black" Opacity="0.5" BlurRadius="4" />
</Setter.Value>
</Setter>
<!--Other Setters-->
</Style>
<!--MyPage.xaml-->
<Page x:Name="MyPage"
DataContext="{Binding ElementName=MyPage}">
<!--MyPage as some other default attributes which I didn't write here-->
<Grid>
<Button x:Name="MyButton" Style="{StaticResource ControlButtons}">
My Button
</Button>
</Grid>
</Page>
It works. The shadow direction is set to LightDirection and program runs normally. However, while debugging, debugger shows a binding error:
Cannot find governing FrameworkElement or FrameworkContentElement for target element.
What is my mistake? How should I prevent this error?
I suggest making a constant
namespace MyNamespace
{
public class Constants
{
public static double LightDirection { get; set; }
}
}
XAML
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:MyNamespace">
<Style x:Key="ControlButtons" TargetType="{x:Type Button}">
<Setter Property="Effect">
<Setter.Value>
<DropShadowEffect ShadowDepth="4"
Direction="{x:Static local:Constants.LightDirection}"
Color="Black"
Opacity="0.5"
BlurRadius="4" />
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
Or declare constant directly in XAML
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:MyNamespace"
xmlns:sys="clr-namespace:System;assembly=mscorlib" >
<sys:Double x:Key="LightDirection">45.0</sys:Double>
<Style x:Key="ControlButtons" TargetType="{x:Type Button}">
<Setter Property="Effect">
<Setter.Value>
<DropShadowEffect ShadowDepth="4"
Direction="{StaticResource LightDirection}"
Color="Black"
Opacity="0.5"
BlurRadius="4" />
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>

Setting the Template property on a UserControl is not supported

I want to make a UserControl which can be used like the example below, however I don't know how to implement that. I found that example somewhere on WPF but seams like this is not supported anymore?
I get following error "WinRT information: Setting the Template property on a UserControl is not supported."
<UserControl
x:Class="Test.Gui.Widgets.WidgetFrame"
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">
<UserControl.Template>
<ControlTemplate TargetType="UserControl">
<Grid BorderBrush="Red" BorderThickness="1">
<ContentPresenter/>
<TextBlock FontSize="100" Foreground="AntiqueWhite">This is a Test</TextBlock>
</Grid>
</ControlTemplate>
</UserControl.Template>
</UserControl>
Using the control
<local:WidgetFrame>
<TextBlock FontSize="20" Foreground="Green">Content Presentation</TextBlock>
</local:WidgetFrame>
I found the solution by looking into other github repos
Seperate xaml and cs file
WidgetFrame.xaml
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:local="using:Test.Gui.Widgets">
<Style TargetType="local:WidgetFrame">
<Style.Setters>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Grid BorderBrush="Red" BorderThickness="1">
<ContentPresenter/>
<TextBlock FontSize="100" Foreground="AntiqueWhite">This is a Test</TextBlock>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style.Setters>
</Style>
</ResourceDictionary>
Add it to the App.xaml
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<XamlControlsResources xmlns="using:Microsoft.UI.Xaml.Controls" />
<ResourceDictionary Source="ms-appx:///Gui/Widgets/WidgetFrame.xaml"/>
<!-- Other merged dictionaries here -->
</ResourceDictionary.MergedDictionaries>
<!-- Other app resources here -->
</ResourceDictionary>
</Application.Resources>
WidgetFrame.cs
internal class WidgetFrame : ContentControl
{
public WidgetFrame() { }
}
Now I can place the content with xaml without overwriting the template
<widgets:WidgetFrame Width="200" Height="200">
<Button>Yes!</Button>
</widgets:WidgetFrame>

Cannot see controls in WPF window after applying style/template to Window

I have a custom template for my application Windows that I have built. It's in App.xaml
<Application.Resources>
<ResourceDictionary>
<Style x:Key="XWindow" TargetType="{x:Type Window}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate >
<Border BorderThickness="3">
<Border.Effect>
<DropShadowEffect BlurRadius="5" Direction="270" RenderingBias="Quality" ShadowDepth="0.5" Opacity="0.8" Color="#FF00B9FF"/>
</Border.Effect>
<Grid Background="White">
<local:ControlButtons Height="38" VerticalAlignment="Top" HorizontalAlignment="Right"/>
<Border BorderBrush="#99007CF7" BorderThickness="1"/>
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
</Application.Resources>
In my MainWindow.xaml I have applied this style like this (and it works) : <Window [...] Style="{DynamicResource XWindow }"
So the style is applied to the window. But when I put a control in the Window, I cannot see it or even select it. It's in the XAML code but even when I debug it's not on the Window.. Anyone has a clue ?
There's a screenshot :
XAML Problem
This is what it should normally do when I add a simple button : XAML Norrmal
As pointed out by #Clemens you have forgotten to add a ContentPresenter to your ControlTemplate. This is where the actual content of the window will be displayed.
You should also remember to put the ContentPresenter in an AdornedDecorator:
<Style x:Key="XWindow" TargetType="{x:Type Window}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Window}">
<Border BorderThickness="3">
<Border.Effect>
<DropShadowEffect BlurRadius="5" Direction="270" RenderingBias="Quality" ShadowDepth="0.5" Opacity="0.8" Color="#FF00B9FF"/>
</Border.Effect>
<Grid Background="White">
<local:ControlButtons Height="38" VerticalAlignment="Top" HorizontalAlignment="Right"/>
<Border BorderBrush="#99007CF7" BorderThickness="1">
<AdornerDecorator>
<ContentPresenter/>
</AdornerDecorator>
</Border>
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
The AdornedDecorator specifies the position of the AdornerLayer in the visual tree as stated on MSDN here: http://msdn.microsoft.com/en-us/library/system.windows.documents.adornerdecorator.aspx. You will for example need one if you intend to dipslay any validation errors in your window as validation errors are displayed on the adorner layer.
Edit: You should also set the TargetType property of the ControlTemplate:
<ControlTemplate TargetType="{x:Type Window}">

How to make a Style generic so I can use it in my entire application?

I've search around here on SO and what not able to find a clear answer explaining how to setup a 'style resource'. In my case my dialog controls several buttons and lists, and other various controls which I want to set a generic theme/style for. Similar to how you would do using a CSS file in HTML.
For the sake of simplicity in this example I have a style i want to use across the board on all my buttons. However I would prefer not to contain all these style resources in the xaml of my UI layout. I would like to move the styles to a general xaml resource file which would contain just the styles, that way i could also easily reference them into other wpf dialogs throughout the tool.
How do I set this up to make use of a general resource file containing styles for the various controls in my tool? Then be able to reference and use that style in my xaml UI's.
Thank you
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="200" Width="200">
<Window.Resources>
<Style TargetType="Button" x:Key="CoolButton" >
<Setter Property="Margin" Value="1,2,1,2"/>
<Setter Property="Background" Value="LightBlue"/>
<Setter Property="HorizontalAlignment" Value="Left"/>
<Setter Property="FontSize" Value="12" />
<Setter Property="FontFamily" Value="Calibri" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border Background="{TemplateBinding Background}"
BorderBrush="Lavender" BorderThickness="5" CornerRadius="6,0,6,0" x:Name="bd">
<ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
Margin="{TemplateBinding Padding}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" RecognizesAccessKey="True" />
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="bd" Property="Background" Value="LightGray"/>
<Setter Property="Foreground" Value="White" />
<Setter Property="Cursor" Value="Hand" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<StackPanel>
<Button Style="{StaticResource CoolButton}" Content="Button" Margin="2" HorizontalAlignment="Left" VerticalAlignment="Top" Width="75"/>
<Button Style="{StaticResource CoolButton}" Content="Button" Margin="2" HorizontalAlignment="Left" VerticalAlignment="Top" Width="75"/>
<CheckBox Margin="2" Content="Great"></CheckBox>
</StackPanel>
</Window>
On a side note why does this not work to use variables for colors in the resource style xaml?
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<!--COLORS-->
<Color x:Key="AccentColor">#CC4021</Color>
<Style TargetType="Button">
<Setter Property="Foreground" Value="{StaticResource AccentColor}"/>
</Style>
</ResourceDictionary>
There are a few steps to follow from making a instance-specific style a generic style.
Remove the Key. This will make the style to be used for every button:
<Style TargetType="Button">
Move it to a resource file, for example Default.xaml:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
>
<Style TargetType="Button">
...
</Style>
</ResourceDictionary>
Include a reference to the resource from a central point, for example the App.xaml, which will load the resources. The App.xaml will cause the styles to be used application-wide in just one go:
<Application x:Class="..."
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Default.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>

Styles defined in app.xaml doesn't take effect for the first time [duplicate]

I have a simple WPF window that has 12 buttons on it. I want the same style to be applied to all of them. This code produces the same error:
<Window x:Class="TestApp.TestWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="TestWindow" Height="400" Width="500"
WindowStyle="None" WindowState="Maximized">
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Resources/AllResources.xaml"/>
<ResourceDictionary>
<Style TargetType="{x:Type Button}">
<Setter Property="FontSize" Value="100"/>
</Style>
</ResourceDictionary>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Button Grid.Column="0" Content="1" Name="Button1"/>
<Button Grid.Column="1" Content="2" Name="Button2"/>
</Grid>
</Window>
The first button does not get the style applied to it, but the second one does. I could set a key and use that on every button, but I would prefer to let WPF handle that for me. I just found out while writing this that when I do not include the outside ResourceDictionary, it works as expected. This will be a problem in the future as my application expands as I have multiple windows that need to share the same resources. The modified code is as follows:
<Window x:Class="TestApp.TestWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="TestWindow" Height="400" Width="500"
WindowStyle="None" WindowState="Maximized">
<Window.Resources>
<Style TargetType="{x:Type Button}">
<Setter Property="FontSize" Value="100"/>
</Style>
</Window.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Button Grid.Column="0" Content="1" Name="Button1"/>
<Button Grid.Column="1" Content="2" Name="Button2"/>
</Grid>
</Window>
It also works if I (instead of removing the merged dictionaries) add an x:Key="key" attribute and then explicitly assign that style to each button.
What is the issue here? Why does the first one skip "Button1" and the second not?
I've seen this problem a couple of times before and it's a pretty weird "bug". It happends when you put a Style directly in a ResourceDictionary inside <ResourceDictionary.MergedDictionaries>. The Style is skipped for the first item. This code produces the same result, the Style is skipped for the first ListBoxItem
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="Foreground" Value="Green"/>
</Style>
</ResourceDictionary>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
<ListBox>
<ListBoxItem Content="Item 1"/>
<ListBoxItem Content="Item 2"/>
<ListBoxItem Content="Item 3"/>
</ListBox>
To get both the styles and MergedDictionaries to work, do it like this instead
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Resources/AllResources.xaml"/>
</ResourceDictionary.MergedDictionaries>
<Style TargetType="{x:Type Button}">
<Setter Property="FontSize" Value="100"/>
</Style>
</ResourceDictionary>
</Window.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Button Grid.Column="0" Content="1" Name="Button1"/>
<Button Grid.Column="1" Content="2" Name="Button2"/>
</Grid>
Although it does not produce an error, according to the documentation:
ResourceDictionary.MergedDictionaries Property
a dictionary used in merged dictionaries should not have content and should use the Source property to refer to another dictionary indirectly. In fact if you put the Style in a resource dictionary and reference both of them in the merge, it works as it should.
We can only speculate why this is not supported but since it isn't, and since the workaround is easy, we can't complain too bitterly except to wish that we received an error at compile time.

Categories