Using BasedOn Style Property on DynamicResources - wpf

i wonder if there is a way to use the basedOn property of wpf styles with dynamicresources. e.g.
<Style BasedOn="{DynamicResource somestyle}">
<Setter Property="SomeProp" Value="SomeValue"/>
</Style>
this e.g. throws an error indicating that the usage of dynamicresources in combination with BasedOn styles is not possible.
i wonder how someone could do that?
thanks

I think the main reason is sealed objects. If you have a Style hierarchy:
Style A
/ \
Style A1 Style A2
this might not be a difficult scenario. You refer to StyleA using a dynamic resource, so whenever that resource changes, Style A1 and Style A2 should change their BasedOn property. However, once a Style is being used in your application, it becomes a sealed object. Style A becomes immutable.
One workaround you can use is:
Style A needs to change.
Create a new Style object that is going to be the new Style A resource.
Create a new version of Style A1 and Style A2. You'd need to write a copy procedure that makes copies of all the Setters, Resources, etc. Set the BasedOn to the new version of Style A.
Update the resources collection so that the three new styles are in there.
{DynamicResource StyleA1} and {DynamicResource StyleA2} should now pick up the fact that those resources changes (from step 4) and update any references automatically.
Note that this is a very simple scenario. Real world style hierarchies can be more complex, especially if they are spread across multiple files and come from merged dictionaries.
Hope I understood your problem and helped out.

I've found that since you can't use BasedOn on a DynamicResource, you can "convert" the DynamicResource to StaticResource by merging the ResourceDictionary holding your "parent" resources to your current Window/UserControl/whatever. This way you are now able to refer to the resource object (eg. Style) using StaticResource. This way you can use Datatriggers on DynamicResource (through conversion).
Example:
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/MyProject.Styles;component/ButtonStyles.xaml"/>
</ResourceDictionary.MergedDictionaries>
[*Your other resources can be put here*]
</ResourceDictionary>
</Window.Resources>
...
<Button Command="{Binding MyCommandInViewModel, RelativeSource={RelativeSource AncestorType=Window}}">
<Button.Style>
<Style BasedOn="{StaticResource StyleFromButtonStyles}" TargetType="Button">
<Style.Triggers>
<DataTrigger Binding="{Binding SomeBool}" Value="True">
<Setter Property="Button.Content" Value="{StaticResource SomeImage}"/>
</DataTrigger>
<DataTrigger Binding="{Binding SomeBool}" Value="False">
<Setter Property="Button.Content" Value="{StaticResource SomeOtherImage}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
Hence Datatriggers are applied to a button styled in an imported ResourceDictionary.
Hope this helps!

Related

Set default Style for FrameworkElement is not applied to derived class

I am a beginner in XAML. I create a new default style in a ResourceDictionary.
<Style TargetType="FrameworkElement" BasedOn="{StaticResource {x:Type FrameworkElement}}">
<Setter Property="Margin" Value="5"/>
<Setter Property="VerticalAlignment" Value="Center"/>
</Style>
Then I import it to UserControl.Resources.
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/PathToStyle.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
What I expect is that it will be applied to all FrameworkElements used in that UserControl, but it is not.
What infomation am I missing here?
The problem is because TargetType is specific.
A style which you do not give a x:Key to automatically is given a key which is equivalent to it's type.
Hence your style:
<Style TargetType="FrameworkElement"
Actually has an x:Key given to it which is
<Style x:Key="{x:Type FrameworkElement}"
https://learn.microsoft.com/en-us/dotnet/api/system.windows.style.targettype?redirectedfrom=MSDN&view=netcore-3.1#System_Windows_Style_TargetType
A piece of ui which is "looking" for a style looks for a matching key to it's type using that x:Key. It does not search up it's inheritance chain as well.
EDIT:
You can prove this:
In a new wpf app, add a frameworkelement to you mainwindow and a style targets frameworkelement.
<Window.Resources>
<Style TargetType="FrameworkElement">
<Setter Property="Height" Value="10"/>
</Style>
</Window.Resources>
<Grid>
<local:TestSubClass/>
</Grid>
and
public class TestSubClass : FrameworkElement
Spin it up and take a look at the live visual tree > Properties.
The frameworkelement fills the grid and has an actualheight matching the grid's height.
No style is set on it.
Hence this is the situation where no style is set and inheritance is not working as claimed in another post.
Change that to a regular frameworkelement:
<Window.Resources>
<Style TargetType="FrameworkElement">
<Setter Property="Height" Value="10"/>
</Style>
</Window.Resources>
<Grid>
<FrameworkElement/>
</Grid>
Spin it up with an f5 and take a look at the live visual tree.
There is a style in the properties.
Actualheight is 10.
What I'm expecting is it will apply to all FrameworkElement used in That UserControl
By all FrameworkElements you probably mean all FrameworkElements including its derivatives. Applying the Style as implicit style works perfectly if you target a distinct control like a Button. However, it may not work as you expect, if you target a base type of a control, like FrameworkElement, because each derivative of that base type can have their own style applied explicitly or implicitly that breaks your anticipated behavior by:
Not being based on the style of the base type
Not being based on a style that is transitively based on the base type style
Overriding the properties of the base style
The essential misunderstanding here is that all styles of derivatives of a base type will base their style on the style of said base type, but this does not apply in general.
You can check this yourself by extracting the Style of any control via Visual Studio or Blend. For example, let's look at the Style of a Button. As you can see, it is not even based on FrameworkElement, so it will not apply your base style. Even if it would, there is a chance that it will override the Margin property itself.
<Style x:Key="ButtonStyle" TargetType="{x:Type Button}">
<Setter Property="FocusVisualStyle" Value="{StaticResource FocusVisual}"/>
<Setter Property="Background" Value="{DynamicResource PrimaryHueMidBrush}"/>
<Setter Property="BorderBrush" Value="{DynamicResource PrimaryHueMidBrush}"/>
<!-- ...other setters. -->
</Style>
Apart from that, you have to define your base type style before any other styles of derivatives, because you can only use BasedOn with StaticResource. Consequently, if the style of a derived control is already defined before your style, your base style will not be applied.
What infomation am I missing here?
The fact that there are default styles defined for more derived types such as for example Button and Control and that these will take precedence over and be applied instead of your custom FrameworkElement style.
Your approach of defining a single Style for all FrameworkElements won't work. You'll need to define an implicit type for each derived FrameworkElement type.

Precedence of multiple styles for the same target type?

If there are multiple styles like this:
<Style TargetType="{x:Type local:MyControl}">
...
that are merged in from resource dictionaries that target the same type of control, which should have precedence, is it the first style encountered or last one?
Where can I find the rules that govern such things?
Styles are applied from the ResourceDictionary closest to the control in question. An example:
<Window>
<Window.Resource>
<Style 1/>
<Window.Resources>
<Grid>
<Grid.Resources>
<Style 2/>
</Grid.Resources>
<TextBox/>
</Grid>
</Window>
In the above example, Style 2 will be applied to the TextBox. Should you wish to cascade the styles (apply both styles to the TextBox), you can set BasedOn on Style2 to point to Style1 using BasedOn="{StaticResource {x:Type TextBox}}". Please check the syntax, I don't have VS here.
As you can see, the type becomes the Key. Since it is not permissible to have two elements with the same key in a single ResourceDictionary, you cannot merge two ResourceDictionaries with overlapping styles. It should be possible to design around such a requirement, remembering that a ResourceDictionary can reference another use. Again, you use BasedOn.
This is the answer for your comment and for question at all. Name, x:Name doesn't play on the scene in this case. Each resource in the dictionary must have the Key. For targeted styles WPF infrastructure generates the Key, so, the styles with the same target type will have same key, thus you can't use more than one targeted style for each type in the dictionary.
It will throw an exception. This is what I tried to test this:
I wrote a simple ResourceDictionary with 2 styles, with same TargetType but without x:Key (not x:Name).
<Style TargetType="TextBox">
<Setter Property="Height" Value="100"/>
</Style>
<Style TargetType="TextBox">
<Setter Property="Height" Value="200"/>
</Style>
Rebuilt the project and it complied successfully. Now which will get applied?
Well, when I ran it, it threw a big exception when loading the styles. So in short, it doesn't work.

Inheriting from Application Styles (WPF)

I am trying to inherit application-level styles for a certain Window in my WPF application, but I'm having trouble getting it to inherit rather than simply override the existing styles.
In App.xaml (under the App.Resources element) I define a style as such:
<Style TargetType="Button">
<Setter Property="Padding" Value="6"/>
<Setter Property="FontWeight" Value="Bold"/>
</Style>
And in the XAML fora a certain Window, I define the following under Window.Resources:
<Style TargetType="Button" BasedOn="{StaticResource {x:Type Button}}">
<Setter Property="Padding" Value="6"/>
<Setter Property="FontWeight" Value="Bold"/>
</Style>
The problem here is that the former (app) style is ignored as if the latter (window) style has overridden it. The BasedOn attribute is set, which is intended to indicate that existing styles should be inherited, as far as I know. Removing the attribute doesn't help either. The only potential cause of which I can think is that {StaticResource {x:Type Button}} only refers to the default WPF style and not the one I have define in App.xaml.
I am aware that this styling behaviour would be possible to accomplish using the x:Key attribute, but I was hoping for a more elegant way that allows me to apply styles with inheritance to all controls within a scope (i.e. application/window).
Update
Thanks for both of your replies. You are indeed right that things work as expected in a sample application. The difference is that I inadvertently failed to mention that the style in App.xaml is contained within a ResourceDictionary, as such:
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="SettingsDictionary.xaml"/>
<ResourceDictionary>
<Style x:Key="DefaultButton" TargetType="Button">
<Setter Property="Padding" Value="4"/>
<Setter Property="HorizontalAlignment" Value="Center"/>
<Setter Property="VerticalAlignment" Value="Center"/>
</Style>
</ResourceDictionary>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
Any suggestion on how to remedy matters in this case?
EDIT
After some research, I've found that the x:Key is being automatically generated if TargetType is set. So, the style in App.xaml is correct. However, the wpf designer is lacking some resource handling skills, and is not displayng both styles. If you build and run the project, both styles will be applied.
If your machine and VS2008 behave like the one upon which I tested your code.
Hope this helps.
EDIT 2
The resources and merged dictionaries in App.xaml have always been quirky.
I've solved the problem by moving the first style declaration out of the merged dictionary, like this:
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<!--<ResourceDictionary Source="SettingsDictionary.xaml"/>-->
</ResourceDictionary.MergedDictionaries>
<Style TargetType="Button">
<Setter Property="Foreground" Value="Red"/>
<Setter Property="Padding" Value="4"/>
<Setter Property="HorizontalAlignment" Value="Center"/>
<Setter Property="VerticalAlignment" Value="Center"/>
</Style>
</ResourceDictionary>
</Application.Resources>
Note also that giving the style an explicitly set key other than the {x:Type Button} will make it a non-default style and make it not apply automatically.
It is generally recommended to specify merged dictionaries only for resources from another file, and coded resources in the default space as above.
I'd second Jeff Wains comment in being surprised that your approach is not working as desired. In fact I'm unable to reproduce your issue via the following steps:
Created a new project via VS 2008 C# WPF application wizard.
resulting in App.xaml and Window1.xaml just like your example
Added a standard button from the toolbox to Window1.
Pasted your snippets as is, but modified one property each to observe the desired effect in the first place (having identical properties/values each is not what you intended to demonstrate I guess).
Well, this is just working fine, i.e. the button in Window1 inherits properties from both styles and modifying properties in either one does properly affect the button. Consequently their must be something weird going on behind the scenes in your project/environment? Have you tried a simple repro case like this already?

How can I apply multiple XAML styles to one element?

I have a toolkit:DataGrid (from Codeplex).
It is starting to have many different styles:
one for switching it on and off based on a variable in the ViewModel
one for visual styling
one for some other triggers, etc.
Do all of these need to be in one big style, is that the only way to do it? Or can I have multiple styles and attach them as I need them? Is there anyway to do this so that you can e.g. swap styles in code?
Styles:
<Style x:Key="CodePlexDataGridSwapper" TargetType="toolkit:DataGrid">
<Style.Triggers>
<DataTrigger Binding="{Binding WhichGrid}" Value="Infragistics">
<Setter Property="toolkit:DataGrid.Visibility" Value="Collapsed"/>
</DataTrigger>
</Style.Triggers>
</Style>
<Style x:Key="ToolkitDataGridLayout" TargetType="toolkit:DataGrid">
<Setter Property="Background" Value="Yellow"/>
</Style>
PSEUDO-CODE:
<toolkit:DataGrid
Style="{StaticResource CodePlexDataGridSwapper, ToolkitDataGridLayout}"
ItemsSource="{Binding Customers}"/>
You may want to look at the BasedOn property of the Style class. Basically, it allows you to inherit one style from another. The 'child' style will have all of the setters and triggers from the parent one (unless it has setters overriding the parent's ones) plus it will have obviously its own setter and triggers.
Maybe this info will be helpful for you.

WPF Trigger for IsSelected in a DataTemplate for ListBox items with Blend

I wanted to change the Foreground color when I selected a listboxItem and I did it using this bit of code:
<DataTrigger Binding="{Binding
RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type ListBoxItem}},Path=IsSelected}" Value="True">
<Setter TargetName="descriptionTB" Property="Foreground" Value="#000"/>
</DataTrigger>
You can find the answer here.
But if I want a designer to do this in Blend, how would he do it without drilling into xaml?
Thank you
Artur,
The Triggers designer in Expression Blend only allows adding and modifying EventTriggers and Property triggers. I haven't found a way to add DataTriggers with Blend. I'm also not sure how to set RelativeSource Binding using Blend either. I've always handed code the XAML for test and use Blend for everything else.
Maybe I'm misunderstanding the question but can't you just create a style resource for descriptionTB and let the designer only deal with that style definition and not the binding?
<DataTrigger Binding="..">
<Setter TargetName="descriptionTB" Property="Style" Value="{StaticResource DescriptionTextBoxStyle}" />
</DataTrigger>
In the resources section of your control or window you add the style definition:
<Style TargetType="{x:Type TextBox}" x:Key="DescriptionTextBoxStyle">
<Setter Property="Foreground" Value="#000" />
</Style>
If you want to further isolate the designer from the mechanics of the UI you can create a resource dictionary in a separate xaml file in which you can collect all styles meant for the designer. Then you can merge that resource dictionary with your control's or application's main resources.

Resources