Merged ResourceDictionary initalization in UWP app - c#

During development of my UWP app I have noticed and intersting oddity which I have hard time explaining.
I user MvvmLight and I decided to add the ViewModelLocator resource instance in a separate ResourceDictionary Core.xaml which will be referenced from MergedDictionaries in App.xaml.
Following is the content of App.xaml:
<Application ...>
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Resources/Core.xaml" />
<ResourceDictionary Source="Resources/Converters.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>
Contents of Core.xaml:
<ResourceDictionary ...>
<viewModel:ViewModelLocator x:Key="Locator" />
</ResourceDictionary>
Now I supposed the resources in Core.xaml are initialized during the InitializeComponent method call in App.xaml.cs, but when I tried to use the ServiceLocator class (which is set in the constructor of ViewModelLocator in MvvmLight) - like this - ServiceLocator.Current.GetInstance<INavigationService>().Navigate<MainViewModel>(); - I get an exception saying:
An exception of type 'System.InvalidOperationException' occurred in
Microsoft.Practices.ServiceLocation.dll but was not handled in user code
Additional information: ServiceLocationProvider must be set.
Indeed, if I put an breakpoint in the constructor of ViewModelLocator, it is not called before the Window is activated. More interestingly still - if I manually reference the Locator resource key (for example putting Debug.WriteLine(Resources["Locator"]); above the call of ServiceLocator), everything works fine. The same goes if I move the ViewModelLocator resource directly to App.xaml - then it is instantiated during IntializeComponent.
Is there a lazy instantiation of merged resource dictionaries in UWP apps? Or why does it behave this way?

A ResourceDictionary in UWP doesn't have any code behind (no InitializeComponent). Therefore, any class references defined in a ResourceDictionary won't be initialized directly.
Neither does the App.InitializeComponent do this for you. Resource dictionaries in UWP just don't provide this functionallity - don't ask me why.
You can easily try this by trying to initialize a DataTemplate in a ResourceDictionary.
This should - sadly - neither work.
However, using the Resources["Locator"] access in code behind triggers the constructor of the class and you're fine.
This ain't be a solution, but a explanation of your problem.
I hope it helps you.

Related

Can't share ResourceDictionary between different projects

I have several Windows application projects that all have the same copy-pasted ResourceDictionary in their app.xaml file. I want to remove this code duplication, put a ResourceDictionary in one file in a project that's referred by all of them and use the ResourceDictionary.Source parameter to reference to it.
Currently every project has something like this in their app.xaml file:
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/SomeProject;component/SomePath/First.xaml"/>
<ResourceDictionary Source="/SomeProject;component/SomePath/Second.xaml"/>
<ResourceDictionary Source="/SomeProject;component/SomePath/Third.xaml"/>
...
</ResourceDictionary.MergedDictionaries>
So I put it all in one file called Resources.xaml in a project called Common (for the example's sake), and in the app.xaml I changed the code to:
<Application.Resources>
<ResourceDictionary Source="pack://application:,,,/Common;component/Resources.xaml"/>
</Application.Resources>
When I click F12 on the filename, it directs me to the intended Resources.xaml file, but when I launch the application I get an exception:
System.Windows.Markup.XamlParseException:
''{DependencyProperty.UnsetValue}' is not a valid value for property
'Background'.'
Inner Exception: InvalidOperationException:
'{DependencyProperty.UnsetValue}' is not a valid value for property
'Background'.
I changed Resources.xaml build option to "Resource" from "Page", but it didn't change anything.
I also looked at this question, and it seems as if I'll have to change all my StaticResource references to DynamicResources, which is not a real viable solution for me.
How Can I prevent the exception? Is there any other way to prevent this code duplication?
You have to use MergedDictionaries and use the pack URI scheme to fully qualify the merged resource.
"I have several Windows application projects that all have the same copy-pasted ResourceDictionary in their app.xaml file."
Usually you create a single WPF APP project and set it as the startup project. Every additional projects are of type library. This means they don't contain an application or framework entry point, which is a class that derives from Application, usually the partial class App defined in App.xaml and App.xaml.cs. Visual Studio offers a project template for control libraries like WPF CustomControl Library or WPF User Control Library.
A WPF application contains only one active App.xaml file. If you need to reference resources in an assembly other than the startup assembly, you import them by defining a MergedDictionaries in the relevant resource files.
App.xaml
<Application.Resources>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/SomeProject;component/SomePath/First.xaml" />
<ResourceDictionary Source="pack://application:,,,/SomeProject;component/SomePath/Second.xaml" />
<ResourceDictionary Source="pack://application:,,,/SomeProject;component/SomePath/Third.xaml" />
...
</ResourceDictionary.MergedDictionaries>
</Application.Resources>
It is recommended to move all relevant and shared resources to the App.xaml dictionary if possible. This eliminates the need to define MergedDictionaries outside of App.xaml, which can improve performance.
Also make sure the order of the merged ResourceDictionary items inside the MergedDictionaries collection are added in the right order.
Problem
Note that the XAML parser follows certain lookup rules. Also StaticResource lookup doesn't support forward declaration: all referenced resources must be defined before the declaration of the actual reference.
Especially when dealing with MergedDictionaries the order of declaration is very important.
In short the static resource lookup starts locally with the ResourceDictionary of the current element. If the resource key was not found in its scope, the XAML parser traverses up the logical tree to check the dictionaries of the logical parents, until it reaches the root element e.g. Window. After the root element the parser checks the application's resource dictionary and then the theme dictionary.
If a the parser encounters a MergedDictionaries (after checking the current ResourceDictionary first), it iterates the merged ResourceDictionary collection in reverse order from bottom to top or from last to first.
Since there is no forward declaration supported by the XAML parser, the order of the merged resources is very important.
Take the following MergedDictionaries collection:
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/SomePath/First.xaml" />
<ResourceDictionary Source="/SomePath/Second.xaml" />
<ResourceDictionary Source="/SomePath/Third.xaml" />
</ResourceDictionary.MergedDictionaries>
Now consider the following situation: you have an element e.g. a Button that statically references a ControlTemplate, which is defined in a parent element's dictionary inside the merged dictionary of Third.xaml. But this template also contains an element, that statically references a Style defined in First.xaml.
If elements or resources declared in Third.xaml would need to statically reference resource from First.xaml, then the parser couldn't not resolve those resources: parser searches for the ControlTemplate and reaches the parent's ResourceDictionary. This dictionary doesn't contain the reference, but a MergedDictioanaries collection. So it starts to iterate over this collection in reverse order, from last to first or from bottom to top: it starts with Third.xaml and successfully finds the referenced ControlTemplate.
In order to instantiate this template, the parser must resolve all template resources. Inside this template the parser finds an element that needs a Style, but this Style was not found in any previous merged ResourceDictionary. It is defined in the ResourceDictionary of First.xaml, which has not been visited yet (forward declaration). Therefore this resource cannot be resolved.
Solution
To fix this, you can either put the merged dictionaries into the right order:
<!-- Collection is iterated in reverse order -->
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/SomePath/Third.xaml" />
<ResourceDictionary Source="/SomePath/Second.xaml" />
<ResourceDictionary Source="/SomePath/First.xaml" />
</ResourceDictionary.MergedDictionaries>
Or replace static references with dynamic references by using the DynamicResource markup.
The DynamicResource markup instructs the XAML parser to create a temporary expression during the first lookup pass (this first lookup pass is the one described before and resolves static references at compile time). After this first pass, a second lookup occurs at runtime. The parser again traverses the tree to execute the temporary expressions previously created by the DynamicResource markup during the first lookup pass.
So whenever you can't provide a definition of a resource before its declaration, you have to use DynamicResource lookup.

WPF Global Class MVVM

In trying to setup a global class in App.xaml;
<Application.Resources>
<local:MySettingsClass x:Key="GlobalSettings" />
<ResourceDictionary >
<!-- My Resources -->
</ResourceDictionary >
</Application.Resources>
Although I am getting an error message of
Each dictionary entry must have an associated key.
and
'App' does not contain a definition for 'InitializeComponent' and no extension method 'InitializeComponent' accepting a first argument of type 'App' could be found (are you missing a using directive or an assembly reference?)
Now I can go in and and an x:Key to ResourceDictionary but I have never seen anyone do that, so that seems wrong. The second error message leads me to believe that I am doing this wrong.
Do I have the wrong solution for this problem or is something simple I am missing?
Application.Resources IS a resource dictionary, implicitly. You could just slap resources right in there. This would be valid:
<Application.Resources>
<local:MySettingsClass x:Key="GlobalSettings" />
<!-- Pretty much anything with an x:Key attribute. -->
</Application.Resources>
But if you're merging dictionaries you do need the explicit <ResourceDictionary> tag, and usually in App.xaml you're merging dictionaries.
The problem you ran into is that in your XAML, the first thing it sees is your GlobalSettings resource, before anything. That's the first thing it sees, so it figures OK, he's just slapping some resources in here. It creates a ResourceDictionary and proceeds to add everything it sees to that. Next thing it sees is <ResourceDictionary>, and it thinks you're trying to add another resource dictionary to the first one as a resource, but without an x:Key attribute. I tried just now and it did let me add one with x:Key, not that I know of any reason to do that. It didn't merge the resources into the outer one.
Try this:
<Application.Resources>
<ResourceDictionary>
<!-- One of My Resources -->
<local:MySettingsClass x:Key="GlobalSettings" />
<!-- My Other Resources -->
<ResourceDictionary.MergedDictionaries>
<!-- Merged dictionaries -->
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
If you keep getting the InitializeComponent error, please share your code for App.xaml.cs

ResourceDictionary in wpf as dll application

Let's start from the beggining. I have an app in wpf which uses my custom window style. I'm defining this custom style in app.xaml like below:
<Application x:Class="GeoLocations.Test.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="ThemedWindowStyle.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
Nowadays there came a requirement that i need to build this application as dll and later on call it from Windows.Forms application. Obviously the app.xaml code is not being fired since this is not start up application anymore. Is there any way to load it ?
I tried to manually register this Dictionary in code behind but with no success. I also tried to change Build Action from "Page" to "Content" and "Do not copy" to "Copy if newer" but it is giving me different exception:
'Failed to create a 'Type' from the text 'local:ThemedWindow" with inner exception "{"Type reference cannot find type named '{clr-namespace:GeoLocations.Test}ThemedWindow'."}
(this exception is beeing fired inside ResourceDictionary so it's loaded but why it can't find the type ?).
ThemedWindow is a type which inherits from Window and later on all my windows inherits from ThemedWindow instead of Window
I have no idea how to solve this issue. Anyone got some knowledge to help ?
Ok, so i resolved this be adding my ResourceDictionary in code behind in my ThemedWindow Constructor. Like below:
var rd = new ResourceDictionary();
rd.Source = new Uri("pack://application:,,,/GeoLocations Screens;component/ThemedWindowStyle.xaml");
Resources.MergedDictionaries.Add(rd);

WPF Prism - Where to put Resources?

I have a prism application and various modules. I am wondering where is the best place to locate resources such as styles, brush, controltemplates, datatemplates?
Should I make one single resource dictionary and put everything there? Should each module have their own resources? Or each view? I would like to follow the Prism goal of keeping everything modular, but also I dont see the point in re-declaring the same resources in every module...
I develop application with Prism, and I use technique very close to described in Prism's manual. There is YourApplication.Infrastructure project, where you usually place all your shared interfaces etc. So:
I just add project YourApplication.Resources
Create there folder Themes
Create separate xaml file in Themes folder for each group of resources (like Generic.WPF.xaml for standard WPF controls' styles, Generic.Brushes.xaml for brushes etc.)
Create file Themes\Generic.xaml (exactly with this name, it will add huge benefits in the future) with content like
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Generic.Brushes.xaml"/>
<ResourceDictionary Source="Generic.WPF.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
Now you can add those resources in any module (you have separate project for it, right?) by adding reference to YourApplication.Resources to that project and adding to your view's xaml:
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/YourApplication.Resources;component/Themes/Generic.xaml"/>
</ResourceDictionary.MergedDictionaries>
<!-- Put your not shared resource here -->
</ResourceDictionary>
</UserControl.Resources>
I don't know, maybe this way has some problems, but it works, and works well for me. If anybody can comment somehow this way (pros/cons) - I will be very happy to hear it!
Application-wide resources I usually put in a ResourceDictionary, which is added to either App.xaml or StartupWindow.xaml
Resources for a specific View are usually located with the View. For example, a UserControl that is being used for a CalendarView will contain any custom resources for the Calendar, such as calendar-specific brushes, styles, templates, etc.
I usually don't see a reason to make module-wide resources, but if I ever do I'd have a ResourceDictionary for the Module which can be loaded into the app's merged dictionaries at runtime, or included in individual Views in the Module.
I would like to share some new knowledges. I am using #chopikadze approach. And it is really cool approach. Thanks to you!
However, if you do not want write every time for each control these piece of code:
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/YourApplication.Resources;component/Themes/Generic.xaml"/>
</ResourceDictionary.MergedDictionaries>
<!-- Put your not shared resource here -->
</ResourceDictionary>
</UserControl.Resources>
Then you can just declare <ResourceDictionary/> in App.xaml of your Bootstrapper like that:
<Application.Resources>
<ResourceDictionary Source="pack://application:,,,/YourApplication.Resources;component/Themes/Generic.xaml"/>
</Application.Resources>

Accessing ResourceDictionary from WPF UserControl

I'm trying to access a resource dictionary in a UserControl code-behind via C# and I'm having little success.
Merged Dictionary:
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/Resources/BiometricDictionary.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
Embedded Dictionary:
<UserControl.Resources>
<BitmapImage x:Key="imageDefault">/Resources/Images/default_thumb.png</BitmapImage>
<BitmapImage x:Key="imageDisconnected">/Resources/Images/disconnect_thumb.png</BitmapImage>
<BitmapImage x:Key="imageFailed">/Resources/Images/failed_thumb.png</BitmapImage>
<BitmapImage x:Key="imageSuccess">/Resources/Images/success_thumb.png</BitmapImage>
</UserControl.Resources>
Code behind:
var resourceDictionary = new ResourceDictionary();
resourceDictionary.Source = new Uri("/Resources/BiometricDictionary.xaml", UriKind.Relative);
I've tried all of the examples and helpful tips but coming up short. Right now, success would be the ability to load the dictionary. Any suggestions?
To access one of your UserControl's XAML resources in your codebehind, all you need to do is access the Resources property of the UserControl. Something like this:
BitmapImage myImage = (BitmapImage)this.Resources["imageDefault"];
Though, the preferred method is to use FindResource(), which will search the entire logical tree for a match to the key, rather than just the object it is called on.
BitmapImage myImage = (BitmapImage)this.FindResource("imageDefault");
Try to remove the forward slash infront of your location. The only time you should use /Resources is if you have to go up a library first. like ../Resources
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Resources/BiometricDictionary.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
Hope this helps you.
So, you have a ResourceDictionary defined in a UserControl's assembly, and would like to access it from that UserControl's code-behind?
You should be able to. However, if the code you listed is in the constructor, you may not have access to the resource dictionary (might not be loaded yet). Try adding that same code to your UserControl's "loaded" event, and see if that works. If you're simply trying to access a resource, such as a style or template, using the "FindResource" or "TryFindResource" functions directly from your class should work as well (i.e. you don't need to have an object of type "ResourceDictionary").
Hope that helps!
d'Oh...after compiling to the local bin so that references are relative, I implemented the pack URI solution found here: ResourceDictionary in a separate assembly and then FindResource(x:key value here).
#PeterAllenWeb, #Pwninstein, thanks for your quick responses and getting me thinking again.

Categories