WinForms strings in resource files, wired up in designer

38,325

Solution 1

To answer the question, no.

But IMO, this should not be done anyways if the text will be static.

Have a read at my answers on localization and resources:
Resource string location
Globalize an existing Windows Forms application
Using .resx files for global application messages

Solution 2

Easy enough to implement, by the way, this can be done for any type of control you like to bind to a resource, or any other class. I do this for static classes like my application settings as well.

Entering code like this:

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

is not giving me a "good feeling", never mind the spelling

Here is a litle sample of the above label that provides a dropdown on the resources of a application.

First the control, contains 1 new property named ResourceName the magic comes from the editor, this one is specified in the annotation above the property and is called ResourceDropDownListPropertyEditor

[Editor(typeof(ResourceDropDownListPropertyEditor), typeof(System.Drawing.Design.UITypeEditor))]

The code for the label class:

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

Here is the class used for the drop down:

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


    }
}

In the end what you get will look like this at design-time: The design-time View

The resource names are created when you drop the control on your form, changes are not seen till you re-compile and close/open the form or drop a new label on the form.

Solution 3

I think I found a way to do this!

First in your Resources.resx set the Access Modifier to Public.

After that in the designer generated code (Form.Designer.cs) you can write this to the appropriate control:

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

for example:

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

ps.:I don't know how ethical this solution is, but it works!

Share:
38,325

Related videos on Youtube

Danny Tuppeny
Author by

Danny Tuppeny

Updated on July 09, 2022

Comments

  • Danny Tuppeny
    Danny Tuppeny almost 2 years

    I'm trying to localise a WinForms app for multiple languages. I'm trying to find a way to set my form labels/buttons text properties to read from the resources file in the designer (rather than having to maintain a chunk of code that sets them programatically).

    I've found I can set form.Localizable=true, but then the resources are read from a file alongside the form, but many of mine are shared across multiple forms.

    Is there any way to set a label's text in the designer, to a value stored in a project-level resx file?

    • Walter
      Walter over 14 years
      What's the need behind doing this in the designer? The effort of doing this in code minimal.
    • Admin
      Admin over 14 years
      Have you had any luck with this? I need this as well. I want all my forms to bind to a single resource file.
    • Danny Tuppeny
      Danny Tuppeny over 14 years
      Sadly not. I'm wiring a lot of stuff up in code :(
    • amir azizkhani
      amir azizkhani over 4 years
  • Danny Tuppeny
    Danny Tuppeny over 14 years
    I thought about something similar, but this seems more nasty than having one resx per file or setting them in code. I'm disappointed that there appears no built-in way to do this, it seems only a minor tweak from what exists with the Localizable property, only that it needs a Resx file property too! :(
  • Danny Tuppeny
    Danny Tuppeny over 11 years
    This is exactly what I was trying to avoid :( "rather than having to maintain a chunk of code that sets them programatically"
  • Ian Kemp
    Ian Kemp over 3 years
    To be a complete solution, this should add an extra property on the label that allows it to be bound to a specific resource type, and then allows a string from that type to be used. As-is, it currently requires you to have a Properties.Resources type defined. It's also very limited because you have to re-define a new property (or two) for each control property you want to bind to...