Resources not resolving from ResourceDictionaries - wpf

I'm having problems resolving Resources from my ResourceDictionaries.
I decided to refactor my rather large ResourceDictionary into individual dictionary files, organized into subfolders.
I have a ResourceLibrary.xaml under Resources:
<ResourceDictionary x:Class="MyProject.Resources.ResourceLibrary"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<ResourceDictionary.MergedDictionaries>
<!-- Colours -->
<ResourceDictionary Source="Colors/ConnectedCellColor.xaml" />
<!-- Brushes -->
<ResourceDictionary Source="Brushes/ConnectorCellBrush.xaml" />
<!-- Control Templates -->
<ResourceDictionary Source="ControlTemplates/ConnectorCellTemplate.xaml" />
<!-- Base Styles -->
<ResourceDictionary Source="BaseStyles/ConnectorBaseStyle.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
The class is there for a reason, in the code behind, I can add [Export(typeof (ResourceDictionary))] so MEF can find it.
I have a View: (simplified)
<UserControl x:Class="MyProject.ConnectorCellView"
Style="{StaticResource ConnectorBaseStyle}"
</UserControl>
ConnectorBaseStyle:
<ResourceDictionary>
<Style x:Key="ConnectorBaseStyle" TargetType="UserControl">
<Setter Property="Template" Value="{StaticResource ConnectorCellTemplate}" />
</Style>
</ResourceDictionary>
The template has StaticResources to try and get a Brush and a colour.
All of these StaticResources will not resolve any more.
I thought it might have been an order issue, but since these resources are contained in a plugin to my main program, I use MEF and ImportMany to get all the exported ResourceDictionaries, and in my Caliburn.Micro bootstrapper:
public void OnImportsSatisfied()
{
foreach (ResourceDictionary resourceDictionary in ResourceDictionaries)
{
Application.Resources.MergedDictionaries.Add(resourceDictionary);
}
}
(Neat trick I found somewhere)
I can actually run my program, and when that view is created it throws an exception when trying to set the style:
System.InvalidCastException
Unable to cast object of type 'MS.Internal.NamedObject' to type System.Windows.FrameworkTemplate'.
The only information I've found relating to this has to do with the order resources are defined, but from the order I have them in ResourceLibrary, it should work.
When the exception is thrown, I can examine Application.Current.Resources.MergedDictionaries,
and see the resources.
I've tried various ways of specifying the Source in ResourceLibrary
<ResourceDictionary Source="/MyProject;component/Resources/BaseStyles/ConnectorBaseStyle.xaml" />
etc, no effect on finding them. These resources are only used by the plugin code.
The only thing that seemed to work was changing all the StaticResources to DynamicResources
which doesn't make sense to me, if it is an order issue, why would Static work when they're all in the same file?
Some of my styles used BasedOn, and they don't work with DynamicResource.
Can you help me understand why this is happening, and how to make it work?

It is an ordering problem but not with the ordering of your merging - it's with the order of loading. Here's basically what happens when the ResourceLibrary dictionary loads:
ConnectedCellColor instantiates
ConnectedCellColor loads into ResourceLibrary
ConnectorCellBrush instantiates
ConnectorCellBrush loads into ResourceLibrary
ConnectorCellTemplate instantiates
ConnectorCellTemplate loads into ResourceLibrary
ConnectorBaseStyle instantiates
ConnectorBaseStyle loads into ResourceLibrary
The problem here is that where before with your single file you had a single instantiation step, you now have that broken up into multiple steps, each of which happen independently. When ConnectorBaseStyle is instantiated ConnectorCellTemplate has been loaded but ResourceLibrary's contents aren't known to ConnectorBaseStyle at this point. With DynamicResource this isn't a problem because those references can just resolve at step 8, but StaticResource requires immediate resolution at step 7.
The simplest fix is to use Dynamic wherever you can. For places that require Static (like BasedOn) you need to guarantee that the resource will be available during instantiation, either by also merging, for example, ConnectorCellTemplate into ConnectorBaseStyle, or by merging everything that's needed into App.xaml which is available to everything. This can make things complicated and hard to manage as you get more files and do merging into multiple places but at least the resource system is smart enough to recognize duplicates so in the case above you would still only get a single instance of ConnectorCellTemplate even though it is being merged at two places.

Related

How to correctly reference external xaml styles in a WPF application?

I believe my question is fairly simple and yet I am having difficulty implementing it successfully. I simply wish to extract the styling of elements in my WPF application because the xaml is rather crowded and xaml is often duplicated.
I therefore wish to place the styling in an external xaml file, in the form of a resource dictionary, then reference that file in the resources section of my code.
I have the following .xaml file:
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style x:Key="PTextBox" TargetType="TextBox" x:Name="PTextBox">
<Setter Property="Foreground" Value="#FFA1C8E7"/>
<Setter Property="BorderBrush" Value="#FFA1C8E7"/>
</Style>
And I reference the dictionary here:
<UserControl.Resources>
<ResourceDictionary x:Key="PegasusStyles">
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="../../Resources/Styles/PegasusStyles.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
Visual studio has resolved the file location so I know this reference is correct.
The text box the styles are applied to then references the style:
<TextBox Style="{StaticResource PTextBox}"/>
If left as a static resource I get a xaml parse error like so:
An unhandled exception of type 'System.Windows.Markup.XamlParseException' occurred in PresentationFramework.dll
And if I make the resource dynamic then the styles simply do not get applied at runtime.
I'm not sure if xaml files require certain properties before run time but mine are as follows:
If someone could answer this mystery it would be wonderful. I googled till my fingers bled but none of the answers posted by others have resolved my issues and this seems very rudimentary.
EDIT: Solved. Switching the build action to Page instead of resource has fixed my issue as suggested by Andrew Stephens. This had been hidden by another underlying problem, which is that I had added a boolean to visibility converter (common tool) to my resources. This alone is fine but once I had declared a resource dictionary this converter needed to be brought inside the dictionary as well.
It sounds like a XAML syntax error somewhere, but can also be caused by an unhandled exception in the main window code-behind (if you have any code in here). There are a few ways to debug this cryptic exception here (read the comments for more tips)
Also the Build Action of your .xaml resource file should be "Page" rather than "Resource".
Try building the solution with your newly merged dictionary before you start referencing the external styles in your xaml.
It may seem counter intuitive but it is possible for visual studio to know about a type in another xaml file without the designer being aware which can cause bugs like this.
Koda

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;

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.

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

Application-level Resources in a Different Assembly

This question involves the Visual Studio (2008) WPF Designer's apparent inability to handle the usage of resources located at the App.xaml level if the App.xaml is in a separate assembly from the view.
To simplify the explanation of the problem I have created a test application. This application has two assemblies: View and Start. The View assembly contains a xaml window called Window1, and the Start assembly includes the App.xaml file. The App.xaml file in the Start assembly has its StartupUri set to the Window1 in the View assembly. Neither of these files have code-behinds (aside from the standard constructors and InitializeComponent() call).
The code for this example is as follows:
App.xaml:
<Application x:Class="Start.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="pack://application:,,,/View;component/Window1.xaml"
>
<Application.Resources>
<!-- Warning Text Style -->
<Style x:Key="WarningTextStyle" TargetType="TextBlock">
<Setter Property="FontWeight" Value="Bold" />
</Style>
</Application.Resources>
Window1.xaml:
<Window x:Class="View.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1"
Height="300"
Width="300"
>
<Grid>
<TextBlock Text="This is test text" Style="{StaticResource WarningTextStyle}" />
</Grid>
</Window>
The Window1.xaml file contains a single TextBlock that references the App-level WarningTextStyle. This code works fine at runtime because the Window properly finds the App-level resource; however, at design-time the designer complains that it cannot find the WarningTextStyle.
Does anybody know of a clean and scalable solution to this problem?
My standard approach with large applications is to organize my app-level resources into resource dictionary files, and then merge those dictionaries in the App.xaml. To work around the problem that I've described above I have to merge those resource dictionaries into each view's resources. This seems very inefficient, and if I later add another resource dictionary then I need to merge that new dictionary into every view.
A silver bullet solution would re-direct the designer to find the app-level resources. A reasonable work around would be the merging of the app-level resource dictionaries into each view, but only at design-time. At runtime I would like to avoid merging these dictionaries in every view because of the efficiency issues.
I've tried merging the dictionaries on each view in the view's code-behind constructor, and then wrapping that logic in an if statement that checks the DesignerProperties.GetIsInDesignMode() method; however, the Visual Studio designer does not run the view's constructor - so this approach appears to be a bust.
Does anybody have a better solution or work around?
Can you merge the resource dictionary in your referenced assembly (be it App.xaml or your own resource dictionary) from your main (exe) assembly's App.xaml?
I just had a different idea: use a DynamicResource instead of a Static one. This might introduce a tiny performance hit, but I doubt it would be measurable.

Resources