Edit ResourceDictionary from an external assembly - wpf

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;

Related

Multiple ResourceDictionary with same DataTemplate key?

I have a ResourceDictionary such as (MyResourceDictionary):
<ResourceDictionary xmlns
.....
>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="SeriesTwo.xaml" />
</ResourceDictionary.MergedDictionaries>
<DataTemplate x:Key="SeriesDetailedInformation">
<StackPanel>
......content...
</StackPanel>
</DataTemplate>
</ResourceDictionary>
SeriesTwo.xaml looks like this and also has the DataTemplate with the same name
<ResourceDictionary xmlns=
.....
>
<DataTemplate x:Key="SeriesDetailedInformation">
<DataGrid>
......content...
</DataGrid>
</DataTemplate>
</ResourceDictionary>
On my view page, which SeriesDetailedInformation data template gets used? Does it depend on which resource dictionary I reference first on my page?
Any good links and other reading material regarding this topic is also appreciated.
A couple of things from this page seem relevant to your question:
Lookup behavior within the MergedDictionaries collection searches the
last-added ResourceDictionary first, and the search stops as soon as a
requested key is found. In other words, the retrieval logic from the
collection of merged resource dictionaries is last in, first out.
And:
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.
From this I gather that the DataTemplate defined in your main dictionary would be used first, then SeriesTwo, and any referenced before/above SeriesTwo after that.
Also, why not just try it out and see what happens? I maintain a solution I call "DumbCrapTestApp" where I test stuff like this when I'm curious about how something works. It has a console app for when I just need to test something within the language, and a WPF and a Silverlight app for when I want to try something there. If I needed a WinForms app, I'd just add one and do my thing there. I simply put whatever code I want to test into the relevant app, set it as my Startup Project, and away I go. It's really useful for figuring out these little things and I've even used it to get clarification and verify answers before I post them here on SO.

How can i make a prefix so i can address a folder in xaml

I always have problems making new prefixes in xaml. Most of the time, i get the message that the URI cannot be found in the assembly. My setup:
I have a WPF project (in a solution with class libs and asp.NET projects) with a MainWindow.xaml file. The XAML starts with : Window x:Class="MainWindow" ... .
So as default, there's no namespace given to it. In that same project i made a folder "Folder". In that folder, i have resx-files. What i need to do is make a prefix in xaml so i can address those files. I was thinking of :
xmlns:p="clr-namespace:WpfApplication.Folder"
and then for my controls
<Label Content="{x:Static p:NameResxFile.KeyName></Label>
However, the prefix generates the "URI cannot be found in the assembly" error. I'm i just failing at making prefixes?
Thanks in advance.
EDIT
If you cannot make a namespace ref to a folder, what is happening here?
xmlns specifies namespaces, it does not bother with folders or files, if you need access to an external resource you can load it into your control's resources via ResourceDictionary.
There was something like this i think:
<Window.Resources>
<ResourceDictionary x:Key="ExternalRes" Source="Folder/File.xaml"/>
....
</Window.Resources>
To reference an element of resource dictionary you should add that dictionary to your control's Resources collection or register it in the App.xaml file. After that you could just use StaticResource extension to get access to the element. Your code will look like this:
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/Folder/NameResxFile.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
<!-- ... -->
<Label Content="{StaticResource KeyName}"/>
Things get trickier if you want to put the resource dictionary to another assembly and reference it. For this purpose refer to PackURIs in WPF article.

Headaches with configuring themed resources from c# (Windows Phone)

I have Windows Phone App and I want to have a different themed resources based on whether the user has a dark or light theme and which accent color they have chosen.
In my app initialize code I detect the users theme and accent color then load the appropriate Resource Dictionary, Dark, Light etc. I add the Resource Dictionary to the App.Current.Resources.MergedDictionaries collection.
The problem comes when I want to reference a value in the (dynamically loaded) from my App.Xaml. In the below example the key is "DefaultBackgroundImageOpacity". For some reason the values in the ResourceDictionary that I load into the MergedDictionaries collection never get found when the App.Xaml parsing happens. I've played with loading the resources before I call InitializeComponent() and after. Neither seem to resolve the issue.
Any ideas?
// Simplified version of adding some xaml...
// Note I'm loading the key "DefaultBackgroundImageOpacity"
this.Resources.MergedDictionaries.Clear();
var myTestXaml = "<ResourceDictionary xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation' xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml' xmlns:System='clr-namespace:System;assembly=mscorlib'> <System:Double x:Key='DefaultBackgroundImageOpacity'>0.2</System:Double></ResourceDictionary>";
this.Resources.MergedDictionaries.Add((ResourceDictionary)XamlReader.Load(myTestXaml));
// This always fails saying that the key "DefaultBackgroundImageOpacity"
// can not be foudnd - even though it was loaded just above
InitializeComponent();
// Phone-specific initialization
InitializePhoneApplication();
I suspect your App.Xaml contains something like this:-
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/somedictionary.xaml" />
...
</ResourceDictionary.MergedDictionaries>
...
</ResourceDictionary>
</Application.Resources>
If that is so then InitializeComponent will replace the default ResourceDictionary which you have manipulated in your code hence your changes to MergeDictionaries will not be present since that instance of a ResourceDictionary is no longer referenced.
If you are not setting up any MergedDictionaries in the App.Xaml then make sure you add resources directly to Applicaiton.Resources and are not creating an new instance of ResourceDictionary in the xaml.

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

Splitting Generic.Xaml - problem loading MergedDictionaries

working on a pretty large library of controls the generic.xaml gets simpy out of control. I want to split it pretty much by control (although once for a namespace which contains only some simple ones for now).
For that, i am adding more Resource Dictionaries.
Then, in the generic.Xaml I add:
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Themes/Generic.Core.xaml" />
</ResourceDictionary.MergedDictionaries>
And... get a loading error.
What is the correct URL?
This is:
Within a dll file containing only controls (not an exe or something).
The generic.Xaml and the other one (for now: Generic.Core.xaml) live in the same assembly, same folder.
I read the MSDN part about the pack URL's but that sounds really complicated for something that looks SO easy.
;) Please help.
Found it:
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/Tradex.Presentation;component/Themes/Generic.Core.xaml" />
</ResourceDictionary.MergedDictionaries>
That works.
Needs to prefix the assembly (replace Tradex.Presentation with assembly name) and start with the component item.

Resources