Proper usage of Merged Resource Dictionaries in Silverlight 3 - silverlight

As I read: http://msdn.microsoft.com/en-us/library/cc903952(VS.95).aspx, specifically the section labeled "Forward References With a ResourceDictionary":
Static resource references from within
any given resource dictionary must
reference a resource that has already
been defined lexically before the
resource reference. Forward references
cannot be resolved by a static
resource reference. For this reason,
if you use static resource references,
you must design your resource
dictionary structure such that
resources intended for further
by-resource use are defined at or near
the beginning of each respective
resource dictionary.
Does this mean that I cannot do something like this in my App.xaml:
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Assets/Colors.xaml"/>
<ResourceDictionary Source="Assets/Brushes.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
Where Brushes.xaml contains SolidColorBrush, LinearColorBrush, etc, definitions that refer to the colors defined in Colors.xaml ?
e.g.
<SolidColorBrush x:Key="OrangeRedBrush" Color="{StaticResource AppOrangeRed}"/>
?
I define my colors in Colors.xaml like:
<Color x:Key="AppOrangeRed">#FFBF3C1F</Color>
I'm getting a runtime error that states it cannot find a resource with key 'AppOrangeRed' for instance.
What are the best practices for organizing Colors and Brushes so they can be reused (where appropriate, and I understand that colors are structs and brushes are reference objects, explained in http://weblogs.manas.com.ar/spalladino/2009/03/02/silverlight-xaml-guidelines/)
Thanks
Rob

I understand it now a little clearer. If you have multiple application-scope resources that sometimes refer to each other, you need to (1) order them in the master dictionary declared in App.xaml, AND you need to include in each file a section that pulls in the dependent pieces. So if I have three files for instance, Brushes.xaml, ScrollViewerStyles.xaml, and ComboBoxStyles.xaml, and ComboBoxStyles.xaml depends on the former two, I would need to simply add to the top of that file:
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Brushes.xaml"/>
<ResourceDictionary Source="ScrollViewerStyles.xaml"/>
</ResourceDictionary.MergedDictionaries>

Rob,
I understand the documentation the same way that you do. I noticed that it is also possible to swap the MergedDictionaries like so, and still receive the same runtime exception:
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Assets/Brushes.xaml"/>
<ResourceDictionary Source="Assets/Colors.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
From the documentation under "Merged Resource Dictionaries" :
In terms of the lookup sequence, a MergedDictionaries dictionary is checked only after checking all the keyed resources of the ResourceDictionary that declared MergedDictionaries. Then, each of the dictionaries within MergedDictionaries is checked, in the inverse of the order that they are declared within the MergedDictionaries property. In other words, the retrieval logic from within the collection of merged resource dictionaries is last in, first out.
It seems like you can override certain Keys defined in ResourceDictionaries by including them in this order, but you're not able to reference a style from one dictionary defined before another in the lookup sequence. This is frustrating and not intuitive.
I guess this would be useful if you had a collection of "blue" styles and wanted to override them with a set of "orange" styles for a different client. You would accomplish this by including the orange keys below the blue keys in your MergedDictionaries collection, so they are found first in the Resource lookup sequence.
Anyways, I share your frustration and hope for a feature like this at some point in the near future.

Related

The right place to put the Styles and Templates for a WPF project

I have a WPF project in MVVM Architecture and I override almost every default style or template. Until now I put all the styles in the App.Resources but it's getting quite messy... Is there a better way to organize those?
You are free to split your App.xaml into multiple pieces and group your resources the way you prefer as most convenient. Just refer them from main app resources, something like that:
<Application.Resources>
<ResourceDictionary>
... something still sits here...
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="StylesHere.xaml" />
<ResourceDictionary Source="StylesThere.xaml" />
<ResourceDictionary Source="StylesEtc.xaml" />
</ResourceDictionary.MergedDictionaries>
Just do not forget about declaration/reference precedence: a resource dictionary can not refer to something that declared in a next resource dictionary. So it can be worth to extract general declarations first dictionary, then put all dependent staff in next dictionaries.

Edit ResourceDictionary from an external assembly

I've been searching around if it is possible for an assembly to change the ResourceDictionary values from another assembly at runtime. So far I've found nothing.
Here´s the deal. I have a UserControl that will work independently so it can be fit into different projects. My UserControl has its own Resources.xaml (Compiled as Resources).
I have a second assembly that it used as a setup tool for this user control. It basically just reads the UserControl Resources.xaml (which is working great) and then replaces the values of the Resources.Xaml. Trouble is, I cannot change the resource values.
Here's the code I use on my setup tool to read the ResourceDictionary:
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/Control;component/Configuration/Resources.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
To change this values I've tried something like:
this.Resources.MergedDictionaries.First()["IsZoomable"] = false;
It does recognize the resources but it wont change it. Is it even possible to change the values of the dictionary of another assembly at runtime or will I need to create an external dictionary that can be acessed by both assemblies?
EDIT:
The user control contains the Resources.xaml. I've defined the Resources in this control like this:
<UserControl.Resources>
<ResourceDictionary Source="Configuration/Resources.xaml"/>
</UserControl.Resources>
The setup tool can acess this resources but I'm not able to change them. When I do, the user control still reads the old values. Resources are defined in the setup tool like so:
<Window.Resources>
<ResourceDictionary Source="pack://application:,,,/Control;component/Configuration/Resources.xaml"/>
</Window.Resources>
Yes, you can change the value of a resource at runtime.
Since you have merged the dictionary, you can change the value like this:
this.Resources["IsZoomable"]=false;
Make sure that the resource key matches the actual key.
If you have merged the dictionary in app.xaml, then you can use:
Application.current.Resources["IsZoomable"]=false;

What is the correct way to reference a resource dictionary?

In my application, I tend to reference my resource dictionaries using a relative path, like so:
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Assets/ResourceDictionaries/SplashScreen.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
However, for one of the libraries I use in my application, (Fluent) the reference to the resource dictionaries I need are different (I believe they're called Pack URI's or something?):
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/Fluent;component/Themes/Office2013/Generic.xaml" />
</ResourceDictionary.MergedDictionaries>
<Color x:Key="{x:Static Fluent:MetroColors.ThemeColorKey}">#60327A</Color>
<vm:MainWindowViewModel x:Key="MainWindowViewModel" />
</ResourceDictionary>
</Application.Resources>
Is there a 'correct' way I should be referencing my resource dictionaries?
Pack URIs are required when the resource is in a different assembly than the one being compiled.
The Fluent URI references the Fluent assembly.
pack://application:,,,/Fluent;component/Themes/Office2013/Generic.xaml
In theory you could reference the current assembly, but I never use them unless I have to because they're such a pain to get right!
The difference is because a URI is used for accessing embedded resources, linked files, or loose files. However the URI may look different, depending on what type of resource is being accessed.
The "pack URI," with the three commas in it, is set up differently from the first URI, because the resource's source is a different type.
For more info, see this MSDN article.

WPF Dynamically change resource file and theme

My project uses a ProjectTheme.xaml file for all WPF windows through out the project.
The ProjectTheme.xaml file references a style theme as follows
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<ResourceDictionary.MergedDictionaries>
<!-- In order to modify the project's theme, change this line -->
<ResourceDictionary Source="/MyProject;component/Themes/WPFThemes/Customized.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
All WPF Windows references WindowBase.xaml
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/MyProject;component/View/WindowBase.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
WindowBase.xaml references customized titlebar Bar1.xaml
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/MyProject;component/Themes/WPFThemes/Bar1.xaml" />
</ResourceDictionary.MergedDictionaries>
Bar1.xaml references ProjectTheme.xaml
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/MyProject;component/ProjectTheme.xaml"/>
</ResourceDictionary.MergedDictionaries>
So the heriarchy is
Window1 references WindowBase.xaml
WindowBase references Bar1.xaml
Bar1 references ProjectTheme.xaml
ProjectTheme.xaml reference the real theme resource file.
This works fine.
Now I want to dynamically change the project theme at run time without quitting the app.
Assuming that I have several theme style files
Customized.xaml
Customized1.xaml
Customized2.xaml
My question is
if it possible to dynamically update ProjectTheme.xaml file at run time to change the line
from
<ResourceDictionary Source="/MyProject;component/Themes/WPFThemes/Customized.xaml" />
to
<ResourceDictionary Source="/MyProject;component/Themes/WPFThemes/Customized1.xaml" />
to achieve my objective?
If yes, how do I do it?
If no, what is the reason and what is the best (other) way to achieve my purpose?
I have tried the following but none of them work: the style does not change.
way 1
Application.Current.Resources.MergedDictionaries.Clear();
Uri NewTheme = new Uri(#"/MyProject;component/Themes/WPFThemes/Customized2.xaml", UriKind.Relative);
ResourceDictionary dictionary = (ResourceDictionary)Application.LoadComponent(NewTheme);
Application.Current.Resources.MergedDictionaries.Add(dictionary);
way 2
Application.Current.Resources.MergedDictionaries.RemoveAt(0);
Uri NewTheme = new Uri(#"/MyProject;component/Themes/WPFThemes/Customized2.xaml", UriKind.Relative);
ResourceDictionary dictionary = (ResourceDictionary)Application.LoadComponent(NewTheme);
Application.Current.Resources.MergedDictionaries.Insert(0, dictionary);
Note:
In my real theme style files (Customized.xaml...) I used a combination of dynamic resource and static resource. Does that matters?
Thanks in advance.
There are a few things to consider here.
First, anything defined with StaticResource will not get updated on a change. If you want a control to support changing the theme at runtime, you need to use DynamicResource so it knows to look for changes.
Your overall approach to changing the theme is correct. The easiest way to accomplish this is using Application-scoped resource dictionaries, making sure your ResourceDictionary is defined in your App.xaml. For adding a new resource, I've used snippets similar to the following:
ResourceDictionary dict = new ResourceDictionary();
dict.Source = new Uri("MyResourceDictionary.xaml", UriKind.Relative);
Application.Current.Resources.MergedDictionaries.Add(dict);
The part you may be confusing yourself over is when using resources within base classes. When you define a resource in a class, the resource will be local to an instance of that type. Think of the XAML compiling into it's own InitializeComponent() method on classes, meaning you can't change the original XAML and expect the changes to go to all instances. On the same note, changing the resources on a class instance doesn't effect other instances.
Since your question really contains two separate concerns (application theming and changing control resources), I would focus on ensuring your application resources are updating properly and using DynamicResource, and hopefully the information I've provided would be sufficient for understanding why certain other resources may not be updating yet.

Static resource shared in merged dictionaries

I'm currently working on having dictionaries of styles and templates that I can dynamically apply to my application. Before this "new wanted" dynamical behavior, I had several resource dictionaries, one for each styled control, that I merged in the App.xaml:
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="ColorsDictionary.xaml"/>
<ResourceDictionary Source="ControlsTemplatesDictionary.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
Now, I'd like my application to be styled, so I decided to merge all my previous resources into a new one called "MyFirstTemplates" and to add only this dictionary to the App.xaml.
New dictionary "MyFirstTemplates.xaml":
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">"
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="ColorsDictionary.xaml"/>
<ResourceDictionary Source="ControlsTemplatesDictionary.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
New App.xaml:
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="MyFirstTemplates.xaml"/>
</ResourceDictionary.MergedDictionaries>
<Style TargetType="{x:Type Window}"/>
</ResourceDictionary>
</Application.Resources>
Note: The default style for the Window is to correct a bug of WPF 4, see Adding a Merged Dictionary to a Merged Dictionary
Now that I have made this change, I cannot use a color resource from "ColorsDictionary.xaml" as a StaticResource in "ControlsTemplateDictionary.xaml" anymore. If I change back to merging these files in the app.xaml, everything works. To make it work, I have to change these StaticResource for DynamicResource. Do you have any idea why this doesn't work anymore?
Thank you :-)
By moving the dictionaries out of App.xaml the resources from each dictionary aren't in the other's resource tree during loading of MyFirstTemplates.xaml. Your original setup first loaded ColorsDictionary which was then available through App resources to ControlsTemplatesDictionary while it loaded. In your new setup, in order for the color resource to be available in App resources it needs to be loaded through MyFirstTemplates, which in turn requires loading of both dictionaries, which in turn requires access to the color resource... so it's sort of an infinite loop of references that can't be resolved statically. DynamicResource can wait until everything is loaded and then access the color without issue.
To fix either use Dynamic or merge ColorsDictionary directly into ControlsTemplatesDictionary.
Great answer by John explaining why this is happening.
So the problem is that when using merged dictionaries within a merged dictionary, the inner dictionaries can't "use" each other as StaticResource.
Basic solutions:
Use DynamicResource
Use just a single level of hierarchy from App.xaml when using StaticResource
Both of these solutions have problems. DynamicResource has a performance problem. The 2nd solution limits you on how you organize your XAML resources.
Alternative solution:
I created a small simple program (provided below in GitHub) that will run as a pre-build event and merge XAML files from a folder into one long .XAML file. Well, they need to be with a different extension (.txaml), otherwise they will be compiled.
This allows to structure resources folders and files however you want, without WPF’s limitations. StaticResource and the designer will work always.
The code in GitHub contains a simple solution that contains the merging program. It merges 2 folders into 2 files. One for App.xaml resources and the other for Generic.xaml resources. The .xaml files in a "Controls" project (There's also "Main" project).
Blog post explaining this

Resources