How to change just one merged resource dictionary in WPF? - wpf

Our application uses a ResourceDictionary for its styles. This resource dictionary itself contains resource dictionaries thus:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="BridgeIt5/General.xaml" />
<ResourceDictionary Source="BridgeIt5/Brushes.xaml" />
<ResourceDictionary Source="BridgeIt5/Constants.xaml" />
<ResourceDictionary Source="BridgeIt5/Button.xaml" />
...
</ResourceDictionary.MergedDictionaries>
So, now depending on the build configuration we just want to change the Brushes.xaml merged resource dictionary. How can this be done?

If you can set the configuration at runtime you can do this during startup:
var resourceDictionary = new ResourceDictionary
{
// Pick uri from configuration
Source = new Uri("BridgeIt5/Brushes.xaml"),
};
Application.Current.Resources.MergedDictionaries.Add(resourceDictionary);

Related

WPF Using multiple Resource Dictionaries from multiple projects

I have two class Library projects:
Project A.Themes
Project B.Themes
Project A.Themes is my base Themes Project.
Project B.Themes using A.Themes and have new styles and some of the resources have keys that already defined in A.Themes.
We want to use this two themes in our Project, and if we use a resource that is defined in both of the project we want to take the resource from B.Themes.
This is our code:
A.Themes have few files of styles:
Brushes.xaml
Buttons.xaml
CheckBox.xaml
etc..
we load them in Bundle.Xaml:
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/A.Themes;component/Assets/Brushes.xaml"/>
<ResourceDictionary Source="pack://application:,,,/A.Themes;component/Assets/Buttons.xaml"/>
<ResourceDictionary Source="pack://application:,,,/A.Themes;component/Assets/CheckBox.xaml" />
</ResourceDictionary.MergedDictionaries>
B.Themes have the same Files:
Brushes.xaml
Buttons.xaml
CheckBox.xaml
we load them in Bundle.Xaml and adding the bundle of A.Themes:
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/A.Themes;component/Bundle.xaml"/>
<ResourceDictionary Source="pack://application:,,,/B.Themes;component/Assets/Brushes.xaml"/>
<ResourceDictionary Source="pack://application:,,,/B.Themes;component/Assets/Buttons.xaml"/>
<ResourceDictionary Source="pack://application:,,,/B.Themes;component/Assets/CheckBox.xaml" />
</ResourceDictionary.MergedDictionaries>
In our Project we load them in App.xaml:
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/A.Themes;component/Bundle.xaml"/>
<ResourceDictionary Source="pack://application:,,,/B.Themes;component/Bundle.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
the problems are:
1. it not always takes the resources from B.Themes and we can't find out why.
2. if i remove the reference to A.Themes/Bundle.xaml from app.xaml the project can't find resources from A.Themes even though it's included in B.Themes/Bundle.xaml
note:
we have refernce to A.Themes project in B.Themes
and refernce to A.Themes and B.Themes in main project
can someone please help me to understande what is going on here?
thanks!
The loading order isn't quite what you expect. From MSDN:
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
https://learn.microsoft.com/en-us/dotnet/framework/wpf/advanced/merged-resource-dictionaries
So the dictionaries that merge into Bundle.xaml of assembly A are actually loaded after the other ones.
Please refer to the following link for more information and an example of the same behaviour: https://social.msdn.microsoft.com/Forums/vstudio/en-US/3bea80f9-d1db-4cb7-ae7a-77a02eaf4ec9/resourcedictionary-load-order?forum=wpf

Merge XAML file into Xaml code

I want to merge one XAML file, that only contains styles into the Window.Resource of my application.
I tried using MergedDictionaries as described in
WPF Reference custom resource defined in another xaml file .
First:
The File is named "MyStyleDocument.xaml" and contains different WPFstyles without using x-Keys.
<ResourceDictionary
xmlns=" http://schemas.microsoft.com/winfx/2006/xaml/presentation "
xmlns:x=" http://schemas.microsoft.com/winfx/2006/xaml ">
<Style TargetType="{x:Type TabControl}">
(...)
</Style>
<Style TargetType="{x:Type XY}">
(...)
</Style>
.
.
.
</ResourceDictionary>
Second:
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="MyStyleDocument.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
I get the Result: "An error occurred while finding the resource dictionary "MyStyleDocument.xaml"." The file is located in the folder of my project.
My question is:
How can I cleverly include one XAML file containing different styles into another XAML code?
The structure of my project is:
WPFApplication(file folder1) -> File Folder1 includes WPFApplication(file folder2);WPFApplication.sln; WPFApplication.suo; WPFApplication.v11.suo
File Folder 2 includes: bin (file folder2.1); obj(file folder2.2); Properties(filefolder2.3); App.xaml; App.xaml.cs; Application.ico; MainWindow.xaml.cs; MyStyleDocument.xaml; WpfApplication.csproj
Here is a demo wpf project:
-WpfApplication
|--App.xaml
|--MyStyle[here is a floder]
|---MyStyleDocument1.xaml
|---MyStyleDocument2.xaml
|---MyStyleDocument3.xaml
Then you can edit App.xaml like this:
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<!--add style file here like this-->
<ResourceDictionary Source="/MyStyle/MyStyleDocument1.xaml" />
</ResourceDictionary.MergedDictionaries>
<ResourceDictionary>
<Application.Resources>

Cannot locate resource 'themes/style/master.xaml'

Here is my Solution explorer:
I want to make this:
ResourceDictionary dictionary = new ResourceDictionary();
dictionary.Source = new Uri("../Themes/Generic.xaml", UriKind.RelativeOrAbsolute);
Application.Current.Resources.MergedDictionaries.Add(new ResourceDictionary());
in the constructor of PageWithoutMaster.
I get error: Cannot locate resource 'themes/style/master.xaml'
What am I doing wrong? Why is it adding the style folder in the path?
As dkozl pointed out, you can fix this by using a Pack URI and including the assembly name in your path
Assuming your resource file looks something like this:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApp4.Themes">
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/Style/Master.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
Get your assembly name from your project properties:
Then update to this value instead:
<ResourceDictionary Source="WpfApp4;component/Style/Master.xaml" />

How to reference an assembly (residing in relative path) using URI pack syntax?

I have this kind of the solution folders structure:
C:\TfsBindRoot\MySln\Development\Sources\WpfProj
C:\TfsBindRoot\MySln\Development\Sources\Resources
Resources project is referenced in WpfProj.
I tried different variants such as
<ResourceDictionary Source="pack://application:,,,//Resources" />
//and
<ResourceDictionary Source="pack://application:,,,/../Resources" />
//and
<ResourceDictionary Source="pack://application:,,, ../Resources" />
But it does not see the library.
It appears that the path you are providing in not in correct format.
The path depends it the resources are in the same package or different package. Since your question does not provide details of your application pack, check MSDN site for details for the proper format.
UPDATE
Resource file in subfolder of referenced assembly ->
"pack://application:,,,/ReferencedAssembly;component/Subfolder/ResourceFile.xaml"
Resource file in versioned referenced assembly ->
"pack://application:,,,/ReferencedAssembly;v1.0.0.0;component/ResourceFile.xaml"
UPDATE 2
I tried and it worked. "ResourceLibrary" was the assembly name.
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/ResourceLibrary;component/Resources/ResourceFile.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
One thing you might be missing is the ResourceFile.xaml where your might have kept all your resources. My ResourceFile.xaml looks like:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<SolidColorBrush x:Key="MyBrush" Color="Cornsilk"/>
</ResourceDictionary>
<ResourceDictionary Source="pack://application:,,,/Resources;component/YourResources.xaml" />
YourResources.xaml replace with name of xaml file which contains resources.

Trouble referencing a Resource Dictionary that contains a Merged Dictionary

I have a library, CommonLibraryWpfThemes, with several Resource Dictionary XAML files in it. My Themes/Generic.xml file contains a ResourceDictionary.MergedDictionaries declaration that merges all the other files together.
Generic.xaml
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary
Source="/CommonLibraryWpfThemes;component/ResourceDictionaries/BrushDictionary.xaml" />
<ResourceDictionary
Source="/CommonLibraryWpfThemes;component/ResourceDictionaries/TextBlockDictionary.xaml" />
<ResourceDictionary
Source="/CommonLibraryWpfThemes;component/ResourceDictionaries/LabelDictionary.xaml" />
<ResourceDictionary
Source="/CommonLibraryWpfThemes;component/ResourceDictionaries/ButtonDictionary.xaml" />
<ResourceDictionary
Source="/CommonLibraryWpfThemes;component/ResourceDictionaries/WindowDictionary.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
In my application project, I have a reference to CommonLibraryWpfThemes, and I explicitly reference Generic.xml in my App.xaml file.
App.xaml -- FAILS
<Application
x:Class="MyApp.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Application.Resources>
<ResourceDictionary
Source="/CommonLibraryWpfThemes;component/Themes/Generic.xaml" />
</Application.Resources>
</Application>
This doesn't work. I get the following error when I run my app:
System.Windows.Markup.XamlParseException occurred
Message="Cannot find resource named '{_fadedOrangeBrush}'. Resource names are case sensitive. Error at object 'System.Windows.Setter' in markup file 'CommonLibraryWpfThemes;component/ResourceDictionaries/WindowDictionary.xaml' Line 18 Position 13."
Source="PresentationFramework"
LineNumber=18
LinePosition=13
If I place the contents of Generic.xaml into App.xaml directly, everything works fine:
App.xaml -- SUCCEEDS
<Application
x:Class="MyApp.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary
Source="/CommonLibraryWpfThemes;component/ResourceDictionaries/BrushDictionary.xaml" />
<ResourceDictionary
Source="/CommonLibraryWpfThemes;component/ResourceDictionaries/TextBlockDictionary.xaml" />
<ResourceDictionary
Source="/CommonLibraryWpfThemes;component/ResourceDictionaries/LabelDictionary.xaml" />
<ResourceDictionary
Source="/CommonLibraryWpfThemes;component/ResourceDictionaries/ButtonDictionary.xaml" />
<ResourceDictionary
Source="/CommonLibraryWpfThemes;component/ResourceDictionaries/WindowDictionary.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>
Maybe I'm going about this in the wrong way. My goal is to make it easy to reference all my theme resources from multiple applications without having to list out all the individual files. Is there a recommended way to do this? (Note: I'm not trying to switch between multiple themes--I just have one theme.)
As a bonus, it would be nice if someone could tell me how to reference resources in an external library without breaking the designer in Visual Studio.
Thanks.
EDIT:
I tried wrapping the ResourceDictionary in a ResourceDictionary.MergedDictionary element, but that also didn't work (I get the same error):
<Application
x:Class="MyApp.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary
Source="/CommonLibraryWpfThemes;component/Themes/Generic.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>
Answered a similar question here earlier, see Adding a Merged Dictionary to a Merged Dictionary question.
This is an optimization bug, see Microsoft Connect / DefaultStyleKey style not found in inner MergedDictionaries:
On the creation of every object in
XAML, if a default style is present
(i.e. style w/ a key of Type) that
style should be applied. As you can
imagine there are several performance
optimizations to make that (implied)
lookup a light weight as possible. One
of them is that we don’t look inside
Resource Dictionaries unless they are
flagged as “containing default
Styles”. There is a bug: if all your
default styles are nested in merged
dictionaries three levels deep (or
deeper) the top dictionary does not
get flagged so the search skips it.
The work around is to put a default
Style to something, anything, in the
root Dictionary.
So adding a dummy style to the root dictionary fixes this. Example
<Application x:Class="MyApp.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary
Source="/CommonLibraryWpfThemes;component/Themes/Generic.xaml" />
</ResourceDictionary.MergedDictionaries>
<!-- Dummy Style, anything you won't use goes -->
<Style TargetType="{x:Type Rectangle}" />
</ResourceDictionary>
</Application.Resources>
</Application>
Check your constructor in App.xaml.cs calls InitializeComponent() - this is what merges the resource dictionaries...
You should not have to reference generic.xaml at all, it has built-in support. This however means that it provides default styling, which you do not set explicitly. Explicitly set styles/templates need to be attainable from explicitly referenced res dictionaries.
(EDIT for clarity)
One exception to this is the App.xaml, where defined resources become accessible by the whole app, without requiring to reference any specific resource dictionary. The resource itself, would have to be accessible by name.
The reason why this fails
<Application.Resources>
<ResourceDictionary
Source="/CommonLibraryWpfThemes;component/Themes/Generic.xaml" />
</Application.Resources>
is, I think, because you didn't wrap it in a MergedDictionary wrapper, adding it to merged dictionaries. Adding directly to resources works only for resources you declare locally, e.g. the styles, etc. themselves.
However, as I said before, you shouldn't have to merge generic.xaml anywhere, maybe you should just refactor brushes and other resources used outside styles, and merge only those resources in app.xaml.
Also note that styles do not have to be in generic.xaml to have "default style" behaviour - if a style with key equal to the type of the element is accessible to it (globally, or in local resources), then it will use the style as a default style. The generic.xaml is just a convenience.
Check this answer.
For other custom brushes, etc, you need to reference those resources explicitly.
You should also check the contents of the WindowDictionary.xaml, this error has a certain smell about it.
I was getting this error in my unit tests and Chris' answer from above gave me the clue I needed. Basically on my first tested method, I put:
MyApplication.App app = new MyApplication.App();
app.InitializeComponent();
And suddenly it could find my template for my pages. Note: this does mean that you have to check to see if an instance of your App already exists if you are unit testing your App.cs as well.
My solution is here, click Workarounds.

Resources