Howto override WPF childcontrols style if a style already is set? - c#

If I have a simple childcontrol that already have a style defined for a element in the childcontrol.
Can I change that style of that element from the parent control?
If I dont set the style in the childcontrol I can override it from the parent control, but I cant seem to get it to work when the style is set.
I can't change the code in the childcontrol.
The example childcontrol:
<UserControl
x:Class="TestingThemes.ChildControl"
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:local="clr-namespace:TestingThemes"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
d:DesignHeight="450"
d:DesignWidth="800"
mc:Ignorable="d">
<UserControl.Resources>
<Style x:Key="aw" TargetType="Button">
<Setter Property="Foreground" Value="Blue" />
</Style>
</UserControl.Resources>
<Grid>
<Button
Width="100"
Height="50"
Content="Kalle Anka"
<!-- If I remove the style I can override it from parent control -->
Style="{DynamicResource aw}" />
</Grid>
</UserControl>
The parentcontrol:
<Grid>
...
<local:ChildControl>
<local:ChildControl.Resources>
<!--<Style BasedOn="{StaticResource local:ResourceKey=aw}" TargetType="Button">-->
<Style TargetType="Button">
<Setter Property="Foreground" Value="Red" />
</Style>
</local:ChildControl.Resources>
</local:ChildControl>
</Grid>

This is possible, but you must change the location of the initial Style.
The XAML engine will traverse the logical tree to lookup StaticResource/DynamicResource resources for an element (e.g., Button) starting locally, with the local ResourceDictionary.
This means, you want the default Style that is defined inside the UserControl to be found at the very end of the lookup route - after any potential custom styles that are intended to override the default Style. To accomplish this, you must move the Style either to App.xaml or Generic.xaml.
Additionally, the reference must be DynamicResource in order to defer the lookup to the moment the logical tree is composed. The overriding Style can be defined anywhere up the route towards the tree root.
Since Generic.xaml provides a different name scope, you would have to define a unique static (global in terms of the XAML name scopes) ComponentResourceKey which you would have to use as the style's x:Key value (in case you plan to move the default Style to the Generic.xaml file).
Because of the aforementioned XAML resource lookup behavior, creating a custom Control is always prefereable over a UserControl when you plan to make the control customizable (like in your case). The default Style of a custom Control is located in the Generic.xaml by default, thus you would have never ran into your current issue.
ChildControl.xaml
<UserControl>
<Grid>
<Button Style="{DynamicResource aw}" />
</Grid>
</UserControl>
App.xaml
The last lookup location before the Generic.xaml. If a Style with the same key was not found yet, the Style found in App.xaml will be applied.
<Style x:Key="aw" TargetType="Button">
<Setter Property="Foreground" Value="Blue" />
</Style>
ParentControl.xaml
<ChildControl>
<ChildControl.Resources>
<Style x:Key="aw" TargetType="Button">
<Setter Property="Foreground" Value="Red" />
</Style>
</ChildControl.Resources>
</ChildControl>

Related

Setting the style of a WPF UserControl

I know I can set the style of a UserControl like so in the control by adding an attribute:
Style="{StaticResource MyStyle}"
And having a style in my ResourceDictionary that looks something like the following:
<Style x:Key="MyStyle" TargetType="{x:Type UserControl}">
<Style.Resources>
<Style TargetType="Label">
<!-- Label Setters -->
</Style>
<Style TargetType="TextBox">
<!-- TextBox Setters -->
</Style>
</Style.Resources>
</Style>
But is there a way I can set the style of the UserControl in the ResourceDictionary directly like:
<Style x:Key="MyStyle" TargetType="{x:Type MyControl}">
Essentially my question is, can I apply the style directly to the control instead of to the controls components?
EDIT:
What I am trying to accomplish is something like the following:
<Style x:Key="MyStyle" TargetType="{x:Type MyControl}">
<Setter Property="Background" Value="Black"/>
</Style>
<Style x:Key="{x:Type MyControl}" TargetType="{x:Type MyControl}" BasedOn="{StaticResource MyStyle}"/>
Where the second line applies the style to all controls in the application, if you do something similar with a normal control this approach works.
However this only sets the Background of the UserControl, so how can I apply that same background to its components.
How can I do it with the UserControl?
You can directly set the UserControl's Style like this:
<UserControl x:Class="MyNamespace.MyControl" xmlns:local="MyNamespace" ...>
<UserControl.Style>
<Style>
<Setter Property="local:MyControl.MyProperty" Value="..."/>
...
</Style>
</UserControl.Style>
</UserControl>
or like this:
<UserControl x:Class="MyNamespace.MyControl" xmlns:local="MyNamespace" ...>
<UserControl.Style>
<Style TargetType="local:MyControl">
<Setter Property="MyProperty" Value="..."/>
...
</Style>
</UserControl.Style>
</UserControl>
A default Style in the UserControl's Resources should also work:
<UserControl x:Class="MyNamespace.MyControl" xmlns:local="MyNamespace" ...>
<UserControl.Resources>
<Style TargetType="local:MyControl">
<Setter Property="MyProperty" Value="..."/>
...
</Style>
</UserControl.Resources>
</UserControl>
You need to remove the x:Key from your defined style so that it can be applied universally to all controls of the same type as what is defined in the TargetType.
To quote from MSDN for Style.TargetType Property:
Setting the TargetType property to the TextBlock type without setting an x:Key implicitly sets the x:Key to {x:Type TextBlock}. This also means that if you give the [...] Style an x:Key value of anything other than {x:Type TextBlock}, the Style would not be applied to all TextBlock elements automatically. Instead, you need to apply the style to the TextBlock elements explicitly.
Necro answer for a special case. If the user control is selected via a DataTemplate resource in another WPF control or window, WPF may not automagically apply a default style from an imported resource dictionary. However, you can apply named style resource after importing a resource dictionary.
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="../../Resources/ResourceDictionary.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
<UserControl.Style>
<Binding Source="{StaticResource MyUserControlStyle}"></Binding>
</UserControl.Style>
To style all controls, add your ResourceDictionary to the resources of your App.xaml.
<Application.Resources>
<!-- Your Resources for the whole application here -->
</Application.Resources>
If your open your Mainwindow with the App...
<Application ...
MainWindow="MainWindow">
or during the startup event...
<Application ...
MainWindow="MainWindow">
Startup="Application_Startup">
the resources are available in every control of your MainWindow.
To set the style for a specific usercontrol look here:
Set Style for user control
in your user control xaml place the style inside the resources tag:
<UserControl>
<UserControl.Resources>
<Style ...</Style>
</UserControl.Resources>
//.. my other components
</UserControl>

Windows Style from ResourceDictionary don't apply

As I have multiple Windows in my application, I am looking for a solution that does not require me to set a binding on each Window.
I created a ResourceDictionary which has a style for the Window Background:
<Style TargetType="{x:Type Window}">
<Setter Property="Background" Value="AliceBlue"/>
</Style>
In my XAML, I set the ResourceDictionary:
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Templates.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
There is no error, but my Window color stays white.
This appears to be caused by a combination of the order in which WPF loads/processes styles from nested ResourceDictionary, and the specifics of the Window class.
Assume MainWindow is defined as per your post. Now put the following in Templates.xaml:
<Style TargetType="{x:Type Window}">
<Setter Property="Background" Value="Red"/>
</Style>
<Style TargetType="{x:Type Window}" x:Key="myStyle">
<Setter Property="Background" Value="Green"/>
</Style>
If MainWindow has no style defined, then you will see that in the designer it appears with a red background. The designer is parsing the whole Xaml and loading the resource dictionary, and then drawing the results. The style is read before the window is drawn, and so the red background is applied.
When you run the application, the window is created before the ResourceDictionary is applied. It looks for a default style (a style with x:Key="{x:Type Window}") before the nested ResourceDictionary is processed, and finds nothing. Therefore at runtime, the window appears with default colour. (This is the behaviour described in the comments above.) Remember that the style with x:Key="{x:Type Window}" has a default value that matches the Windows style.
This is borne out if you use myStyle explicitly. If you add to your Window definition the attribute Style="{StaticResource myStyle}" you'll find that the designer fails, but you also get a run-time error, because myStyle hasn't been created at the time that the Window needs it. If you switch to Style="{DynamicResource myStyle}" then you'll see that it works as you hope, because DynamicResource will update once the ResourceDictionary has been parsed and the style included.
So, applying this, you can fix the problem in one way by adding this to your Window element: Style="{DynamicResource {x:Type Window}}" - but this is cludgy. The better solution is to include your resource dictionary in the app.xaml file, where it will be parsed before any window is opened and thus available to all:
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Templates.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
The real issue here is that your Window is not really a Window: it is a class that derives from Window and will in fact be MainWindow, Window2, etc... This means that the automatic style wireup for a Window will never work in this way, and some level of manual binding will unfortunately always be required.
This is the solution I used in my application. It lets me keep all my window styles together, and requires just a couple lines after the <Window.Resources> section.
Do your Style like so:
<Style x:Key="MyWindowStyle">
<Setter Property="Window.Background" Value="AliceBlue"/>
</Style>
Then, in your Window, after </Window.Resources> include the following:
<Window.Style>
<Style BasedOn="{StaticResource MyWindowStyle}"/>
</Window.Style>
Add a new brush in your resource dictionary
<SolidColorBrush x:Key="WindowBackground" Color="AliceBlue" />
and in your WPF window simply set the required resource to the window background property
<Window x:Class="GDD.Presentation.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="300" Width="300"
Background="{StaticResource WindowBackground}">

Specifying a custom Window's default appearance in WPF?

I would like to make a library of some of my commonly used WPF controls, and one of these controls is a CustomWindow which inherits from the Window class. How can I get my CustomWindow to use a default appearance that is defined in the library with it?
I can replace
<Window x:Class="..." />
with
<MyControls:CustomWindow x:Class="..." />
and it works for the window behavior, but not the appearnce.
EDIT
Here is a simplified version of what I have so far:
Custom Window Control. Located in the Control library.
public class CustomChromeWindow: Window
{
static CustomChromeWindow()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(CustomChromeWindow),
new FrameworkPropertyMetadata(typeof(CustomChromeWindow)));
}
}
Window Style. Located in a Generic.xaml, a ResourceDictionary in the Themes folder of the control library
<Style TargetType="local:CustomChromeWindow">
<Setter Property="WindowStyle" Value="None" />
<Setter Property="Background" Value="Red" />
</Style>
Test Window. Startup window of a separate project that references the control library
<local:CustomChromeWindow
x:Class="MyControlsTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:MyControls;assembly=MyControls"
Title="MainWindow" Height="350" Width="525"
>
<Grid>
<TextBlock Text="This is a Test" />
</Grid>
</local:CustomChromeWindow>
What I end up getting is a window with the regular WindowStyle and a black background.
Use this xaml:
<Window x:Class="MyNamespace.CustomWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:MyControls="MyNamespace">
<Window.Style>
<Style TargetType="MyControls:CustomWindow">
...
</Style>
</Window.Style>
<ContentPresenter />
</Window>
You may want to design a new theme for the window. If so place following theme in (your library)\Themes\Generic.xaml resource file:
<Style TargetType="{x:Type MyControls:CustomWindow}">
<Setter Property="WindowStyle" Value="None" />
<Setter Property="AllowsTransparency" Value="True" />
...
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type MyControls:CustomWindow}">
<Border>
...
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Try adding this to the AssemblyInfo.cs file in your class library
[assembly: ThemeInfo(
ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
//(used if a resource is not found in the page,
// or application resource dictionaries)
ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
//(used if a resource is not found in the page,
// app, or any theme specific resource dictionaries)
)]
How about this (put this in the default Cctor) :
DefaultStyleKeyProperty.OverrideMetadata(typeof(CustomWindow)
, new FrameworkPropertyMetadata(typeof(CustomWindow)));`

WPF: TextTrimming on a ContentPresenter

Is there a simple way to just get TextTrimming to work with a ContentPresenter?
I have implict styles for TextBlock and AccessText that have TextTrimming set to CharacterEllipsis, but it's not picked up by the ContentPresenter. I can change the ContentPresenter to an AccessText or TextBlock and set it there, but then the template only handles text content.
Any suggestions?
Thanks!
Implicit Styles for elements that derive from UIElement, but not Control, are not applied if the element is defined in a control's Template unless the implict Style is defined in the application Resources. The same holds true for TextBlocks used by ContentPresenter.
For example, in the following XAML the TextBlock that is ultimately used to present the button's content will not get the implicit Style:
<Window.Resources>
<Style TargetType="TextBlock">
<Setter Property="Foreground" Value="Red" />
</Style>
</Window.Resources>
<StackPanel>
<Button Content="Will not be red" />
<TextBlock Text="Will be red" />
</StackPanel>
If you take that exact same Style and move it to the application's Resources, then both will be red:
<Application.Resources>
<Style TargetType="TextBlock">
<Setter Property="Foreground" Value="Red" />
</Style>
</Application.Resources>
So you can either move your implicit Style to application resources, which is generally not a good idea. Or you can customize the display for the specific scenario you have. This can include adding an implicit DataTemplate, or customizing a control's Template.
If you can provide more information, then it would be easier to know which is the best approach.
Thanks to this Gist by James Nugent: "WPF style which puts character ellipsis on button contents without replacing the ContentPresenter with a TextBlock and thus losing the ability to support access keys."
This worked for me:
<ContentPresenter.Resources>
<Style TargetType="TextBlock">
<Setter Property="TextTrimming" Value="CharacterEllipsis"></Setter>
</Style>
</ContentPresenter.Resources>

Setting a style on Control type not working

I'm writing a very basic WPF dialog and want to apply a simple style to all objects that inherit from the Control class. The code I'm using:
<Window.Resources>
<Style TargetType="{x:Type Control}">
<Setter Property="Margin" Value="20"/>
</Style>
</Window.Resources>
<StackPanel>
<TextBlock Text="some text"/>
<TextBox x:Name="x_NameTextBox"/>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right">
<Button x:Name="x_CancelButton" Click="x_CancelButton_Click" Content="Cancel"/>
<Button x:Name="x_OkButton" Click="x_OkButton_Click" Content="OK"/>
</StackPanel>
</StackPanel>
</Window>
The Style defined above doesn't change the layout of the window at all unless I specify a key and set the style on each individual object, which is exactly what I'm trying to avoid. It also works for more specific types (setting the TargetType to Button, for example.)
Any ideas why this isn't working?
Every control when it gets instantiated it gets its Style from the explicitly defined resource or look for the immediate parent where it can get a default style. In your case the Button control will get its default Style from the platform because your App haven't defined one. Now that platform Button Style has no way to know about your custom defined Control base style. Because styles will look for a base style only when you explicitly define BasedOn
So you got only two ways
1. Define Style for every control - which you don't want I think.
2. Define Styles for the controls you are interested and set the BasedOn
<Style TargetType="{x:Type Control}">
<Setter Property="Margin" Value="20"/>
</Style>
<Style TargetType="{x:Type Button}" BasedOn="{StaticResource {x:Type Control}}">
</Style>

Categories