76

Is it possible to add some value from resource file right into the XAML markup? Or for localization we always have to make something like this in *.cs file:

txtMessage.Text = Messages.WarningUserMessage;

Where Messages is resource, and txtMessage is TextBlock.

0x49D1
  • 8,505
  • 11
  • 76
  • 127
  • 2
    How did you create Messages resource file? Did you just added Messages.resx to you properties folder? If so, I cannot reach this file. – Sergey Aug 06 '11 at 07:18
  • @Sergey, no matter where it is. Sure you can add it to properties folder too. Sure resx file must be in project directory. May be you cant add it to properties directory from VS? Btw thats bad practice to add smth to properties directory. Better create "resource" directory to hold you res files. – 0x49D1 Aug 06 '11 at 14:03
  • Closely related: https://stackoverflow.com/questions/12363834/how-to-use-resources-resx-to-link-images – StayOnTarget Feb 07 '20 at 14:45

7 Answers7

90

Make sure that Code Generation is set to Public in the resx editor, then you can simply use:

<TextBlock Text="{x:Static Messages.WarningUserMessage}" />
Julien Lebosquain
  • 40,639
  • 8
  • 105
  • 117
  • 1
    Im new to WPF and this answer is just what i needed! Thank you. – 0x49D1 May 27 '10 at 08:04
  • 2
    Note that it works only if `Messages` is in the same namespace as the screen. – Julien N Dec 23 '11 at 15:24
  • 4
    ... and if `Messages` is not in a different assembly since the constructor is generated as internal. – Davi Fiamenghi Oct 18 '13 at 15:38
  • @DaviFiamenghi: the constructor doesn't matter, we're only accessing static members here. – Julien Lebosquain Oct 18 '13 at 15:56
  • Sorry, you are right... I had this problem because of small typo in my solution and tough that was because of the assembly – Davi Fiamenghi Oct 18 '13 at 16:31
  • 3
    You still need to add the namespace in the designer like in the @Yogs answer xmlns:resource="clr-namespace:YourProject.Properties" and use it like {x:Static resource:Resources.ResourceName} – Ilya Kochetov Apr 22 '19 at 08:55
  • Check this page: https://developercommunity.visualstudio.com/t/xaml-editor-cannot-resolve-static-resources-resx-f/989330?viewtype=all: If your project is 64-bit, you may find you have to temporarily set the Solution Platform to "x86". – Morris Jun 21 '21 at 10:37
74

It's a lot easier to do it like this. Add a xmlns in XAML file and use the resources directly.

xmlns:resx="clr-namespace:wpfapplicationname.Properties"
Title="{x:Static resx:Resources.name}"
joran
  • 169,992
  • 32
  • 429
  • 468
Daniex
  • 741
  • 5
  • 2
  • 3
    The only drawback to this approach being of course that you cannot change culture dynamically. There is a great article about how to accomplish that behaviour through use of a markup extension and localization manager somewhere. – Stephen Drew Nov 23 '12 at 12:27
  • 5
    here is it: http://www.grumpydev.com/2009/09/08/localising-wpf-applications-using-resx-files-and-standard-data-binding-without-a-markupextension/ – Davi Fiamenghi Oct 18 '13 at 16:31
  • @DaviFiamenghi Thanks, but gone, here is the archive version: https://web.archive.org/web/20210629145415/http://www.grumpydev.com/2009/09/08/localising-wpf-applications-using-resx-files-and-standard-data-binding-without-a-markupextension/ – Martin Braun Oct 31 '21 at 16:24
14

I understand my reply is a bit late, but I thought its worth sharing:

To use a string stored in the *.resx file without Static keyword:

  1. In App.Xaml file add a namespace for Properties xmlns:resource="clr-namespace:YourProject.Properties"
  2. In ApplicationResources(app.xaml file) Add a Resource for your *.resx file

    <Application.Resources> <resource:ResourceFileName x:Key="ApplicationStringResources" /> </Application.Resources>

  3. In your XAML file use the following Binding, let us take an example of Window Title

    Title="{Binding TitleString, Source={StaticResource ResourceKey=ApplicationStringResources}}"

    TitleString is the name of StringProperty in your *.resx file

  4. Last but not least, don't forget to change the resource file access modifier to Public.

Magesh
  • 308
  • 1
  • 4
  • 18
Yogs
  • 141
  • 1
  • 2
  • 1
    How did you get this to work? I followed your instructions exactly, but the resource file (with a public access modifier set to 'Public') created basically contains a static class with a protected constructor. In both WPF and Metro, I get an error 'the type ResourceFileName does not include any accessible constructors' in the XAML designer (and another, similar error when it compiles). – Quark Soup Jan 03 '17 at 20:05
  • Worked for me. Just struggled to find the compile 'public' option for the .resx. It's a tiny dropdown menu on top of the .resx editor when opening the .resx file. Additionally the project needs to be rebuild in order to have the strings available – Marco Jun 29 '20 at 12:26
  • Make sure to rebuild the solution at the end – Trake Vital Jul 12 '20 at 10:20
  • For me to get it to work in VS 2019 v16.6.5, I had to change the Properties>Build Action in the Resources.resx file from "Embedded Resource" to "None" and then back to "Embedded Resource." This seems like a bug to me but it worked. – Brett Wertz Aug 03 '20 at 11:39
5

The simplest way is probably to reference the items directly (they are static properties, internal by default):

<TextBlock x:Name="txtMessage" Text="{x:Static MyApp.Properties.Resource.TextString}"/>

If you are working on a localised WPF app though then I'd recommend taking a look at the guidance on CodePlex at http://wpflocalization.codeplex.com/ , and if you're building a composite app (using PRISM or MEF) then I have a blog post on a nice way to accomplish WPF localisation using standard bindings.

Steven Robbins
  • 26,441
  • 7
  • 76
  • 90
2

After a whole day investigation this Comment Xaml localization: Using .resx Resources in Xaml without x:static I found a simple solution to provide multilanguage support with (embedded resources or referenced assembly) *.resx - files. Since Framework 4 there is a base class called DynamicObject for specifying dynamic behavior at run time in namespace System.Dynamic.

I derived following ResourceLoader from System.Dynamic.DynamicObject - class:

public class ResourceLoader : DynamicObject
{
    #region Fields ---------------------------------------------------------------

    private const string DefaultResourcesSuffix = "Resource";
    private ResourceManager _resourceMan;
    private CultureInfo culture;
    private readonly string _defaultAssemblyName;
    private readonly Assembly _defaultAssembly;
    private Assembly theAssembly;
    private string resourcesSuffix;
    private string assembly;

    #endregion // Fields

    #region Properties -----------------------------------------------------------

    /// <summary>
    /// Gets or sets the assembly.
    /// </summary>
    public string Assembly
    {
        get { return assembly; }
        set
        {
            assembly = value;
            theAssembly = System.Reflection.Assembly.Load(assembly);
            _resourceMan = null;
        }
    }

    /// <summary>
    /// Gets or sets the resources suffix.
    /// </summary>
    public string ResourcesSuffix
    {
        get { return resourcesSuffix; }
        set
        {
            resourcesSuffix = value;
            _resourceMan = null;
        }
    }

    /// <summary>
    /// Get, set culture
    /// </summary>
    public CultureInfo CurrentCulture
    {
        get { this.culture = this.culture ?? CultureInfo.InvariantCulture; return this.culture; }
        set { this.culture = value; }
    }

    /// <summary>
    /// Creates new instace of <see cref="System.Resources.ResourceManager"/> at initialisation or change of <see cref="ResourceFileAccessSample.ResourceBinding.ResourceLoader.Assembly"/>.
    /// </summary>
    private ResourceManager ResourceManager
    {
        get
        {
            if (ReferenceEquals(_resourceMan, null))
            {
                ResourceManager temp = new ResourceManager(
                    string.Format("{0}.{1}", Assembly ?? _defaultAssemblyName, ResourcesSuffix ?? DefaultResourcesSuffix),
                    theAssembly ?? _defaultAssembly);
                _resourceMan = temp;
            }
            return _resourceMan;
        }
    }

    #endregion // Properties

    #region Methods --------------------------------------------------------------

    private object GetResource(string name, CultureInfo language)
    {
        if (language == null || language == CultureInfo.InvariantCulture)
            return ResourceManager.GetObject(name);
        return ResourceManager.GetObject(name, language);
    }

    /// <summary>
    /// Provides the implementation for operations that get member values. Classes derived from the <see cref="T:System.Dynamic.DynamicObject"/> class can override this method to specify dynamic behavior for operations such as getting a value for a property.
    /// </summary>
    /// <param name="binder">Provides information about the object that called the dynamic operation. The binder.Name property provides the name of the member on which the dynamic operation is performed. For example, for the Console.WriteLine(sampleObject.SampleProperty) statement, where sampleObject is an instance of the class derived from the <see cref="T:System.Dynamic.DynamicObject"/> class, binder.Name returns "SampleProperty". The binder.IgnoreCase property specifies whether the member name is case-sensitive.</param>
    /// <param name="result">The result of the get operation. For example, if the method is called for a property, you can assign the property value to <paramref name="result"/>.</param>
    /// <returns>
    /// true if the operation is successful; otherwise, false. If this method returns false, the run-time binder of the language determines the behavior. (In most cases, a run-time exception is thrown.)
    /// </returns>
    public override bool TryGetMember(GetMemberBinder binder, out object result)
    {
        result = GetResource(binder.Name, this.culture);

        if (result != null && result.GetType() == typeof(System.Drawing.Bitmap))
        {
            System.Drawing.Bitmap currentBmp = result as System.Drawing.Bitmap;
            currentBmp.MakeTransparent(System.Drawing.Color.Magenta);
            BitmapSource src = Imaging.CreateBitmapSourceFromHBitmap(currentBmp.GetHbitmap(), IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions());
            result = src;
        }
        return result == null ? false : true;
    }

    /// <summary>
    /// Switch set culture
    /// </summary>
    public void SwitchCulture(CultureInfo NewCulture)
    {
        this.culture = NewCulture;
    }
    #endregion // Methods

    #region Constructors ---------------------------------------------------------

    /// <summary>
    /// Initializes a new instance of the <see cref="ResourceLoader"/> class.
    /// </summary>
    public ResourceLoader()
        : this(CultureInfo.InvariantCulture, DefaultResourcesSuffix)
    { }

    /// <summary>
    /// Initializes a new instance of the <see cref="ResourceLoader"/> class.
    /// </summary>
    public ResourceLoader(CultureInfo InitCulture, string ResourceSuffix)
    {
        _defaultAssemblyName = GetType().Assembly.GetName().Name;
        _defaultAssembly = GetType().Assembly;
        this.culture = InitCulture;
        this.resourcesSuffix = ResourceSuffix;
    }

    #endregion // Constructors
}

You can create instance within xaml like this:

<Application x:Class="ResourceFileAccessSample.App"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"           
         xmlns:src="clr-namespace:ResourceFileAccessSample.ResourceBinding"             
         StartupUri="Window1.xaml" Startup="Application_Startup" >

<Application.Resources>
    <src:ResourceLoader x:Key="resource" CurrentCulture="(Default)" ResourcesSuffix="Resource"   />
</Application.Resources>

C# code:

    /// <summary>
/// Interaction logic for Window1.xaml
/// </summary>
public partial class Window1 : Window
{
    private ResourceLoader res;
    public Window1()
    {            
        InitializeComponent();
        // load it from WPF Resources 
        this.res = (ResourceLoader)this.FindResource("resource");
        // or create an instance 
        //this.res = new ResourceLoader(CultureInfo.InvariantCulture, "Resource");      
        this.LayoutRoot.DataContext = res;                    
    }

    private void btnSwichLanguage_Click(object sender, RoutedEventArgs e)
    {            
        res.SwitchCulture(new CultureInfo("de"));               
        this.LayoutRoot.DataContext = null;
        this.LayoutRoot.DataContext = res;                      
    }       
}

Now it is possible to bind strings and images (images will be converted into WPF compilant BitmapSource:

    <StackPanel Name="LayoutRoot" Orientation="Vertical">
    <Label Name="lblText" Content="{Binding Path=rsName, Mode=OneWay}" HorizontalContentAlignment="Center" Margin="5" Padding="0" />
    <Image Source="{Binding Path=AlignObjectsTop}" Height="16" Width="16" Margin="5" />
    <Button Name="btnSwichLanguage" Content="Switch to de" Click="btnSwichLanguage_Click" MinHeight="25" Width="100" />

</StackPanel>
cppguy
  • 3,611
  • 2
  • 21
  • 36
  • This is not compatible with the MVVM pattern because ResourceLoader is assigned to DataContext which can't be used with a ViewModel anymore. – Jinjinov Dec 13 '18 at 14:26
0

The simplest way, where you can define the width of textbox also according to the length of text in each language.

Xaml Code

<TextBlock x:Uid="Greeting" Text="" />

Have a look at resource file:- Click View

Vikrant Singh
  • 642
  • 1
  • 5
  • 17
-2

Hide an other textblock and bind it's text In that textblock you'll have the resource from .cs

cppguy
  • 3,611
  • 2
  • 21
  • 36
xrhstos
  • 101
  • 1
  • 3