从 XAML 中的 *.resx 文件中获取值

Posted

技术标签:

【中文标题】从 XAML 中的 *.resx 文件中获取值【英文标题】:Get values from *.resx files in XAML 【发布时间】:2011-02-24 13:13:00 【问题描述】:

是否可以将资源文件中的一些值直接添加到 XAML 标记中?或者为了本地化,我们总是必须在 *.cs 文件中做这样的事情:

txtMessage.Text = Messages.WarningUserMessage;

Messages 是资源,txtMessage 是 TextBlock。

【问题讨论】:

您是如何创建消息资源文件的?您是否刚刚将 Messages.resx 添加到您的属性文件夹中?如果是这样,我无法访问此文件。 @Sergey,不管它在哪里。当然,您也可以将其添加到属性文件夹中。当然 resx 文件必须在项目目录中。可能你不能将它从 VS 添加到属性目录?顺便说一句,将 smth 添加到属性目录是不好的做法。更好地创建“资源”目录来保存你的 res 文件。 密切相关:***.com/questions/12363834/… 【参考方案1】:

确保在 resx 编辑器中将 Code Generation 设置为 Public,然后您可以简单地使用:

<TextBlock Text="x:Static Messages.WarningUserMessage" />

【讨论】:

我是 WPF 新手,这个答案正是我所需要的!谢谢。 请注意,它仅在Messages 与屏幕位于同一命名空间时才有效。 ... 如果 Messages 不在不同的程序集中,因为构造函数是作为内部生成的。 @DaviFiamenghi:构造函数无所谓,我们这里只访问静态成员。 您仍然需要在设计器中添加命名空间,如@Yogs 答案 xmlns:resource="clr-namespace:YourProject.Properties" 并像 x:Static resource:Resources.ResourceName 一样使用它【参考方案2】:

这样做要容易得多。在 XAML 文件中添加一个 xmlns 并直接使用资源。

xmlns:resx="clr-namespace:wpfapplicationname.Properties"
Title="x:Static resx:Resources.name"

【讨论】:

这种方法的唯一缺点当然是你不能动态地改变文化。有一篇很棒的文章介绍了如何通过在某处使用标记扩展和本地化管理器来完成该行为。 这里是:grumpydev.com/2009/09/08/… @DaviFiamenghi 谢谢,但是没了,这是存档版本:web.archive.org/web/20210629145415/http://www.grumpydev.com/…【参考方案3】:

我知道我的回复有点晚了,但我认为它值得分享:

要使用存储在 *.resx 文件中的字符串而不使用静态关键字:

    在 App.Xaml 文件中为属性添加命名空间 xmlns:resource="clr-namespace:YourProject.Properties"

    在 ApplicationResources(app.xaml 文件)中为您的 *.resx 文件添加资源

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

    在您的 XAML 文件中使用以下 Binding,让我们以 Window Title 为例

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

    TitleString 是 *.resx 文件中 StringProperty 的名称

    最后但同样重要的是,不要忘记将资源文件访问修饰符更改为 Public。

【讨论】:

你是如何让它工作的?我完全按照您的说明进行操作,但是创建的资源文件(公共访问修饰符设置为“公共”)基本上包含一个带有受保护构造函数的静态类。在 WPF 和 Metro 中,我在 XAML 设计器中收到错误“ResourceFileName 类型不包括任何可访问的构造函数”(以及编译时的另一个类似错误)。 为我工作。只是努力为 .resx 找到编译“公共”选项。打开 .resx 文件时,它是 .resx 编辑器顶部的一个小下拉菜单。此外,需要重建项目才能使字符串可用 确保最后重建解决方案 为了让它在 VS 2019 v16.6.5 中工作,我必须将 Resources.resx 文件中的 Properties>Build Action 从“Embedded Resource”更改为“None”,然后返回“嵌入式资源”。这对我来说似乎是一个错误,但它确实有效。【参考方案4】:

最简单的方法可能是直接引用项目(它们是静态属性,默认为内部):

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

如果您正在开发本地化的 WPF 应用程序,那么我建议您查看 http://wpflocalization.codeplex.com/ 上的 CodePlex 指南,如果您正在构建复合应用程序(使用 PRISM 或 MEF),那么我有一个blog post on a nice way to accomplish WPF localisation using standard bindings.

【讨论】:

【参考方案5】:

经过一整天的调查,这条评论 Xaml localization: Using .resx Resources in Xaml without x:static 我找到了一个简单的解决方案,可以通过(嵌入式资源或引用的程序集)*.resx - 文件提供多语言支持。 从 Framework 4 开始,有一个名为 DynamicObject 的基类,用于在命名空间 System.Dynamic 中指定运行时的动态行为。

我从 System.Dynamic.DynamicObject 派生了以下 ResourceLoader - 类:

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

您可以像这样在 xaml 中创建实例:

<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#代码:

    /// <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;                      
           

现在可以绑定字符串和图片了(图片会被转换成WPF兼容的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>

【讨论】:

这与 MVVM 模式不兼容,因为 ResourceLoader 被分配给 DataContext 不能再与 ViewModel 一起使用。【参考方案6】:

最简单的方法,您可以根据每种语言的文本长度来定义文本框的宽度。

Xaml 代码

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

Have a look at resource file:- Click View

【讨论】:

请按此格式设置您的代码,在所有代码行前面输入 4 个空格。【参考方案7】:

隐藏另一个文本块并绑定它的文本 在该文本块中,您将拥有来自 .cs 的资源

【讨论】:

以上是关于从 XAML 中的 *.resx 文件中获取值的主要内容,如果未能解决你的问题,请参考以下文章

如何在xaml和resx中使用GNU gettext

如何从页面中的 App.xaml 获取颜色值

WPF 访问 Resources.resx 资源文件

如何使用特定区域性从 App_LocalResources 文件夹中的 resx 文件中读取?

循环遍历 .resx 文件中的所有资源

.net 核心中的 MissingManifestResourceException