XAML resource handling slowly driving me crazy - c#

I am working with loose XAMLs (Build Action None, but always copy to output directory). Also i have a "root" xaml that is a resource dictionary which references some merged dictionaries which are located in a subfolder. This is my folder structure:
<root>\Root.xaml
<root>\SubFolder1\MergedDict1.xaml
<root>\SubFolder1\MergedDict2.xaml
<root>\SubFolder2\MergedDict3.xaml
<root>\SubFolder2\MergedDict4.xaml
I need to be able to add additional xaml files to the folders without recompiling, that is why i need to work with loose xamls.
In the application i load the root xaml, but always get the exception that the merged dictionaries cannot be found. I have tried a lot of different variants of assigning relative path as a source. Some of those include:
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="<root>/SubFolder1/MergedDict1.xaml" />
<ResourceDictionary Source="/SubFolder1/MergedDict1.xaml" />
<ResourceDictionary Source="SubFolder1/MergedDict1.xaml" />
<ResourceDictionary Source="./SubFolder1/MergedDict1.xaml" />
<ResourceDictionary Source="../SubFolder1/MergedDict1.xaml" />
...
To my surprise (and ongoing horror) none of them have worked.
I started questioning my sanity so i have even written a small test application to generate the relative path for me, using URIs:
System.Uri uri2 = new Uri(#"C:\<root>\SubFolder1\MergedDict1.xaml");
System.Uri uri1 = new Uri(#"C:\<root>");
var r = uri1.MakeRelativeUri(uri2).ToString();
The generated relative path was, as expected, SubFolder1/MergedDict1.xaml, did also not work. Who can teach me the black magic involved in getting the correct relative path?

It looks like your xaml files are all in the same component. Still, using pack URIs may help: http://msdn.microsoft.com/en-us/library/aa970069.aspx

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.

Styles from resource dictionary not found in user control

I have a resource dictionary with a bunch of styles that I am linking too in my user controls like so:
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="../Theme/ThemedResources.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
This all works fine during runtime.
However I get a recurring problem in that one of the styles in the resource dictionary 'cannot be found' in whichever user control is the most recent, until the solution is rebuilt. This error will then appear again after I stop the debugging process, and again will disappear with a rebuild.
I don't think this is linked to a specific style, as when I add new styles the style that can't be found seems to change.
Any ideas how I can stop this from happening?
You have used a Relative URI, so it will looks for your resource file in a somewhere that you used your UserControl. It can't find you resource file because your resource file is not in the AbsolutePath.
AbsolutePath = CurrentPath (r.g Where you used your UserControl) + RelativePath
so Use an AbsolutePath:
<ResourceDictionary Source="pack://application:,,,/{YourAssemblyName};component/Theme/ThemedResources.xaml" />

Shared project with resource dictionary (xaml)

I am looking for a way to share ResourceDictionary between projects.
Adding new item to shared project doesn't offer resource dictionary. It can be created in other (main) project and dragged. But then I can't change its build options to Page:
The idea is to load resource dictionary like this
var dictionary = new ResourceDictionary();
dictionary.Source = new Uri("/WpfApplication91;component/Dictionary2.xaml", UriKind.Relative);
This is obviously fails currently with
An exception of type 'System.IO.IOException' occurred in PresentationFramework.dll but was not handled in user code
Additional information: Cannot locate resource 'dictionary2.xaml'.
Any ideas?
It's possible to manually edit shared project to set build action for resource dictionary.
Shared project consists of Project.shproj and Project.projitems files, open second and locate dictionary there:
<ItemGroup>
<None Include="$(MSBuildThisFileDirectory)Dictionary.xaml" />
</ItemGroup>
Add after that
<ItemGroup>
<Page Include="$(MSBuildThisFileDirectory)Dictionary.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
</ItemGroup>
it's a copy/paste thing from normal csproj for WPF project containing dictionary.
Seems to work, though this build action is not visible when project is loaded into Visual Studio. Adding files to shared project doesn't affect this manual change.
Now I can have shared project containing resource dictionary, yay!
Resource dictionary can be merged into application dictionaries like if it's located in the root of the project (to use as static/dynamic resource in xaml designer):
<Application.Resources>
<ResourceDictionary >
<ResourceDictionary.MergedDictionaries>
<!-- doesn't really exists in project -->
<ResourceDictionary Source="Dictionary.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
and/or loaded manually, e.g. using this pack Uri :
var dictionary = new ResourceDictionary()
{
Source = new Uri("pack://application:,,,/FlexProperty.xaml"),
};
I was having the same problem. There's a solution for including Xaml in shared projects which doesn't require editing the .projitems file directly.
You just have to add Xamarin to your Visual Studio installation. (I did it with VS Community 2015.)
You can now add xaml types via the usual Visual Studio dialog:
And the correct build action is available:
Xaml in shared projects now compiles and runs as expected.
(Presumably this is there to support Xamarin Forms, but it works for any xaml document.)
This issue is that you are putting the application name. You need the project name. Below is how to do it in both XAML and code
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/SharedProject1;Component/Dictionary2.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
Or
var dictionary = new ResourceDictionary();
dictionary.Source = new Uri("/SharedProject1;component/Dictionary2.xaml", UriKind.Relative);

What kind of "Key" do I need to supply when loading Resource Dictionaries?

In trying to set a default ResourceDictionary I receive the following warning:
The designer does not support loading dictionaries that mix
'ResourceDictionary' items without a key and other items in the same
collection. Please ensure that the 'Resources' property does not
contain 'ResourceDictionary' items without a key, or that the
'ResourceDictionary' item is the only element in the collection.
This is the code that I am using in my App.xaml file, that received the above warning:
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Lang.en-US.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
This is the exact same code that I've used to set a ResourceDictionary in Visual Studio 2008. I am now using VS 2010. What Key do I need to provide this ResourceDictionary for it to work correctly?
This is the line in my MainWindow.xaml that I am currently testing along with this code:
<MenuItem Header="{DynamicResource new_test}" />
Since you haven't posted your complete XAML file, i suspect there are other resources apart from merged dictionary in your resources section.
As per MSDN -
It is legal to define resources within a ResourceDictionary that is
specified as a merged dictionary, either as an alternative to
specifying Source, or in addition to whatever resources are included
from the specified source. However, this is not a common scenario; the
main scenario for merged dictionaries is to merge resources from
external file locations. If you want to specify resources within the
markup for a page, you should typically define these in the main
ResourceDictionary and not in the merged dictionaries.
Try moving other resources in separate resource dictionary and make sure all other resources have x:Key set on them -
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Lang.en-US.xaml" />
</ResourceDictionary.MergedDictionaries>
<ResourceDictionary>
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
<ContextMenu x:Key="MyContextMenu"/>
</ResourceDictionary>
</ResourceDictionary>
</Application.Resources>
Use resource file for translations. Its better than resource dictionary.
Here is an example:
Set prefix like this for usage in xaml.
xmlns:const="clr-namespace:FileExplorer.Properties"
Resources are located in properties.
To use them in XAML you will need following:
<TextBox Text="{x:Static const:Resources.Window_Title_String}"/>
If you have different languages then create for each language own resource file following naming convention.
For example:
Resources.resx (this will be default)
Resources.de-DE.resx (this is for german)
Now you just have to set current culture to german for your app to be on german and the proper resource file will be used automatically.
Like this in Main method:
Thread.CurrentThread.CurrentUICulture = new CultureInfo("de-DE");

Deploying a silverlight site

I have just tried to deploy my silverlight 4 site and ran across a problem.
I keep all of my styles etc. in a resource file and reference them like this:
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Resources/Styles.xaml" />
<ResourceDictionary Source="Resources/Templates.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
After deploying my website, my listbox (which has an overriden item template which i store in my templates.xaml) looked empty. However i know that i should see three items in my listbox, and i am able select three lines in the listbox they are just all empty. The problem seems to be that nothing gets drawn in the listbox items because i can not get to the resource file to get my style.
I have tried making the resource files embeded resources but no help.
Any ideas?
Ensure that the Templates.xaml file is part of your xap file.
To do that make sure the "Build Action" (in the properties of the Templates.xaml file) is set to "Resource"

Categories