【问题标题】:WinForms strings in resource files, wired up in designer资源文件中的 WinForms 字符串,在设计器中连接
【发布时间】:2009-12-03 21:29:34
【问题描述】:

我正在尝试为多种语言本地化 WinForms 应用程序。我正在尝试找到一种方法来设置我的表单标签/按钮文本属性以从设计器中的资源文件中读取(而不是必须维护一大块以编程方式设置它们的代码)。

我发现我可以设置 form.Localizable=true,但随后从表单旁边的文件中读取资源,但我的许多资源在多个表单之间共享。

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

【问题讨论】:

  • 在设计师中这样做的背后有什么需要?在代码中执行此操作的工作量很小。
  • 你有什么运气吗?我也需要这个。我希望我的所有表单都绑定到一个资源文件。
  • 遗憾的是没有。我在代码中连接了很多东西:(

标签: c# .net winforms resources


【解决方案1】:

要回答这个问题,不。

但是 IMO,如果文本是静态的,则无论如何都不应该这样做。

阅读我关于本地化和资源的答案:
Resource string location
Globalize an existing Windows Forms application
Using .resx files for global application messages

【讨论】:

    【解决方案2】:

    我想我找到了办法!

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

    之后在设计器生成的代码 (Form.Designer.cs) 中,您可以将其写入相应的控件:

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

    例如:

    this.footerLabel.Text = Properties.Resources.footerString;
    

    ps.:我不知道这个解决方案有多合乎道德,但它确实有效!

    【讨论】:

    • 这正是我试图避免的 :( “而不是必须维护一大块以编程方式设置它们的代码”
    【解决方案3】:

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

    像这样输入代码:

    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://docs.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.
            }
        }
    }
    

    这是用于下拉的类:

    /// <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();
    
    
        }
    }
    

    最终,您在设计时得到的结果如下所示:

    资源名称是在您将控件放在表单上时创建的,在您重新编译并关闭/打开表单或在表单上放置新标签之前不会看到更改。

    【讨论】:

    • 作为一个完整的解决方案,这应该在标签上添加一个额外的属性,允许它绑定到特定的资源 type,然后允许来自该类型的字符串要使用的。目前,它要求您定义 Properties.Resources 类型。它也非常有限,因为您必须为要绑定到的每个控件属性重新定义一个(或两个)新属性...
    【解决方案4】:

    我能想到的唯一方法是创建一个自定义控件,为资源名称添加一个属性。设置属性后,从项目资源文件中获取值并使用它设置文本属性。您需要确保 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.
            }
        }
    }
    

    【讨论】:

    • 我想过类似的事情,但这似乎比每个文件一个 resx 或在代码中设置它们更令人讨厌。我很失望似乎没有内置的方法来做到这一点,它似乎只是对 Localizable 属性存在的一个小调整,只是它也需要一个 Resx 文件属性! :(
    【解决方案5】:

    我一直在看这个东西。
    如果您拥有该控件,即它是您自己的自定义控件,您可以使用CodeDOM

    阅读this article 了解一些背景知识,阅读download this example 了解它是如何完成的。

    在我们的应用中,我们需要将占位符替换为数据库中的“DisplayText”。
    所以我们有像"Order {Product}"这样的Text属性,我们想用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);
                        });
    

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

    :-)

    【讨论】:

      猜你喜欢
      • 2016-05-05
      • 2012-12-26
      • 1970-01-01
      • 2018-01-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多