WinForms中的资源文件字符串在设计器中如何连接?

18

我正在尝试为多种语言本地化WinForms应用程序。我正在寻找一种方法来设置表单标签/按钮文本属性,以从资源文件中读取设计器中的内容(而不是通过编写代码来设置它们)。

我发现可以设置form.Localizable=true,但是这样资源将从与窗体相邻的文件中读取,而我的资源文件在多个窗体之间共享。

是否有办法在设计器中将标签的文本设置为存储在项目级别的resx文件中的值?


在设计器中这样做的必要性是什么?而在代码中完成这个任务所需付出的努力却很少。 - Walter
你有没有在这方面有什么好运气?我也需要这个。我希望所有的表单都绑定到一个单一的资源文件。 - user93202
不幸的是,我正在用代码连接很多东西 :( - Danny Tuppeny
https://dev59.com/F3RB5IYBdhLWcg3w9b59 - Amir Azizkhani
5个回答

5

我想我找到了一种做这件事的方法!

首先在你的Resources.resx中将访问修饰符设置为Public。

之后,在生成的设计代码(Form.Designer.cs)中,你可以将以下内容写入适当的控件:

this.<control>.Text = Properties.Resources.<stringname>

例如:
this.footerLabel.Text = Properties.Resources.footerString;

备注:我不知道这种解决方案的道德性有多高,但它是可行的!


5
这恰恰是我试图避免的 :( “与其必须维护一段以编程方式设置它们的代码块” - Danny Tuppeny

4

顺便说一下,这很容易实现,您可以为任何喜欢绑定到资源或任何其他类的控件执行此操作。我也会为静态类(如应用程序设置)执行此操作。

输入以下代码:

textBox2.DataBindings.Add("Text", source, "<className>.<PropertyName>");  

我感觉不太好,更不用说拼写了。

这是一个关于应用程序资源下拉列表的样例。

首先,控件包含了1个名为ResourceName的新属性,魔法来自于编辑器,在该属性上面指定的注释叫做ResourceDropDownListPropertyEditor

[Editor(typeof(ResourceDropDownListPropertyEditor), typeof(System.Drawing.Design.UITypeEditor))]
/// <summary>
/// Label bound to resource
/// </summary>
/// <remarks>
/// The bitmap does not appear in the Toolbox for autogenerated controls and components.
/// https://learn.microsoft.com/en-us/dotnet/framework/winforms/controls/how-to-provide-a-toolbox-bitmap-for-a-control</remarks>
/// <seealso cref="System.Windows.Forms.Label" />
[ToolboxBitmap(typeof(Label))]
public partial class ResourceLabel : Label
{

    /// <summary>
    /// backing field for the resource key property
    /// </summary>
    private string mResourceName;
    [Browsable(true)]
    [DefaultValue("")]
    [SettingsBindable(true)]
    [Editor(typeof(ResourceDropDownListPropertyEditor), typeof(System.Drawing.Design.UITypeEditor))]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
    [Description("Select the resource key that you would like to bind the text to.")]
    public string ResourceName
    {
        get { return mResourceName; }
        set
        {
            mResourceName = value;
            if (!string.IsNullOrEmpty(mResourceName))
            {   
                base.Text = Properties.Resources.ResourceManager.GetString(mResourceName);
            }
        }
    }

    /// <summary>
    /// Designer helper method: https://msdn.microsoft.com/en-us/library/ms973818.aspx
    /// </summary>
    /// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns>
    private bool ShouldSerializeResourceName()
    {
        return !string.IsNullOrEmpty(ResourceName);
    }    
    /// <summary>
    /// Will be default text if no resource is available
    /// </summary>
    [Description("default text if no resource is assigned or key is available in the runtime language")]
    public override string Text
    {
        get { return base.Text; }
        set
        {
            // Set is done by resource name.
        }
    }
}

这是用于下拉菜单的类:
```html

这是用于下拉菜单的类:

```
/// <summary>
/// used for editor definition on those properties that should be able 
/// to select a resource
/// </summary>
/// <seealso cref="System.Drawing.Design.UITypeEditor" />
class ResourceDropDownListPropertyEditor : UITypeEditor
{
    IWindowsFormsEditorService _service;

    /// <summary>
    /// Gets the editing style of the <see cref="EditValue"/> method.
    /// </summary>
    /// <param name="context">An ITypeDescriptorContext that can be used to gain additional context information.</param>
    /// <returns>Returns the DropDown style, since this editor uses a drop down list.</returns>
    public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context)
    {
        // We're using a drop down style UITypeEditor.
        return UITypeEditorEditStyle.DropDown;
    }

    /// <summary>
    /// Displays a list of available values for the specified component than sets the value.
    /// </summary>
    /// <param name="context">An ITypeDescriptorContext that can be used to gain additional context information.</param>
    /// <param name="provider">A service provider object through which editing services may be obtained.</param>
    /// <param name="value">An instance of the value being edited.</param>
    /// <returns>The new value of the object. If the value of the object hasn't changed, this method should return the same object it was passed.</returns>
    public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value)
    {
        if (provider != null)
        {
            // This service is in charge of popping our ListBox.
            _service = ((IWindowsFormsEditorService)provider.GetService(typeof(IWindowsFormsEditorService)));

            if (_service != null)
            {


                var items = typeof(Properties.Resources).GetProperties()
                            .Where(p => p.PropertyType == typeof(string))
                            .Select(s => s.Name)
                            .OrderBy(o => o);

                var list = new ListBox();
                list.Click += ListBox_Click;

                foreach (string item in items)
                {
                    list.Items.Add(item);
                }
                if (value != null)
                {
                    list.SelectedValue = value;
                }

                // Drop the list control.
                _service.DropDownControl(list);

                if (list.SelectedItem != null && list.SelectedIndices.Count == 1)
                {
                    list.SelectedItem = list.SelectedItem.ToString();
                    value = list.SelectedItem.ToString();
                }

                list.Click -= ListBox_Click;
            }
        }

        return value;
    }

    private void ListBox_Click(object sender, System.EventArgs e)
    {
        if (_service != null)
            _service.CloseDropDown();


    }
}

最终在设计时间,您会得到像这样的结果: 设计时间视图 资源名称是在控件拖放到表单上时创建的,改变只有在重新编译、关闭/打开表单或拖放新标签时才会看到。

为了成为一个完整的解决方案,这应该在标签上添加一个额外的属性,允许将其绑定到特定的资源类型,然后允许使用该类型的字符串。目前,它需要你定义一个Properties.Resources类型。而且非常有限,因为你必须为每个要绑定的控件属性重新定义一个新属性(或两个)。 - Ian Kemp

4

0
我能想到的唯一方法是创建一个自定义控件,为资源名称添加一个属性。当该属性被设置时,从项目资源文件中获取该值并将其设置为文本属性。您需要确保 Text 不会序列化,否则可能会覆盖 ResourceName 设置的值。
public class ResourceLabel
    : Label
{
    private string mResourceName;
    public string ResourceName
    {
        get { return mResourceName; }
        set
        {
            mResourceName = value;
            if (!string.IsNullOrEmpty(mResourceName))
                base.Text = Properties.Resources.ResourceManager.GetString(mResourceName);
        }
    }

    [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
    public override string Text
    {
        get { return base.Text; }
        set 
        { 
            // Set is done by resource name.
        }
    }
}

1
我曾经考虑过类似的方法,但是这种做法似乎比每个文件都有一个resx文件或者在代码中设置它们更糟糕。我对于没有内置的方法来实现这个功能感到失望,它似乎只是对现有的Localizable属性进行了微小的调整,只是需要一个Resx文件属性而已!:( - Danny Tuppeny

0

我刚刚在研究这个问题。
如果您拥有该控件,即它是您自己的自定义控件,则可以使用CodeDOM

阅读本文以了解一些背景知识,并下载此示例以查看如何完成。

在我们的应用程序中,我们需要使用数据库中的“DisplayText”替换占位符。
因此,我们有像"Order {Product}"这样的文本属性,我们想要用GetDisplayText("Order {Product}")替换。

因此,为了实现这一点,我添加了以下代码:

                statements.OfType<CodeAssignStatement>()
                    .Where(s => s.Left is CodePropertyReferenceExpression && ((CodePropertyReferenceExpression)s.Left).PropertyName == "Text")
                    .ToList().ForEach(s =>
                    {
                        s.Right = new CodeMethodInvokeExpression(
                            new CodeMethodReferenceExpression(new CodeTypeReferenceExpression("Core.DisplayText"), "GetDisplayText"),
                            s.Right);
                    });

然而我仍在尝试中,还没有创建出可行的解决方案...但它可能会对你有所帮助。

:-)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接