Merging mutiple XAML files into one - c#

I'm trying to merge multiple xaml files into one file. I'm looking for advice on how to continue. Currently, I just append xaml code to one file, but without taking care of headers and footers (opening and closing ResourceDictionary tags, and namespace mappings).
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"><SolidColorBrush x:Key="MyBrush">#FFE4F2F9</SolidColorBrush></ResourceDictionary>
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib"> <some xaml code /> </ResourceDictionary>
The only thing that comes to my mind is using regular expressions to extract ResourceDictionary bodies and place them inside one ResourceDictionary tag. However, what would be the best course of action here, and also, caring not to duplicate namespace mappings (like System namespace mapping, that is referenced inside multiple files).

It sounds like you don't know how to use merged dictionaries. I think this will help 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.

Using Localization.Comments on strings defined in a ResourceDictionary

We use the LocBaml tool to localize our application. For that, we have created a
ResourceDictionary as described in this post: https://wpf.2000things.com/2014/08/12/1134-localization-xv-localizing-other-content/
Instead of using Controls like Button or FrameworkElements like TextBlock, we directly use
System.String as follows, which works fine so far:
<ResourceDictionary x:Uid="ResourceDictionary_1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:System="clr-namespace:System;assembly=mscorlib">
<System:String x:Uid="System:String_1" x:Key="Home">Home</System:String>
</ResourceDictionary>
But if we try to use the Localization.Comments attached property (for LocBaml as input; it generates an additional
column in the output and fills in the comment, which we want to use to give some hints to our translation personnel),
the compiler complains that this is not supported.
We suspect that this only works on FrameworkElements, maybe also on DependencyObjects.
Is there any way to make this work on our raw strings (maybe by defining the AttachedProperty for string, too;
or writing an extension method)? We want to be able to use these in different controls
like MessageBox and Button, and in FrameworkElements like TextBlock. So adding these to the resources instead
is not an option for us.
P.S.: Why are x:Key and x:Uid working on these raw strings then? Maybe we can extend this via the same mechanism
to Localization.Comments?

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");

Is there any way to use StaticResource in a WPF control library and be able to view at design-time?

I have a WPF Control Library that is being added to a windows forms application. We want to allow the controls to be localizable, however I am not sure how to FULLY accomplish this without duplicating code. This is what I am doing now.
Basically, in the windows forms app, before the main application kicks off, I am instantiating an App.xaml that live within the forms app (containing my links to my resources that also live within the forms app). This works perfectly for runtime.
However, my user controls all have Content="{StaticResource SomeVariableName}", which end up being blank. I can fix this by having an app.xaml and appropriate resource dictionaries in my control library that match those in my windows forms app. However, this is duplicated code.
Things I have already tried to no avail:
Instantiate the App.xaml that lives within the user control library from within my forms app. This does not work because the URIs to my resources is looking for an embedded resource, not my local resource dictionary (I could then simply copy the resource files from the control to an appropriate location within my forms app on build). Could I leverage DeferrableContent here? There is not much online as far as I could find on this attribute and how it should be used, though.
I would like to use post builds for both App and dictionaries, however, the App instantiation is a static reference to a compiled App.xaml as far as I can tell. So, App.xaml must live within the form at least
I did try to have a duplicated App.xaml with a post build moving the resourcedictionary.xaml. I figured that a duplicated app.xaml is ok since that is the driving force and you might not want to rely on one from the control anyway (which circles back and makes you wonder if you should then have the App.xaml in the control at all? Unless you want to allow a default that uses embedded resources....) That too failed saying it could not find the resource even though it was placed where the URI should have been pointing to. The decompiled code points to Uri resourceLocater = new Uri("/WindowsFormsApplication3;component/app.xaml", UriKind.Relative);
So, Is there any way to allow for this to work AND have design time viewing of the component defaults AND avoid duplication? Or, is the duplication OK in this case? If my 2nd bullet's sub-item seems ok (duplicated App.xaml with build copied resourcedictionaries), how do I make it not look for a component level item, but instead a file level one?
Last question (and I can post this separately if necessary) that I just paid attention to. My App.xaml is being built into the code, so that does not allow me to create new ResourceDictionaries on the fly anyway. Is there any way to do this?
Final option...possibly the best one?
- I plan on using Andre van Heerwaarde's code anyway, so should I just check for the existence of a file and add it as a merged resource on the fly? Basically, have one App.xaml in my user control that links to a default embedded ResourceDictionary. And, then have the code look for the appropriate localized resources on the fly, which can be relative file paths? The only downside I see here is that the default cannot be changed on the fly...which I could probably even have that look in a specified place (using some sort of convention) and have that preferred over the built-in one?
Oh, and my reason for not wanting embedded resources is so that end users can add/modify new localized resources after the build is deployed.
I can add code if it will help you visualize this better, just let me know.
UPDATE
I am now running into a further problem with styling and not just localizing.
Here is an example of one of the internal buttons on one of the controls:
<Button Style="{StaticResource GrayButton}"
Some more things I tried/thought:
I cannot create an app.xaml (that would never be used) with the ResourceDictionary set up as ApplicationDefinitions are not allowed in library projects. I could embed this in the control's resources, but then that would always take precedence over any application level resources and I lose customizability.
Here is a connect case that actually sounds like what I am looking for, however it does not provide any real solution to this
The solution (beyond the top..which does not work) that I can think of that might work (and have yet to try) also seems like a lot of work for something that I would think should be simple. But, I might be able to create some dependency properties in the control that I can Bind to and then allow those to be overriden by the project that will be using the control. As I said, that seems like a lot of work for a pretty simple request :). Would this even work? And more importantly, is there a better, simpler solution that I am missing?
I've run into this problem once, and I resolved it by dropping the whole "Resources are objects indexed by key in canonical dictionaries" thing.
I mean, the simple fact of defining a resource in one project and referencing it in another by it's "key" should give goosebumps to any sane person. I wanted strong references.
My solution to this problem was to create a custom tool that converts my resource xaml files to static classes with a property for each resource:
So MyResources.xaml:
<ResourceDictionary>
<SolidColorBrush x:Key="LightBrush" ... />
<SolidColorBrush x:Key="DarkBrush" ... />
</ResourceDictionary>
Becomes MyResources.xaml.cs
public static class MyResources {
static MyResources() {
// load the xaml file and assign values to static properties
}
public static SolidColorBrush LightBrush { get; set; }
public static SolidColorBrush DarkBrush { get; set; }
}
For referencing a resource, you can use the x:Static instead of StaticResource:
<Border
Fill="{x:Static MyResources.LightBrush}"
BorderBrush="{x:Static MyResources.DarkBrush}"
... />
Now you got strong references, autocompletion and compile time check of resources.
I too had a problem dealing with Styling Themes and available static resources. So, I created a stand-alone library that basically had nothing but the themes to be used all nested like your MERGED resources of your prior linked question.
Then, in the Windows form (.xaml), I just put reference to that library, something like
<Window x:Class="MyAppNamespace.MyView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" ... />
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<!-- Common base theme -->
<ResourceDictionary Source="pack://application:,,,/MyLibrary;component/Themes/MyMainThemeWrapper.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
<Rest of XAML for the WPF window>
</Window>
The "component" appears to refer to the root of the given "MyLibrary" project. In the actual project, I created a subfolder called "Themes", hence the source includes... ;component/Themes/...
The "MyMainThemeWrapper.xaml" is very much like your nested Merged Resource dictionaries, and it sees everything perfectly from other libraries.
Here's my partial solution to your problem. I haven't tried to handle loose resources, but I have some success with sharing resources between WinForms and WPF.
Create a class library to contain your resources in .ResX files (e.g. Resources.resx, Resources.fr.resx, etc)
Create your WPF controls in a WPF user control library
Create your WinForms host
Reference the resources in your resource library from WPF using the Infralution.Localization.Wpf markup extension and culture manager, e.g.
<TextBlock Text="{Resx ResxName=ResourceLib.Resources, Key=Test}"/>
Put the content of your WPF user controls into one or more resource dictionaries as control templates,e,g
<ControlTemplate x:Key="TestTemplate">
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<TextBlock Text="{Resx ResxName=ResourceLib.Resources, Key=Test}"/>
</Grid>
</ControlTemplate>
Use the resource template in your user controls
<UserControl x:Class="WpfControls.UserControl1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300" >
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="ResourceDictionary.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
<ContentControl Template="{StaticResource TestTemplate}" />
</UserControl>
Add a couple of lines of code to make things work
public partial class UserControl1 : UserControl
{
// we require a reference to the resource library to ensure it's loaded into memory
private Class1 _class1 = new Class1();
public UserControl1()
{
// Use the CultureManager to switch to the current culture
CultureManager.UICulture = Thread.CurrentThread.CurrentCulture;
InitializeComponent();
}
}
Here's a simple demo app called WindowsFormsHost.7z

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>

Categories