WPF Style Hierarchy - wpf

I have created a custom control, ColorToggleButton, which inherits ToggleButton. In a corresponding .xaml file, a for ColorToggleButton is specific via TargetType and BasedOn ToggleButton.
<Style TargetType="ctl:ColorToggleButton" BasedOn="{StaticResource {x:Type ToggleButton}}">
This works fine, but if I apply another style in a window using x:Key, as in
<Style x:Key="SameContent"><Setter Property="Content" Value="Same Content" /></Style>
<ctl:ColorToggleButton Style={StaticResource SameContent} />
the old style seems to get wiped out completely and replaced with the new one. I can circumvent the problem by using BasedOn
<Style x:Key="SameContent" BasedOn="{StaticResource {x:Type ctl:ColorToggleButton}}"><Setter Property="Content" Value="Same Content" /></Style>
<ctl:ColorToggleButton Style={StaticResource MyKey} />
but this seems counterintuitive to me, seeing as I wouldn't use the BasedOn attribute if I was applying styles to a normal ToggleButton or some other default control. Is this the standard way of implementing your own controls? Am I doing something horribly wrong?
Edit: The static constructor of ColorToggleButton is as follows:
static ColorToggleButton()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(ColorToggleButton), new FrameworkPropertyMetadata(typeof(ColorToggleButton)));
}

In your control, did you provide static constructor with DefaultStyleKeyProperty override?
static ColorToggleButton()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(ColorToggleButton), new FrameworkPropertyMetadata(typeof(ColorToggleButton)));
}

Related

Not all styles are overwritten in my WPF controls

I have a system where I use my WPF UI as a class library. I also use different usercontrols to control what the user see during the span of the UIs life cycle. These are all set in code. I then added a theme system where you could create a XAML ResourseDictionary file and use that to change the look on the UI. To apply the style, everytime I create the window or a user control, that FrameworkElement goes through this code:
public void ApplyStyle(FrameworkElement element)
{
var targetDir = element.Resources.MergedDictionaries.FirstOrDefault(d => d.Contains("SonaStyleDocument"));
if (targetDir != null && loadedResource != null)
{
element.Resources.MergedDictionaries.Remove(targetDir);
element.Resources.MergedDictionaries.Add(loadedResource);
}
}
In my view I do set a standard style document via the xaml code. I locate the default style in the code, remove it and add the new one. Now this works, and I can see the changes when I apply a external ResourceDictionary. However, for some reason my buttons does not react to the changes. They keep the same styles, even though it has been removed. Here is one of my buttons code:
<Button Style="{StaticResource KeyboardToggleButton}" Command="{Binding KeyboardToggleCommand}">
<Button.Content>...</Button.Content>
</Button>
It uses the style KeyboardToggleButton, which has the follow style in my default style document:
<Style TargetType="Button" x:Key="SonaButton">
...
</Style>
<Style TargetType="Button" x:Key="SonaPrimaryButton" BasedOn="{StaticResource SonaButton}">
<Setter Property="Template" Value="{StaticResource DxcPrimaryButtonTemplate}" />
...
</Style>
<Style TargetType="Button" x:Key="ManipulationButton" BasedOn="{StaticResource SonaPrimaryButton}">
...
</Style>
<Style TargetType="Button" BasedOn="{StaticResource ManipulationButton}" x:Key="KeyboardToggleButton" />
But I replace it with the following style(s):
<Style TargetType="Button" x:Key="SonaButton">
<Setter Property="Template" Value="{StaticResource RoundCornerTemplate}"></Setter>
</Style>
<Style TargetType="Button" x:Key="ManipulationButton" BasedOn="{StaticResource SonaButton}">
...
</Style>
<Style TargetType="Button" x:Key="KeyboardToggleButton" BasedOn="{StaticResource ManipulationButton}"/>
What could be going on here? They both have the same key for KeyboardToggleButton, though the templating is different and I can see that my other controls react fine to the new styles they get. I even tested just adding the new style and let the old one remain and that has the same result. The buttons just won't accept the new style.
Update:
So I was reading various articles about styles to see if I find some more information, and some article mentioned Dynamic resources. To isolate the problem I first remade my default style of one of my buttons to be very basic:
<Style TargetType="ButtonBase" x:Key="KeyboardToggleButton" />
And then changed the style I want to overwrite the default style, to just change the background:
<Style TargetType="Button" x:Key="KeyboardToggleButton">
<Setter Property="Background" Value="Yellow"/>
</Style>
With this setup nothing happened, but when I changed the Style from a StaticResource to DynamicResource, then it worked. However, this presented another problem. I tried testing this result on one of my other buttons with all substyles and control templates activated and the system throwsan exception in the code that adds the style:
System.InvalidOperationException: 'Property can not be null on Trigger.'
The style I try to use instead of the default style has a control template with triggers, to handle hover events, click event and more. My theory at this point is that I have had this problem also with the StaticResource, but the error is hidden and the system defaults directly to the old style. My issue with this theory is that I actively remove the default style before I enter in the new. Not removing the old style does not help.
I found the problem. The exception was indeed the answer, but only when I changed the code to apply each style individually. I should have followed the exception a bit more, but I had tunnel vision. The answer came from here: Why is the WPF property IsDefaulted not found?
I have first tried to change to buttonbase, but that does not work with the trigger "IsDefaulted". That will not resolve and the property will be null. Changing back to TargetType="Button" worked. No more exceptions. Then I also changed all style references to be dynamic and now all buttons (Except one, which I am looking into) have the correct style. It makes sense that it should be Dynamic as I change it during runtime.

My custom control in ToolBar

I have a specific control. I want to make style for it when it is placed in ToolBar. I have found how to do it in case of Button, CheckBox and other standard controls, but how I should make it for my control?
<Style x:Key="MyStyleForCustomControl" TargetType="{x:Type NameSpace:CustomControl}">
// Your setter's for your controls go here.
</Style>
NameSpace - where your Control is present
CustomControl - Name of your control.
Is this what you want?
EDIT:
If you want Style for Toolbar in your control, simply place the style in your UserControl Resources, it will be applied to the Toolbar placed within your control. The scope for this style will be limited to your control and will be hidden outside your control.
<UserControl.Resources>
<Style TargetType="{x:Type ToolBar}">
....
</Style>
</UserControl.Resources>
Ok now you clarified your question I think I can give you a solution. It looks like you simply need to set the ItemContainerStyle of the ToolBar control for your specific user control. First declare the "my" namespace where your specific control is located, then just add something like this:
<ToolBar>
<ToolBar.ItemContainerStyle>
<Style TargetType="{x:Type my:MyUserControl}">
<Setter Property="Background" Value="Azure"/>
</Style>
</ToolBar.ItemContainerStyle>
<my:MyUserControl/>
</ToolBar>
If you wanna add other control types to the ToolBar like the Button you mentioned above, then you will need to define a custom StyleSelector instead that you will set to the ItemContainerStyleSelector property. Here is a pretty good sample of StyleSelector implementation: Style Selectors

WPF Grid.Resources Style breaks ResourceDictionary Style. How to make them coexist?

In a WPF app I have a ResourceDictionary with Style defined for the TargetType MyCustomControl:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:cc="clr-namespace:MyControlLibrary;assembly=MyControlLibrary" >
<Style TargetType="{x:Type cc:MyCustomControl}">
<Setter Property="Prop1" Value="1" />
<Setter Property="Prop2" Value="2" />
...
The problem is that I need to define a context menu for MyCustomControl in the page XAML like following:
<Grid>
<Grid.Resources>
<ContextMenu x:Key="MyControlContextMenu">
<MenuItem Name="Name1"
Header="Header1"
Click="Cm1_Click"
.../>
....
</ContextMenu>
<Style TargetType="{x:Type ScNamespace:MyCustomControl}">
<Setter Property="ContextMenu" Value="{StaticResource MyControlContextMenu}"/>
</Style>
</Grid.Resources>
...
In this case, though I only assign one Property in the Style definition inside my Grid, the ResourceDictionary Style values are not applied at all. They get overridden by page resource style and ignored.
How to solve this issue? Maybe there is a way to make a reference to ResourceDictionary in the Grid.Resources section to enforce looking up the ResourceDictionary Style?
Base your new style on your default style:
<Style TargetType="{x:Type ScNamespace:MyCustomControl}" BasedOn="{StaticResource {x:Type ScNamespace:MyCustomControl}}">
<Setter Property="ContextMenu" Value="{StaticResource MyControlContextMenu}"/>
</Style>
Not sure if it is what you're looking for, but a Style can inherit from another. With the BasedOn-property you can define the base style of a Style, so that the new style inherits all settings from this style.
However I never tried if it works also if the BasedOn references to the same key (type). Maybe it works:
<Style TargetType="{x:Type ScNamespace:MyCustomControl}"
BasedOn="{x:Type ScNamespace:MyCustomControl}">
If this works not, maybe you can separate the Style, define it globaly with a key and then reference to the globaly defined Style via the BasedOn-property.
In general, Controls should have their default Styles defined in the Themes folder in a theme specific file (see here for more info).
When an application looks for a
resource, it looks at three levels in
the following order:
1) The element level.
The system starts with the element
that references the resource and then
searches resources of the logical
parent and so forth until the root
element is reached.
2) The application level.
Resources defined by the Application
object.
3) The theme level.
Theme-level dictionaries are stored in
a subfolder named Themes. The files in
the Themes folder correspond to
themes. For example, you might have
Aero.NormalColor.xaml,
Luna.NormalColor.xaml,
Royale.NormalColor.xaml, and so on.
You can also have a file named
generic.xaml. When the system looks
for a resource at the themes level, it
first looks for it in the
theme-specific file and then looks for
it in generic.xaml.
In your case, you have two implicit Styles, so HCL and Kent's answers should work. Since only one implicit Style can be applied at a time. Same goes for setting the Style properly directly. In that case, no implicit Styles will be applied.
If you have your first Style setup as a default Style at the theme level, then it would be applied in addition to your second implicit Style (or any explicitly defined Style).

override WPF styles

Looking at SharpDevelop source code I found this button declaration:
<Button Style="{x:Static core:GlobalStyles.ButtonStyle}"
Content="{core:Localize StartPage.StartMenu.NewCombineButton}"
Click="newSolutionClick" Margin="8,0,0,0" />
my problem is the style declaration: Style="{x:Static core:GlobalStyles.ButtonStyle}"
it prevents me from externally apply custom button style
if I remove the style declaration, the external theme (stored in .xaml) file works just fine.
my question: is there a way to override these specific style declarations?
Thanks a lot.
Adi Barda
You could try using BasedOn in your style... I've never used it with an x:Static so I'm not positive if it works or the syntax, but an example would be something like:
<Style x:Key="MyButtonStyle" TargetType="{x:Type Button}"
BasedOn={StaticResource core:GlobalStyles.ButtonStyle}">
<!-- Style Button here - If you define a Setter that already
exists in base style it will overwrite it -->
</Style>

Overriding styles defined in generic.xaml

I have a custom controls library, in which I defined a control template, that uses some custom styles. My control template is located in the Generic.xaml file, and the styles it uses are also located there, and accessed by the control template with the StaticResource markup extension.
In some cases, when I use this controls library, I want to change some of the styles used template, but I don't know how to do that.
I thought that if I would add to my Window's resource dictionary a style with a name, that is used by the template, my style will "override" the one that is defined in Generic.xaml file, but it didn't work.
What should I do?
does that work?
<Style TargetType="{x:Type YourCustomControl}"
BasedOn="{StaticResource {x:Type YourCustomControl}}">
<Setter Property="SomeStylePropertyOfYourCustomControl"
Value="{StaticResource SomeStyleYouWantToUseInstead}"/>
</Style>

Resources