我正在尝试为多种语言本地化WinForms应用程序。我正在寻找一种方法来设置表单标签/按钮文本属性,以从资源文件中读取设计器中的内容(而不是通过编写代码来设置它们)。
我发现可以设置form.Localizable=true,但是这样资源将从与窗体相邻的文件中读取,而我的资源文件在多个窗体之间共享。
是否有办法在设计器中将标签的文本设置为存储在项目级别的resx文件中的值?
我正在尝试为多种语言本地化WinForms应用程序。我正在寻找一种方法来设置表单标签/按钮文本属性,以从资源文件中读取设计器中的内容(而不是通过编写代码来设置它们)。
我发现可以设置form.Localizable=true,但是这样资源将从与窗体相邻的文件中读取,而我的资源文件在多个窗体之间共享。
是否有办法在设计器中将标签的文本设置为存储在项目级别的resx文件中的值?
我想我找到了一种做这件事的方法!
首先在你的Resources.resx中将访问修饰符设置为Public。
之后,在生成的设计代码(Form.Designer.cs)中,你可以将以下内容写入适当的控件:
this.<control>.Text = Properties.Resources.<stringname>
this.footerLabel.Text = Properties.Resources.footerString;
备注:我不知道这种解决方案的道德性有多高,但它是可行的!
顺便说一下,这很容易实现,您可以为任何喜欢绑定到资源或任何其他类的控件执行此操作。我也会为静态类(如应用程序设置)执行此操作。
输入以下代码:
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.
}
}
}
这是用于下拉菜单的类:
```/// <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 Kemppublic 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.
}
}
}
我刚刚在研究这个问题。
如果您拥有该控件,即它是您自己的自定义控件,则可以使用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);
});
然而我仍在尝试中,还没有创建出可行的解决方案...但它可能会对你有所帮助。
:-)