Reading values out of a language.xaml file - c#

I'm creating a C# WPF MVVM application and currently busy with adding different languages.
Right now I use a xaml file which contains a lot of rows like:
<system:String x:Key="btnNew">New</system:String>
<system:String x:Key="btnEdit">Edit</system:String>
The program reads a specific folder, gets the different language files (English.xaml, Dutch.xaml) and adds the selected file to the Resources.MergedDictionaties:
void updateLanguage(int id)
{
string sSelectedLanguage = obcLanguages[id].sRealName; // Get selected language name
ResourceDictionary dict = new ResourceDictionary(); // Create new resource dictionary
string sSelectedLanguageUrl = "pack://siteoforigin:,,,/Languages/" + sSelectedLanguage; // Create url to file
dict.Source = new Uri(sSelectedLanguageUrl, UriKind.RelativeOrAbsolute);
App.Current.MainWindow.Resources.MergedDictionaries.Add(dict); // Add new Language file to resources. This file will be automaticly selected
}
My xaml content with buttons refers with the DynamicResource to the 'bntNew':
<Button x:Name="btnNewSeam" Content="{DynamicResource btnNew}"Style="{StaticResource BtnStyle}" Command="{Binding newSeamTemplateCommand}" />
This works fantastic.
But right now i'm using Observablecollections for comboboxes and I'm adding options in code like this:
obcFunctionsPedalOptions.Add(new clCbbFilltype1("Block", 0));
obcFunctionsPedalOptions.Add(new clCbbFilltype1("Free", 1));
obcFunctionsPedalOptions.Add(new clCbbFilltype1(">0", 2));
But the options "Block", "Free" and ">0" have to be translated with information from the Language.xaml file.
How can I read this specific Xml file with code, get for an example the PedalOptions x:Key and read its options?
Example:
<system:String x:Key="PedalOptions">Block, Free, >0</system:String>

Rather than define the strings in XAML use a resource (.resx) files. You have a default (ResourceFile.resx) file and then language specific ones (ResourceFile.nl.resx for Dutch, ResourceFile.en-US.resx for US English, etc). The system picks the correct resource file based on the current culture settings.
The MSDN has a section on this which you should read.
You can then just have the following in your code:
obcFunctionsPedalOptions.Add(new clCbbFilltype1(Resources.ResourceFile.Block, 0));
You'll need have make this visible to the XAML as follows by defining a class:
public class Localize : INotifyPropertyChanged
{
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyChange(String name)
{
if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(name));
}
#endregion
//Declarations
private static ResourceFile _localizedStrings = new ResourceFile();
public ResourceFile LocalizedStrings
{
get { return _localizedStrings; }
set { NotifyChange("LocalizedStrings"); }
}
}
Then reference this from the XAML:
<local:Localize x:Key="LocalizedStrings"
xmlns:local="clr-namespace:Module" />
You can then use it as follows:
<TextBlock Text="{Binding LocalizedStrings.Block, Source={StaticResource LocalizedStrings}}" />

Thanks for your answer, i did make it work like you told me and the MSDN page:)
So, right now i have one default "LanguageResource.resx" file added to my resources and did test it with a second "LanguageResource.nl.resx" file, and changed the language with CultureInfo("nl").
But, my customer wants a folder where he can put different "LanguageResource.XX.resx" files, the application adds this files from the folder, and he is able to change the language.
My customers doesn't want to compile this program everytime he adds a new language.
So how do i runtime add a few .resx files to the resource manager?

Related

How can I chain Resource Dictionaries that are externally loaded from disk, not included in project or assembly?

I've got a ResourceDictionary in a xaml file that represents a skin in a folder on my hard drive in some random folder. Say D:\Temp2\BlackSkin.xaml.
I've set the permissions correctly to give full access so that's not the issue.
This ResourceDictionary BlackSkin.xaml references a BaseSkin.xaml like so:
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="{D:\Temp2\BaseSkin.xaml">
</ResourceDictionary.MergedDictionaries>
So I'm loading this BlackSkin.xaml using XamlReader.Load(...)
It works fine if I put:
Source="D:\Temp2\BaseSkin.xaml"/>
Which is of course a hard-coded path. I would like to provide a relative path, not relative to the current application that is running, but a relative path to the current skin being loaded (BlackSkin.xaml)...aka "D:\Temp2\BaseSkin.xaml"...or...if you will just "BaseSkin.xaml" since they are located in the same folder, which again is not the same folder as the application.
Do I need to define a Custom Markup Extension to do this? And if so, where would I put this in my project? Just make a folder called "Extensions" and create some random .cs file and implement it there? Would such a Custom Markup Extension get called when calling XamlReader.Load(...)?
Then I would need to tie this Custom Markup Extension into the program to get a fully qualified path from some setting or config file in my application and return the path as part of the ProvideValue function?
Are there any other ways of doing this?
Such as where I call XamlReader.Load(fileStream) like so:
public void ApplySkin(string fileName, string baseSkinFileName)
{
if (File.Exists(baseSkinFileName))
{
ResourceDictionary baseSkinDictionary = null;
using (FileStream baseSkinStream = new FileStream(baseSkinFileName, FileMode.Open))
{
//Read in the BaseSkin ResourceDictionary File so we can merge them manually!
baseSkinDictionary = (ResourceDictionary)XamlReader.Load(baseSkinStream);
baseSkinStream.Close();
}
using (FileStream fileStream = new FileStream(fileName, FileMode.Open))
{
// Read in ResourceDictionary File
ResourceDictionary skinDictionary = (ResourceDictionary)XamlReader.Load(fileStream);
skinDictionary.MergedDictionaries.Add(baseSkinDictionary);
// Clear any previous dictionaries loaded
Resources.MergedDictionaries.Clear();
// Add in newly loaded Resource Dictionary
Resources.MergedDictionaries.Add(skinDictionary);
fileStream.Close();
}
And perhaps just remove the BaseSkin.xaml lookup in the BlackSkin.xaml...that is to say remove this block of code from the BaseSkin.xaml?
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="{StaticResource baseSkin}"/> <!--D:\Temp2\BaseSkin.xaml" /-->
</ResourceDictionary.MergedDictionaries>
But then the question becomes what if I have other resources that I want to load in the BlackSkin.xaml, like images or anything else? So I guess the above really only works for a generic single-use situation like this, but what I'm really after is a generic solution that can work for chaining all kinds of different resources into a ResourceDictionary that is an external file located at a random folder on the disk, and resources that may or may-not be co-located with the originating xaml resource Dictionary file.
Which brings me back to the Custom Markup Extensions question.
EDIT:
Well I did try to create a custom markup extension, but it throws a XamlParseException, I think because it doesn't know what the clr-namespace is, again because it's not in any assembly or anything of that nature that can be related back to the running program.
So here is the Xaml that I have now, maybe I got it completely wrong, or maybe I'm going completely down the wrong path...?
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:base="clr-namespace:TimersXP.Extensions">
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="{base:BaseSkinPath}"/> <!--D:\Temp2\BaseSkin.xaml" /-->
</ResourceDictionary.MergedDictionaries>
And this is what I have for the custom extension, just trying to see if I can make it work with a hard-coded path for now in the custom extension. Did I misunderstand something?
I'm going off from this example:
http://tech.pro/tutorial/883/wpf-tutorial-fun-with-markup-extensions
using System;
using System.Windows.Markup;
namespace TimersXP.Extensions
{
class BaseSkin : MarkupExtension
{
/// <summary>Gets the base skin file path.</summary>
/// <value>The base skin.</value>
public string BaseSkinPath
{
get { return "D:\\Temp2\\BaseSkin.xaml"; }
}
public override object ProvideValue(IServiceProvider serviceProvider)
{
return "D:\\Temp2\\BaseSkin.xaml";
}
}
}
I think you can define your own class, inherited from ResourceDictionary, and use it in MergedDictionaries section.
Define:
public class SkinResourceDictionary : ResourceDictionary {
public static string BaseDirectory { get; set; }
public new Uri Source {
get {
return base.Source;
}
set {
base.Source = BaseDirectory + value;
}
}
}
Use:
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:App1">
<ResourceDictionary.MergedDictionaries>
<local:SkinResourceDictionary Source="\BaseSkin.xaml" />
</ResourceDictionary.MergedDictionaries>
...
But you're still need to decide how to set BaseDirectory property;

How to use .resx and .resource files in custom server control asp?

I am writing my own server side control and I am using images that are being stored in a .resx file. In the console application this code works fine:
ResXResourceReader rsxr = new ResXResourceReader("Resource1.resx");
foreach (DictionaryEntry d in rsxr)
{
Console.WriteLine(d.Key.ToString() + ":\t" + d.Value.ToString());
}
rsxr.Close();
but here
protected override void RenderContents(HtmlTextWriter output)
{
ResXResourceReader rsxr = new ResXResourceReader("Resource1.resx");
base.RenderContents(output);
foreach (DictionaryEntry d in rsxr)
{
output.Write(d.Key.ToString());
}
}
I get this error:
Could not find file 'C:\Program Files\Common Files\Microsoft Shared\DevServer\10.0\Resource1.resx'
I tried to use the ResourceManager, but it requires a .resource file. I can't access the resgen tool (command prompt does not understand the resgen command) and install it (during the attempt some errors ocured).
My questions are:
Why can't I read .resx?
How to install the resgen tool properly?
thanks.
It is considered good practice to store your resource file under App_GlobalResources folder in application root or in App_LocalResources with the same name as your user control file. So for example user user control is uc.ascx file in local resource folder should be uc.ascx.resx. That way it is easier to maintain and asp.net will automatically detect it.
Now your answers:
First Question:
Use Server.MapPath("~/") points to physical directly where your web.config is. If you want to use a resource file in Controls folder you have to write Server.MapPath("~/Controls/Resource1.resx") to get the path.
Not sure what you want to do with resgen tool? When you compile your application, resource file will also be compiled. select your resource file and click F4, it will show you build action, choose resource in build action and your resource file will be included in assembly.
You can review this post for more information: How to use image resource in asp.net website?
From your description, I understand you need to locate and access the user control's resource file. I found that it works nicely the following way:
Create a App_GlobalResources on project level (via context menu Add -> Add ASP.NET Folder -> App_GlobalResources)
Create the ressource file with the same name as the control, but inside the App_GlobalResources. For example, if the control is named myControl.ascx, then the ressource file's name for the default language has to be myControl.ascx.resx
Create additional ressource files for each language you require. For instance, if you need German ("de-DE"), then add myControl.ascx.de.resx
Add the class MultiLanguageUserControl as follows:
public class MultiLanguageUserControl : System.Web.UI.UserControl
{
public string getResValue(string id)
{
var ctrlPath = TemplateControl.AppRelativeVirtualPath;
var ctrlFile = System.IO.Path.GetFileName(ctrlPath);
var resObj = GetGlobalResourceObject(ctrlFile, id);
if (resObj!=null)
return resObj.ToString();
else
return string.Format("UNRESOLVED[{0}]", id);
}
}
Open the code behind of myControl and make it inherit from MultiLanguageUserControl instead from System.Web.UI.UserControl:
public partial class myControl : MultiLanguageUserControl { //... }
In the HTML code, use the new function, e.g.: <%=getResValue("resid")%>, where "resid" is the name of the ressource string you want to look up. You can also use the HTML-encoding tag <%: instead of <%=, depending on your requirements. Alternatively, you can use getResValue anywhere in your server-sided C# code in your user control to retrieve the value from the ressource file.
Ensure that you support the language detection in the Page_Load event of the page, which uses the user control. How you can do this is described here (look for the function InitializeCulture).
NOTE: If you want to read the page's local resource strings from inside the user control then take a look here.

project with localisation folder and classes?

I'm trying to use a new resources file with localisation in a Silverlight Project from 2009.
I've added a new resource file to the folder Resources in my Silverlight Application and if
I try to use the new resources in a control (.xaml) I get the blue underlining with the message "could not resolve resTest.resx).
I found a folder "Localisation" with classes like this:
namespace SilverlightApplication.Localization{
public class ContentGrid
{
public ContentGrid() {
}
private static Resources.ContentGrid _Resource = new SilverlightApplication.Resources.ContentGrid();
public Resources.ContentGrid Resource
{
get
{
return _Resource;
}
}
}
}
I've added a new class for my new resources file, but it doesn't work. Still the same error. I only know the automatic resources method, where I put a resource file in the folder Resources and everything works automatic.
Maybe I'm doing something wrong?
I read the flowing article which explains how to make xaml content localizable.
http://msdn.microsoft.com/en-us/library/dd882554%28v=vs.95%29.aspx
I forgot to make an entry in the app.xaml file.

Silverlight resource constructor always return to internal

When I modify my resource file (.resx) add text or modify, the constructor of my resource always go to internal and after that, when I run my silverlight I have an error in my binding XAML.
Is there a way to avoid this scenario? I need to go in the designer of my resource and put the constructor to public to solve the problem
I use my resource like this in my xaml file
<UserControl.Resources>
<resources:LibraryItemDetailsView x:Key="LibraryItemDetailsViewResources"></resources:LibraryItemDetailsView>
</UserControl.Resources>
<TextBlock FontSize="12" FontWeight="Bold" Text="{Binding Path=FileSelectedText3, Source={StaticResource LibraryItemDetailsViewResources}}"></TextBlock>
Another way to do this without code changes is as below. Worked well for me.
http://guysmithferrier.com/post/2010/09/PublicResourceCodeGenerator-now-works-with-Visual-Studio-2010.aspx
You can create a public class that exposes the resources through a property:
public class StringsWrapper
{
private static LibraryItemDetailsView _view = null;
public LibraryItemDetailsView View
{
get
{
if (_view == null)
{
_view = new LibraryItemDetailsView();
}
return _view;
}
}
}
Then in your XAML you can access your resource:
<UserControl.Resources>
<StringsWrapper x:Key="LibraryItemDetailsViewResources"></StringsWrapper>
</UserControl.Resources>
<TextBlock FontSize="12" FontWeight="Bold" Text="{Binding Path=View.FileSelectedText3, Source={StaticResource LibraryItemDetailsViewResources}}"></TextBlock>
This way the resx constructor can be internal!
Well, what I did was to add a command line utility to pre-build event of each Silverlight project that replaces each internal string with public :)
You can edit pre-build and post-build events by: Right-clicking on a project -> Properties -> Build Events.
I used a utility called RXFIND, it's free and can replace a string within selected files using a RegEx regular expression.
Here's the command line I'm using:
"$(SolutionDir)ThirdParty\rxfind\rxfind.exe" "$(ProjectDir)Resources\*.Designer.cs" "/P:internal " "/R:public " /Q /B:2
Please note, that all my resources are located under Resource directory within each project and the command line utility resides in \ThirdParty\rxfind directory
I also have the same error. To solve the problem I just created a public class that inherits from the class representing the resources file, knowing that it must also be public class this is my exep:
public class TrackResourceWrapper : TrackResource
{
}
with:
TrackResourceWrapper is inheriting class
TrackResource is the class which is located in the code resource file behind (public class)
Simply:
Add a new class that inherits from the resource class
In the App.xaml file, modify the resource class which you created
Done!
The reason for this is that you shouldn't be instantiating the class yourself. You should instead always use ConsoleApplication1.Resource1.ResourceManager which itself instantiates the class for you.
Here, ConsoleApplication1 is the name of your assembly and Resource1 the name of your resource file.
I created a macro to do this for me for each file I edit. I still have to remember to run it, but it's a lot quicker. Please see my post.

Access system:string resource dictionary XAML in C#

How do I access the contents in a resource dictionary using C#?
For example, here is my code in XAML:
<system:String x:Key="NewGroup">New Group Name</system:String>
And I want to access it here in C#:
private void OnAddGroup(object sender, ExecutedRoutedEventArgs e)
{
BooksGroupInfo group = new BooksGroupInfo();
group.GroupName = "New Group" + TheTabControl.Items.Count;
TabItem tab = AddGroup(group);
_currentLibrary.addGroup(group);
_currentLibrary.CurrentGroup = group;
}
Instead of typing "New Group" in C#, I would like to replace that and have access in the resource dictionary in XAML. So the command will automatically get the Name that is in the resource dictionary.
I've tried a couple of solutions like:
(System.String)this.FindResource("NewGroup");
Application.Current.Resources[typeof(System.String)];
and so on...
but they do not seem to work.
I am doing a Localization using locbaml and it doesn't parse the Text/Name on C# (or I don't know how to) and that was the only solution I thought was possible.
Usually using FrameworkElement.FindResource like this: string s = this.FindResource("NewGroup") as string; works. It is more likely that the resource with the key "NewGroup" does not exist in the scope of your control or window (whatever this is). You must make sure that the resource is there. E.g. if your resource comes from another file you have to use MergedDictionaries. You can test if the resource is actually acessible try to acess it from XAML that belongs to your codebehind where OnAddGroup is definde.
I hope that makes sense.

Categories