1

I made Custom Control and I added a new property that is meant to point to a Resource.
How can I get the value, which is set in the Form Designer, of this property from another property of the Custom Control? When I try to read it, the value it return is null.

enter image description here

My code related to the of Custom Control and the mentioned property:

class ResourceLabel : Label
{
    private string resourceKey;
    
    [Category("Appearance")]
    [Browsable(true)]
    [Description("Sets the resource key for localization")]
    public string ResourceKey
    {
        get { return resourceKey; }
        set {
            if (resourceKey != value) {
                resourceKey = value;
                Invalidate();
            }
        }
    }

    [Browsable(true)]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
    [EditorBrowsable(EditorBrowsableState.Always)]
    [Bindable(true)]
    public override string Text
    {
        if (base.Text != value) {
            Console.WriteLine($"Test: {ResourceKey}");

            if (ResourceKey != null) {
                var locale = CultureInfo.GetCultureInfo(Properties.Settings.Default.Language);
                var textFromResource = Resources.ResourceManager.GetString(ResourceKey, locale);
                base.Text = textFromResource;
            } 
            else {
                base.Text = value;
            }
        }
    }
}

This string Console.WriteLine($"Test: {ResourceKey}"); returns null.

Jimi
  • 29,621
  • 8
  • 43
  • 61
  • 2
    https://learn.microsoft.com/en-us/dotnet/api/system.componentmodel.isupportinitialize?view=netframework-4.8 – Hans Passant Sep 13 '20 at 12:02
  • 1
    Hans' suggestion is *interesting*, but the point here (the point I'm trying to make, at least) is that you, most probably, don't need this property at all. When you localize your application, using the `Localizable` Designer property, a new `.resx` file is generated for each language you choose in the PropertyGrid. So, you don't really need to *pick* a value from resources: this is done automatically when the CurrentCulture changes (`Thread.CurrentThread.CurrentCulture` and `Thread.CurrentThread.CurrentUICulture` change). See these features before anything else. – Jimi Sep 13 '20 at 15:46
  • 1
    You could choose [Change language at runtime in C# winform](https://stackoverflow.com/a/21068497/7444103) or [How to make multi-language app in Winforms?](https://stackoverflow.com/a/32990088/7444103). What I, personally, suggest to do is pretty much clear. – Jimi Sep 13 '20 at 15:55
  • [Change language at runtime in C# winform](https://stackoverflow.com/a/21068497/7444103) It's works at runtime. Thank you so much! – snikitin-de Sep 13 '20 at 17:13
  • 1
    It's a choice. Note that the method(s) shown there are incomplete, Components (and quasi-Components) are not included. External debugger-visualizers can *go nuts* (or other stuff you may not be interested in at this time) – Jimi Sep 13 '20 at 17:20
  • It's working so strange. The language changes only one time and only one control changes the language. Also form's title changes. – snikitin-de Sep 13 '20 at 18:26

1 Answers1

3

Given the description of the problem and the Description attribute applied to the ResourceKey property of your Custom Control, it appears that you're localizing your application, setting - as a consequence - the Localizable property of the parent Form to true.

This changes some details in the Form's initialization.
If you look inside the Form.Designer.cs file, you'll notice that some new parts have been added:
The usual:
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(YourForm));

has now new companions. In the initialization section of each control, resources.ApplyResources(this.controlName, "controlName"); has been added.

Your custom Label should show, e.g.:

resources.ApplyResources(this.resourceLabel1, "resourceLabel1");

Many of the standard properties derived from Control are localizable (are decorated with a [Localizable(true)] attribute). The Text property is of course among these. The value of these properties is now stored in the resource files used for the different localizations. Hence, these properties are not set in the Designer.cs file anymore and also are initialized before other properties.

Your ResourceKey property is not defined as Localizable, so it's added in the Designer.cs file and initialized after the localizable properties.

▶ A simple fix is to define the ResourceKey property as Localizable, so it will be initialized before the non-localized properties, along with the Text property.

[Localizable(true), Browsable(true)]
[Category("Appearance"), Description("Sets the resource key for localization")]
public string ResourceKey {
    get { return resourceKey; }
    set {
        if (resourceKey != value) {
            resourceKey = value;
            Invalidate();
        }
    }
}

Note that this is somewhat a breaking change, you should:

1 - Add the Localizable attribute to the ResourceKey Property
2 - Remove the old ResourceLabel Control from the Form Designer
3 - Compile the Solution
4 - Add the custom ResourceLabel back where it was
5 - Set the required Control's properties in the Properties Panel
6 - Run the application or compile the Project

Check the Designer.cs file of your Form, see that the ResourceKey property has disappeared, its value is now set through resources.ApplyResource().

▶ If you don't want to make that property localizable, you'll have to read its value later, when the Control's initialization is complete; overriding OnHandleCreated, for example. Note that the Handle of a Control can be re-created at run-time in a few occasions (setting key properties that require the Control to recreate the Handle).

Note:
You should not rely on the initialization sequence of your properties, there's no real guarantee that a Property is initialized before another. Take this into consideration while designing a Control's behavior.

Jimi
  • 29,621
  • 8
  • 43
  • 61
  • Have I understood right? My code has no guarantee that it will be execute the same way every time. I added Localizable attribute to my property and it works well. – snikitin-de Sep 13 '20 at 11:35
  • If your code relies on properties values in the initialization phase, then no: as in this case, as a good example, properties values are not meant to be initialized in a specific sequence (not all of them anyway). You can assign default values, in the class Constructor, set a `[DefaultValue()]` attribute and compare this value with the current etc. In general, you better not assume that a Property will be already initialized or have a specific value at a given time (especially when the class is first initialized). If you explain what this property is used for, you may have better advice. – Jimi Sep 13 '20 at 11:51
  • I use this property for get the value from a resource file and set the value to Text property. `var textFromResource = Resources.ResourceManager.GetString(ResourceKey, locale);` – snikitin-de Sep 13 '20 at 14:46