在WinForms应用程序中,Panel
用作占位符以将单个用户控件显示为导航策略:每当用户希望导航到给定区域时,相应的用户控件将添加到面板中。简化为:
contentPanel.Controls.Clear();
userControl.Dock = DockStyle.Fill;
contentPanel.Controls.Add(userControl);
由于要求不受我的控制,表单必须支持动态切换语言。这是使用Hans Passant's answer实现并正常工作,并进行了修改以使用用户控件的资源管理器,该资源管理器正确获取并将本地化文本应用于控件。
然后,在应用来自用户控件的相应资源文件的资源之后,由于DockStyle.Fill
导致的布局对于本身未设置为DockStyle.Fill
的用户控件的组成控件而丢失。这具有控制不再拉伸以填充可用区域的效果,并且限于设计器/资源文件中定义的原始大小。请注意,应用资源后,用户控件的Dock
属性仍然正确设置为DockStyle.Fill
。
我创建了一个示例应用程序来说明/重现问题:下面的表单有一个面板,用户控件动态添加到该面板并设置为DockStyle.Fill
。用户控件的标签位于默认语言环境的左上角,位于德语语言环境的右上角。我希望表单捕捉标签,该标签固定在表格右边缘的右侧,但用户控件的大小会重置为设计时的值。 View source code
如果我在德语区域设置上启动表单,则标签正确地布置在表单的右边缘:
我想要发生的是在调用ApplyResources
后保留布局。当然,我可以简单地制作控件'Location
和Size
属性的副本(如another answer中对上述同一问题的建议),但不幸的是,这些属性的值在区域设置之间有所不同。因此,在应用本地化字符串和定位之后,如何指导用户控件重新布局其所有控件?
我尝试了什么
InitializeComponent()
,我尝试将PerformLayout()
调用Panel
容器,用户控件和表单无效。SuspendLayout()
和ResumeLayout(true)
,也没有成功。其他实施细节
对用户更改语言的事件做出反应:
protected virtual void OnChangeCulture(CultureInfo newCulture)
{
System.Threading.Thread.CurrentThread.CurrentCulture = newCulture;
System.Threading.Thread.CurrentThread.CurrentUICulture = newCulture;
SuspendLayout();
ComponentResourceManager resources = new ComponentResourceManager(this.GetType());
ApplyResources(resources, this, newCulture);
ResumeLayout(true);
}
将资源应用于表单中的所有控件:
private void ApplyResources(ComponentResourceManager resourceMgr, Component target, CultureInfo culture)
{
//Since target can be a Control or a Component, get their name and children (OMITTED) in order to apply the resources and recurse
string name;
IEnumerable<Component> children;
//Have the resource manager apply the resources to the given target
resourceMgr.ApplyResources(target, name, culture);
//iterate through the collection of children and recursively apply resources
foreach (Component c in children)
{
//In the case of user controls, they have their own ResourceManager with the translated strings, so get it and use it instead
if (c is UserControl)
resourceMgr = new ComponentResourceManager(c.GetType());
//recursively apply resources to the child
this.ApplyResources(resourceMgr, c, culture);
}
}
非常感谢任何提示!
答案 0 :(得分:2)
我可以建议以下自定义扩展方法:
using System.ComponentModel;
using System.Globalization;
namespace System.Windows.Forms
{
public static partial class Extensions
{
public static void ApplyResources(this Control target, CultureInfo culture = null)
{
ApplyResources(new ComponentResourceManager(target.GetType()), target, "$this", culture);
}
static void ApplyResources(ComponentResourceManager resourceManager, Control target, string name, CultureInfo culture = null)
{
// Preserve and reset Dock property
var dock = target.Dock;
target.Dock = DockStyle.None;
// Reset Anchor property
target.Anchor = AnchorStyles.Top | AnchorStyles.Left;
// Have the resource manager apply the resources to the given target
resourceManager.ApplyResources(target, name, culture);
// Iterate through the collection of children and recursively apply resources
foreach (Control child in target.Controls)
{
if (child is UserControl)
ApplyResources(child, culture);
else
ApplyResources(resourceManager, child, child.Name, culture);
}
// Restore Dock property
target.Dock = dock;
}
}
}
基本的变化是两个。
首先,由于存储的位置/大小是相对于容器设计大小(在停靠之前),我们保留Dock
属性,在None
调用期间将其重置为ApplyResources
控件及其子控件,最后将其恢复为当前值。
这基本上解决了右锚的问题。但是,由于Windows窗体设计器不保存具有默认值的属性值,并且Anchor
属性的默认值为AnchorStyles.Top | AnchorStyles.Left
,因此它未存储,因此未正确设置(从德语转到英语时)在你的样本中)。
所以第二个问题是在调用ApplyResources
之前将其重置为默认值。
用法很简单:
protected virtual void OnChangeCulture(CultureInfo newCulture)
{
System.Threading.Thread.CurrentThread.CurrentCulture = newCulture;
System.Threading.Thread.CurrentThread.CurrentUICulture = newCulture;
SuspendLayout();
this.ApplyResources(); // <--
ResumeLayout(true);
}
请注意,SuspendLayout
和ResumeLayout
来电不是必需的 - 无论是否有效,都可以使用。它们用于最终防止闪烁,这在您的示例中不会发生。