How to dynamically change resource dictionary source name in wpf? - wpf

<Application x:Class="CustControls.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="MainWindow.xaml">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/ControlLibrary;component/Styles/ControlResource.xaml"/>
<ResourceDictionary Source="StringLocalization/Dictionary_fr-FR.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
Now i want change the ResourceDictionary's source name Source="StringLocalization/Dictionary_fr-FR.xaml" to Source="StringLocalization/Dictionary_en-US.xaml"
What should i do for that.

The MSDN-documentation explains,
"In code, you do not set the Source property. Instead, you must obtain
a ResourceDictionary object by either creating one or loading one. One
way to load an existing ResourceDictionary to call XamlReader.Load on
an existing XAML file stream that has a ResourceDictionary root, then
casting the XamlReader.Load return value to ResourceDictionary."
It looks like you can only obtain a ResourceDictionary by either creating one or loading one.
It's also important to understand your purpose with the use of ResourceDictionaries. If you are meant to use them as a 'shared resource' you cannot build the dictionaries using 'Embedded Resource' action. Make sure they are marked as 'Content' and linked properly to their path-locations. Furthermore, its also important to understand how Merged Dictionaries behave in regards to which resource is chosen over the other (taken from the MSDN-documentation):
Resources in a merged dictionary occupy a location in the resource
lookup scope that is just after the scope of the main resource
dictionary they are merged into. Although a resource key must be
unique within any individual dictionary, a key can exist multiple
times in a set of merged dictionaries. In this case, the resource that
is returned will come from the last dictionary found sequentially in
the MergedDictionaries collection. If the MergedDictionaries
collection was defined in XAML, then the order of the merged
dictionaries in the collection is the order of the elements as
provided in the markup. If a key is defined in the primary dictionary
and also in a dictionary that was merged, then the resource that is
returned will come from the primary dictionary. These scoping rules
apply equally for both static resource references and dynamic resource
references.
Looking at your code, it seems that you just want to load another ResourceDictionary into your application. If that's all you want then probably adding it to the MergedDictionaries collection may already suffice.
If you want to load one on runtime you can use the following code (or similar to). Just make sure that you don't embed your resources:
try
{
string path = #".\Themes\Dictionary1.xaml";
var xmlTextReader = new XmlTextReader(path);
var resourceDictionary = (ResourceDictionary)XamlReader.Load(xmlTextReader);
Application.Current.Resources.MergedDictionaries.Clear();
Application.Current.Resources.MergedDictionaries.Add(resourceDictionary);
}
catch (Exception exception)
{
Debug.WriteLine(exception.Message);
}
Here's the code in case you need it. Let me know if this helps.

Related

How to provide DataTemplate from a prism module

I've a WPF application, with Prism, we are using some modules(and more globally, a lot of micro-services).
I'm searching for the best practices to provide to the application a template that could be used to represent a model.
Since I've to do this from a module, I cannot just create a ResourcesDictionary and add it to the App resources.
How would you do this? Our objective is to have a good isolation of features.
I think you've not really fully explained your purpose or what you have here. With that proviso in mind.
If you created a regular resource dictionary in an app you can merge that in app.xaml.
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Dictionary1.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
And you can then target resources in there in markup using x:Key or implicit styling. And templating which targets a datatype.
What that does is add the things in your resource dictionary to a sort of dictionary. ( It's not exactly a dictionary ) It has key and value.
That has scope of your entire application.
You can reference the things in there and switch them out. You can merge another resource dictionary in code. Once they're in there though, they are there to stay until you shut the app down or clear those resources.
You can read a resource dictionary:
ResourceDictionary rd = new ResourceDictionary
{
Source = new Uri("Dictionary1.xaml", UriKind.Relative)
};
And merge it:
Application.Current.Resources.MergedDictionaries.Add(rd);
If you merge a resource dictionary in markup, it doesn't even have to be compiled. I never tried that in code but I guess you might well find you could merge a "loose" uncompiled resource dictionary. If it didn't work directly you could definitely xamlreader.Load or .Parse an uncompiled rd into one in memory.
That adds to application scope. If you wanted that then maybe you ought to just merge your resource dictionaries in app.xaml though.
If you want scope, then windows, usercontrols etc etc all have resources. You can merge a resource dictionary in at pretty much any level you fancy and it'll then have a narrower scope than application.

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;

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

Why are absolute uri's required for merged dictionaries in Generic.xaml?

Consider a File | New Project of a WPF Application that contains:
A new custom control named CustomControl1
Two new resource dictionaries named Dictionary1 and Dictionary2
Take the generated style out of Generic.xaml and move it to Dictionary2. Then merge Dictionary2 into Dictionary1 and Dictionary1 into Generic like this:
<!--Generic.xaml-->
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/Themes/Dictionary1.xaml"/>
</ResourceDictionary.MergedDictionaries>
<!--Dictionary1.xaml-->
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Dictionary2.xaml"/>
</ResourceDictionary.MergedDictionaries>
Then, add an instance of CustomControl1 into MainWindow's grid. (This part is necessary to reproduce the issue. The project always compiles fine - only at runtime does the issue show up, and the dictionaries must be referenced.)
In Dictionary1.xaml I am merging in another dict in the same folder, so a simple Source="Dictionary2.xaml" works. Yet in Generic.xaml I must use an absolute URI. If I change the above to be Source="Dictionary1.xaml" without the pack://application stuff then I get a XamlParseException caused by an IOException "Cannot locate resource 'dictionary1.xaml'" when it tries to construct the MainWindow.
My Question: What's special about generic.xaml regarding relative URI resolution, and why?
Excuse me because I have no ability to write comments so I post this as an answer.
I have the same situation and everything works fine for me. I don't need to put "pack://application" in the path in Generic.xaml. But only when the output type of an assembly is "Windows Application".
For "Class library" I need to add assembly name to the path (Source="/ClassLibarayAssemblyName;component/Themes/Dictionary1.xaml") becasue without it WPF engine tries to look for Dictionary1.xaml in application's main assembly.
Target framework in both cases is ".NET Framework 4 Client Profile"
Just a guess: generic.xaml needs to be accessible from outside assemblies as well, so it's a way to ensure that the resources can be found from anywhere, using absolute URIs. As I said, it's just a stab in the dark, not sure.

Proper usage of Merged Resource Dictionaries in Silverlight 3

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.

Resources