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.
Related
I want to make a style for fields of XamDataGrid to set the AllowEdit property of the fields in the converter using some conditions.
I am trying with this but not working for me.
<Style TargetType="{x:Type igWPF:Field}">
<Style.Setters>
<Setter Property="AllowEdit" Value="{Binding Path=Field, Source={RelativeSource Self}, Converter={StaticResource FieldsEditableConverter}}">
</Setter>
</Style.Setters>
</Style>
Please suggest some code.
Infragistics said : "Once WPF Styles are in use, you cannot modify them."
Since I was looking for slightly the same thing (XamDataGrid, Field, Style, Setter modification of just access setter programmatically) but I wasn't able to. I've found the following information :
This exception has nothing to do with Infragistics controls. Once WPF Styles are in use, you cannot modify them. If you want to programmatically create Styles, you should create them from scratch, and not try to modify existing Style objects.
http://www.infragistics.com/community/forums/t/13909.aspx
Despite the fact that you can not modify it dynamically, so, on my specific case, I finally used x:Name of Style used by the targeted setter when required : so just before it has to modify the list (CanExecute).
This is how my code looks like :
C# :
var cm = uc.Resources["fiContextMenu"] as ContextMenu;
foreach(MenuItem mi in cm.Items)
if(mi.Header.Equals("Remove File"))
mi.IsEnabled = false; //Disallow remove when empty list
Xaml :
<igDP:FieldSettings.EditorStyle>
<Style TargetType="{x:Type igEditors:XamComboEditor}">
<Setter Property="ContextMenu" Value="{StaticResource fiContextMenu}" />
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)));
}
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).
Is there a way to setup global styles for my WPF application?
What I'm hoping to do is apply a style to all my Buttons that also have an Image child.
Well, sort of - it's a catch-all approach you can do - put the following element in your App.xaml - all your buttons will change (except the ones you apply a style to, manually).
<Style TargetType="{x:Type Button}">
<Setter Property="Background" Value="LightPink"/> <!-- You should notice that one... -->
</Style>
However, if you want to hit only buttons with images - you have to inherit from Button everytime you do and then apply a style like this:
public class CustomImageButton:Button{}
<Style TargetType="{x:Type local:CustomImageButton}">
<Setter Property="Background" Value="LimeGreen"/>
</Style>
<local:CustomImageButton Content="ClickMe"/>
It is a very coarse-grained global styling - and you need to follow the convention to make it work.
An alternative is to use Themes - read more about that here.
You can do implicit styles in WPF which are applied by type
for instance
<Style TargetType="Button">
Will be applied to ALL the buttons within the scope of that style (If the style is in App.XAML it will apply to all buttons, if it is lower in the chain it will apply to all buttons underneath it)
If you want to apply it to only certain types of buttons (say imagebuttons) create a type that derives from button (call it ImageButton) and then create a style targeted to that type.
Put the style into a ResourceDictionary tag inside your App.xaml and it will apply to the entire app.
I have an application resource of the following
<Style TargetType="{x:Type TextBlock}">
<Setter Property="Background" Value="{DynamicResource windowTextBackColor}"/>
<Setter Property="Foreground" Value="{DynamicResource windowsTextForeColor}"/>
</Style>
So all the text blocks in my application should assume those colours.
However the Menu and its containing MenuItems on my Main Window does not take these colours?
I have to do the XAML
for it to assume those colours, Is there a reason why setting a style that targets Text blocks does not work?
Thanks
I think you have to style the menu and menuitems separately. A MenuItem is a HeaderedContentControl, and its Header property is not a TextBlock, but an object, so it wouldn't be affected by a style for TextBlock.
You might also try changing that style to target Control instead of TextBlock. (Control is where Foreground and Background are defined.) I can't say for sure that it'll work, but if it does, it'll make every Control (TextBlocks, MenuItems, Buttons...) have those background and foreground colors.
Also, you might consider using BasedOn so that you can "inherit" the styles. If you don't, then styles defined farther up the hierarchy won't affect controls that have a style defined lower in the hierarchy. Basically, the lower ones mask the higher ones, unless you used BasedOn. Use it in this fashion:
BasedOn="{StaticResource {x:Type <your type here>}}"