Splitting Generic.Xaml - problem loading MergedDictionaries - wpf

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.

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.

Why can't I move my resource dictionary in Silverlight?

For some reason the following code is giving me an exception.
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/PPCa.Common.Infrastructure;component/Skins/Default.xaml"/>
<ResourceDictionary>
<app:ResourceWrapper x:Key="ResourceWrapper" />
<app:NotOperatorValueConverter x:Key="NotOperatorValueConverter" />
</ResourceDictionary>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
Here is the exception:
System.Windows.Markup.XamlParseException occurred
Message=Failed to assign to property 'System.Windows.ResourceDictionary.Source'. [Line: 11 Position: 44]
LineNumber=11
LinePosition=44
StackTrace:
at System.Windows.Application.LoadComponent(Object component, Uri resourceLocator)
InnerException:
Line 11 is this:
<ResourceDictionary Source="/PPCa.Common.Infrastructure;component/Skins/Default.xaml"/>
Am I somehow merging my dictionaries wrongly?
Why is no one answering my questions? Am I blacklisted or something. People used to help me out quite a bit more. Anyway, I discovered my problem. The exception was a red herring. The problem had nothing to do with my application resource definition. The problem I was having deep inside my resource dictionaries. I was merge-referencing a dictionary inside my dictionary that no longer existed. That was difficult to figure out.
Are these dictionaries in the same XAP? If so, a relative path should work. I have used all relative paths to merge in dictionaries without any issue. Here is an example of mine:
<Application.Resources>
<ResourceDictionary x:Name="appDictionary">
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary x:Name="ControlStylesDictionary"
Source="Resources/Styles/ControlStyles.xaml" />
<ResourceDictionary x:Name="MenuStylesDictionary"
Source="Resources/Styles/MenuStyles.xaml" />
</ResourceDictionary>
</Application.Resources>
That is the only difference I can see in your XAML.
If the dictionary is in another XAP, you need to download the XAP and load the dictionary from there.
I ran into the same problem, same generic error message and found the solution. This appears to be a bug IMHO.
I am building an app for .Net, SL5, WP7.1. To do this I put the code in the .Net app and add source links for the other projects. Works great.
My main app projects are called MyApp.Net, MyApp.SL, and MyApp.WP. However, I make the default namespace and project output for these projects simply MyApp. Again, works great as it should.
I place my resources in projects MyApp.Resources.Net, MyApp.Resources.SL, and MyApp.Resources.WP. There is a bit of a glitch with using the VS source links as Expression Blend wants direct access to the physical files and gets confused when (for example) the MyApp.Resources.WP project contains a source link to a MyStyles.xaml file in the MyApp.Resources.Net projects. Therefore, all my resource projects actually contain physical files. This works fine also, I just have to synch the files manually. No problems so far.
However, for my resource projects I change the namespace and output files to MyApp.Resources. This keeps my application code simple also, regardless the platform it's built for, the namespace is the same.
Yes, I know this is a bit complicated but it allows me to build for 3 platforms (technically 5 if you include Blendability and UnitTesting) all with the same code base.
To continue, if I create a ResourceDictionary as such
<ResourceDictionary Source="/MyApp.Resources;component/Styles/TextStyles.xaml"/>
I get the Failed to assign to property 'System.Windows.ResourceDictionary.Source' etc...
In short, I discovered that if the assembly name contains a '.' this error shows up. For example if I change my project names to simply 'Resources' it works fine. Or if I leave my projects with their default build names of 'MyApp.Resources.WP' it also works fine.
This has nothing to do with changing my resource dll output file names, I change them all day long and it works great, but if they contain a '.' I get the above error. For example, I can change the output name to "MyAppResourceThatWorks" (leaving the project name as MyApp.Resources.WP and load it in App.xaml with
<ResourceDictionary Source="/MyAppResourceThatWorks;component/Styles/TextStyles.xaml"/>
Works great. Change the output name to "MyAppResourcesThatDoNot.Work" and load it with
<ResourceDictionary Source="/MyAppResourceThatDoNot.Work;component/Styles/TextStyles.xaml"/>
Fails.
Yes, I tried changing the assembly properties, etc. etc. It's a load issue with Pack Uri's.

How can I add a ResourceDictionary defined in a subfolder to App.xaml?

All of the code samples I've found so far reference a Resource Dictionary that's not in a project subfolder (the dictionary is at the root of the project). I have mine in a subfolder, and I can't figure out how to get WPF to find it. This doesn't work...
<Application.Resources>
<ResourceDictionary Source="/Skins/Black/GlossyButtons.xaml"/>
</Application.Resources>
The ASP.Net tilde syntax doesn't seem to work either.
Is this the best way to include a ResourceDictionary in your app so that WPF can find resources? My goal in this particular case is to apply a style defined in GlossyButtons.xaml to all of the buttons in my app.
Try the Pack URI syntax
http://msdn.microsoft.com/en-us/library/aa970069.aspx
<Application.Resources>
<ResourceDictionary Source="pack://application:,,,/Skins/Black/GlossyButtons.xaml"/>
</Application.Resources>
Discovered the problem - WPF couldn't find an assembly referenced in GlossyButtons.xaml, but didn't show the actual error (that there was a problem with the .xaml) until I had compiled and executed several times. The error displayed (at first) was that GlossyButtons.xaml couldn't be located.
That was a little confusing.

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.

Resources