Строки WinForms в файлах ресурсов, подключенные в конструкторе

Я пытаюсь локализовать приложение WinForms для нескольких языков. Я пытаюсь найти способ установить свойства текста меток / кнопок формы для чтения из файла ресурсов в конструкторе (вместо того, чтобы поддерживать кусок кода, который устанавливает их программно).

Я обнаружил, что могу установить form.Localizable = true, но тогда ресурсы считываются из файла вместе с формой, но многие из моих используются в нескольких формах.

Есть ли способ установить для текста метки в дизайнере значение, хранящееся в файле resx на уровне проекта?


person Danny Tuppeny    schedule 03.12.2009    source источник
comment
Зачем это нужно делать в дизайнере? Усилия сделать это в коде минимальны.   -  person Walter    schedule 04.12.2009
comment
Тебе с этим повезло? Мне это тоже нужно. Я хочу, чтобы все мои формы были привязаны к одному файлу ресурсов.   -  person    schedule 09.12.2009
comment
К сожалению, нет. Я запрограммировал много чего в коде :(   -  person Danny Tuppeny    schedule 11.12.2009
comment
stackoverflow.com/questions/453161/   -  person amir110    schedule 27.10.2019


Ответы (5)


Чтобы ответить на вопрос, нет.

Но ИМО, этого не следует делать в любом случае, если текст будет статическим.

Прочтите мои ответы о локализации и ресурсах:
Расположение строки ресурса < br> Глобализация существующего приложения Windows Forms
Использование файлов .resx для сообщений глобального приложения

person Jon Seigel    schedule 15.01.2010

Думаю, я нашел способ это сделать!

Сначала в файле Resources.resx установите для модификатора доступа значение Public.

После этого в сгенерированном дизайнером коде (Form.Designer.cs) вы можете записать это в соответствующий элемент управления:

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

Например:

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

ps .: Не знаю, насколько это этично решение, но оно работает!

person Isti115    schedule 30.01.2013
comment
Это именно то, чего я пытался избежать :( вместо того, чтобы поддерживать кусок кода, который устанавливает их программно - person Danny Tuppeny; 01.02.2013

Между прочим, достаточно легко реализовать, это можно сделать для любого типа элемента управления, который вы хотите привязать к ресурсу или любому другому классу. Я делаю это и для статических классов, таких как настройки моего приложения.

Ввод такого кода:

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


    }
}

В итоге во время разработки вы получите следующее:  Просмотр во время разработки

Имена ресурсов создаются, когда вы перетаскиваете элемент управления в форму, изменения не видны, пока вы не перекомпилируете и не закроете / не откроете форму или не добавите новую метку в форму.

person Walter Vehoeven    schedule 30.04.2018
comment
Чтобы быть законченным решением, это должно добавить к метке дополнительное свойство, которое позволяет привязать его к определенному типу ресурса , а затем позволяет использовать строку из этого типа. Как есть, в настоящее время требуется, чтобы у вас был определен тип Properties.Resources. Это также очень ограничено, потому что вам нужно переопределить новое свойство (или два) для каждого свойства элемента управления, к которому вы хотите привязать ... - person Ian Kemp; 21.09.2020

Единственный способ, который я могу придумать, - это создать настраиваемый элемент управления, который добавлял бы свойство для имени ресурса. Когда свойство установлено, возьмите значение из файла ресурсов проекта и установите с ним свойство 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.
        }
    }
}
person Brian    schedule 04.12.2009
comment
Я думал о чем-то подобном, но это кажется более неприятным, чем иметь один resx для каждого файла или устанавливать их в коде. Я разочарован тем, что встроенного способа сделать это не представляется возможным, это всего лишь незначительное изменение по сравнению с тем, что существует со свойством Localizable, только то, что ему также требуется свойство файла Resx! :( - person Danny Tuppeny; 04.12.2009

Я только что присмотрелся к этому самому.
Если у вас есть элемент управления, то есть это ваш собственный пользовательский элемент управления, вы можете использовать CodeDOM

Прочтите эта статья, чтобы получить некоторую справочную информацию, и загрузите этот пример, чтобы увидеть, как это делается. .

В нашем приложении нам нужно заменить заполнители на «DisplayText» из базы данных.
Итак, у нас есть свойства Text, такие как "Order {Product}", и мы хотим заменить их на _2 _ ")`.

Поэтому для этого я добавил следующий код:

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

Однако я все еще экспериментирую с этим и еще не создал рабочего решения ... Но это может вам помочь.

:-)

person Luke T O'Brien    schedule 07.02.2019