Localizing strings in WPF codebehind using XAML ResourceDictionary - wpf

I'm localizing a WPF app using the LocBaml method. Everything works great for UI defined in .xaml files. However I have a few strings that are generated in codebehind that must also be localized. So I tried to take the approach recommended by Microsoft in this article. I have a xaml resource dictionary file as such:
<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">
<!-- String resource that can be localized -->
<system:String x:Uid="system:String_1" x:Key="localizedMessage">en-US Message</system:String>
</ResourceDictionary>
I then use a third-party tool to generate the localized resources.dll containing a spanish version of the resource dictionary.
However, when I call
string localizedMessage = (string)Application.Current.Resources["localizedMessage"];
localizedMessage always returns the value defined in the en-US version of the dll. I must be misunderstanding something. What do I have to do to get the localized version of the string returned?

After discussion with the OP, the problem is in setting the language too late (in App's OnStartup).
Indeed, the localized resource dictionaries are loaded once, using the current language of the thread. If the thread language changes too late, the resources are already loaded, and no reload is triggered.
A solution in this particular case would be to change the UI thread's locale as early as possible -- that is, in the App's constructor.

Related

Specifying styles in a WPF user control library

I'm having some problems sharing common styles between different projects. Here are my projects:-
"Common" - a class library containing common WPF styles.
"Plugin" - a class library containing a user control (not a custom control).
"Core app" - the core WPF application, which displays the user control defined in "Plugin".
"Common" and "Core app" reside in the same solution, while "Plugin" is in a solution of its own.
Styles in the "Core app" project work fine - it references the "Common" project and has the following in App.xaml:-
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/Common;component/MyStyles.xaml" />
</ResourceDictionary.MergedDictionaries>
When editing XAML files in the "Core app" project, I get intellisense on style names, no squiggly underlines, and everything works fine at runtime too.
The Problem is with the "Plugin" project. I've referenced the "Common" assembly, and created a \Themes\Generic.xaml file containing the same merge XAML as above. Generic.xaml has a build action of "Page", and I've added the following to AssemblyInfo.cs:-
[assembly: ThemeInfo(ResourceDictionaryLocation.None,
ResourceDictionaryLocation.SourceAssembly)]
When I edit the XAML of a user control in this "Plugin" project, styles in the "Common" assembly don't show up in intellisense, and VS/Resharper puts a squiggly line under their names. I've even added a style directly to Generic.xaml, but the UC can't see that either. The user control looks something like this:-
<UserControl ..blah..>
<StackPanel>
<TextBlock Style="{StaticResource AStyleInCommonAssembly}" Text="Hello"/>
<TextBlock Style="{StaticResource AStyleInGenericXaml}" Text="World"/>
</StackPanel>
</UserControl>
At runtime, WPF does correctly apply styles that reside in the "Common" assembly (I guess it's finding them due to the dictionary merge in App.xaml). However it's still unable to find the style that I added directly to Generic.xaml.
What am I missing? Does this approach only work with custom controls, and not user controls as I'm dealing with? My priority is to get things working at runtime, but getting the design-time/intellisense experience would be a bonus.
Solved. It seems that the technique of using generic.xaml only applies to custom controls, not user controls. Styles defined in generic.xaml (directly, or merged from another assembly) are not accessible by a user controls.
After realising this, I just went back to merging the external assembly resource dictionary within the user control itself, i.e.:
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/Common;component/MyStyles.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
Not ideal having to do this in each UC, but I can live with it in my scenario as I'll only ever have one or two in these "plugin" projects.
In doing this though, I did uncover another issue, and the following may help someone in the future. The MyStyles.xaml file in my "Common" assembly contains no styles of its own - it simply merges a number of other resource dictionaries in that assembly. This was done for convenience, meaning a consumer only needed to merge in MyStyles.xaml, rather than the dozen or so individual XAMLs in that assembly.
It turns out that there is a bug in WPF whereby "nested" merged dictionaries don't get parsed correctly. I found that if I put a style directly in MyStyles.xaml, the user control would find it. However it refuses to recognise any of the styles in the dictionaries that MyStyles.xaml merges! I've now dropped MyStyles.xaml, and have gone back to merging in the individual dictionaries (from within the user control XAML) - everything works, at design-time and runtime.
You can also define a UserControlBase from which all your UC derive and then merge all your styles in it.

How can I access a string table that is in a XAML file from code?

I have a XAMl string table that is working a treat when i need to refer to it from my controls in XAML.
However I could also do with being able to access it for use in other sections of code - such as validation messages in my model. As you cannot add a resource file to a silverlight I beed to use this.
someone must know what i need to call to get at the resource file, preferably strongly typed so intelli-sense can bail out my forgetfulness.
The Resource File definition
<ResourceDictionary
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:Key="test">test</system:String>
</ResourceDictionary>
Instead of listing the strings in XAML, why not using resource files (RESX) directly? This way Visual Studio will create wrappers around your resources using (publicresxfilecodegenerator) and you will be able to get rid of the magic strings, both in code as well as XAML.
You can use the indexer to receive this. Once you have a reference to the resource dictionary, you can use:
string test = (string)resources["test"];
For details, see Referencing Resources from Code.

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.

WPF UserControl cannot find XAML resource in referencing project

In my WPF project i keep a user control in a separate library project. The user control accesses resources in a separate XAML file, like this:
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/Resources/ViewResources.xaml" />
</ResourceDictionary.MergedDictionaries>
<!-- Local styles here -->
</ResourceDictionary>
</UserControl.Resources>
The resource file, ViewResources.xaml, resides in a folder in the control library project named Resources. It has the default build action (Page) and custom tool (MSBuild:Compile).
The problem is when I reference the control library in my WPF application and use the user control. At runtime, I get the following XamlParseException:
Set property 'System.Windows.ResourceDictionary.Source' threw an exception.
...which wraps the IOException:
Cannot locate resource 'resources/viewresources.xaml'.
How can I fix this? I have tried to change the resource file's build action to "content" and have it copied to the output directory (that works for files and similar "dumb" resources). But to no avail. Also, it doesn't work property in the user control then.
Is there a better way to specify the path?
Will I have to move the resource file to the application project (I'd rather not, as it belongs in the user control's domain).
Found it.
Turns out there is a better way to specify the path, Pack URIs. I changed the XAML to the following:
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/RoutingManager;component/Resources/ViewResources.xaml" />
</ResourceDictionary.MergedDictionaries>
<!-- Local styles here -->
</ResourceDictionary>
</UserControl.Resources>
and that fixed it.
I thought it was worth posting this just in case anyone else is struggling with the same problem, as I've spent over two hours fighting with syntax, etc. only to find that the solution was dead simple, but not that apparent:
When referencing a packed resource from another control library, it seems to work fine at design time, and even compiles without error, but fails at runtime with the 'Set property 'System.Windows.ResourceDictionary.Source' threw an exception' error. It turns out that simply referencing the resource assembly from your control library is not enough, you ALSO need to add a REFERENCE to the assembly containing the resource dictionary in you main application assembly, else it seems it does not get compiled into the application. (i.e. Startup Application (the one with app.xaml) -> Add Reference -> select assembly with referenced resource file/s).
Hope this helps!
In my case I had the ResourceDictionary and the UserControl on the same Library, but separate from the main application. What worked for me was specifying the name of the assembly in the format Adam suggested in the comment AND I had to change the ResourceDictionary in the project from Embedded Resource to Page. I didn't try using the pack:// format, but I assume it would work too.
<ResourceDictionary Source="/AssemblyName;component/Assets/MyResource.xaml"/>
I had the same error (IOException - file not found), which cost me a day of my life that I'll never get back.
Using neither the simpler "/assemblyname..." nor the "pack://...." syntax worked for me.
I was referencing the resource assembly in my main assembly correctly.
The error disappeared when I changed my xaml resource file Build Action property to "Resource", as mentioned above.
However, I then encountered a XamlParseException at this line:
<ImageBrush x:Key="WindowBackground" ImageSource="Images/gradient.png" />
(which I had hand-typed).
This left the xaml resource file I was trying to include with effectively an invalid dependency.
Oddly the fix was to delete the ImageSource property I had typed, re-insert it BUT select the image from the pulldown menus that appear as a result.
Even though the resulting line appears exactly the same, it clearly isn't.
Starting to dislike WPF (VS2013), but hope this helps.
:0/
I had the same situation, but the Pack URIs didn't help me, I was still getting "Cannot locate resource..." exception in the referencing (executable) project. What helped me, was the setting of my ResourceDictionary files in the custom control library project as Embedded Resource.

How to bind resources string to Xaml in Silverlight

How do you bind resources string to Xaml in Silverlight?
You need to add this reference to the App.xaml
xmlns:sys="clr-namespace:System;assembly=mscorlib"
Then you need to add the string into the <Application.Resources> section
<sys:String x:Key="ResourceString">Resource String</clr:String>
Then all you need to do is refer to *{StaticResource ResourceString}
for example:
<TextBlock Text="{StaticResource ResourceString}"></TextBlock>
Been a while since this was asked and answered, I just wanted to add an additional answer as the first one is not entirely correct. I think he's asking for resources, aka. text written in .resx files. It doesn't make sense at all to add individual strings into the StaticResources collection in the application.
I blogged recently on how to simplify the way you work with resources in Silverlight, enabling both automatic update when the culture changes and a dependency property which gives you simpler syntax.
http://sondreb.com/blog/post/Simplifying-Text-Resources-in-Silverlight.aspx

Resources