简体   繁体   English

在调用ApplyResources之后重新应用动态添加的UserControl的布局

[英]Re-apply layout of a dynamically added UserControl after calling ApplyResources

In a WinForms application, a Panel is used as a placeholder to display a single User Control as a navigation strategy: whenever the user wishes to navigate to a given area, the respective User Control is added to the Panel. 在WinForms应用程序中, Panel用作占位符以将单个用户控件显示为导航策略:每当用户希望导航到给定区域时,相应的用户控件将添加到Panel。 Simplified: 简化:

contentPanel.Controls.Clear();
userControl.Dock = DockStyle.Fill;
contentPanel.Controls.Add(userControl);

As a result of a requirement that is out of my control, the Form must support switching the language dynamically. 由于要求不受我的控制,表单必须支持动态切换语言。 This is implemented and working fine using Hans Passant's answer , with a modification to use the User Control's Resource Manager, which correctly gets and applies the localized text to controls. 使用Hans Passant的答案实现并正常工作,并修改使用用户控件的资源管理器,该管理器正确获取并将本地化文本应用于控件。

After applying the resources from the User Control's respective resource file, however, the layout resulting from DockStyle.Fill is lost for the User Control's constituent controls that are not themselves set to have a DockStyle.Fill . 但是,在从User Control的相应资源文件中应用资源之后, DockStyle.Fill导致的布局将丢失,因为User Control的组成控件本身并未设置为具有DockStyle.Fill This has the effect that controls no longer stretch to fill the available area, and are limited to the original size defined in the designer/resource file. 这具有控制不再拉伸以填充可用区域的效果,并且限于设计器/资源文件中定义的原始大小。 Note that the Dock property of the User Control is still set correctly to DockStyle.Fill after applying the resources. 请注意,应用资源后,User Control的Dock属性仍然正确设置为DockStyle.Fill

I created an example application which illustrates/reproduces the problem: the form below has a panel to which a user control is added dynamically and set to DockStyle.Fill . 我创建了一个示例应用程序来说明/重现问题:下面的表单有一个面板,动态添加用户控件并设置为DockStyle.Fill The user control has a label which is anchored top left on the Default locale and top right in the German locale. 用户控件的标签位于默认语言环境的左上角,位于德语语言环境的右上角。 I would expect the form to snap the label which is anchored to the right against the right margin of the form, but the size of the user control is reset to the value at design time. 我希望表单捕捉标签,该标签固定在表格右边缘的右侧,但用户控件的大小会重置为设计时的值。 View source code . 查看源代码

If I start the form on the German locale, the label is correctly laid out against the right edge of the form: 如果我在德语区域设置上启动表单,则标签将正确放置在表单的右边缘:

在此输入图像描述

What I'd like to happen is that the layout is retained after calling ApplyResources . 我想要发生的是在调用ApplyResources后保留布局。 Of course I could simply make a copy of the controls' Location and Size properties (as suggested in another answer to the same question mentioned above) but unfortunately values of these properties differ between locales. 当然,我可以简单地复制控件的LocationSize属性(如上面提到的同一问题的另一个答案中所建议的),但遗憾的是这些属性的值在区域设置之间有所不同。 So, after the localized string and positioning are applied, how can the User Control be directed to layout all its controls anew? 因此,在应用本地化字符串和定位之后,如何指导用户控件重新布局其所有控件?

What I've tried 我试过的

  • By looking into InitializeComponent() , I've tried calling PerformLayout() to the Panel container, the User Control, and the Form to no avail. 通过查看InitializeComponent() ,我尝试调用PerformLayout()Panel容器,用户控件和表单无济于事。
  • Adding SuspendLayout() and ResumeLayout(true) before and after the call to ApplyResources, also without success. 在调用ApplyResources之前和之后添加SuspendLayout()ResumeLayout(true) ,也没有成功。

Additional implementation details 其他实施细节

  • References to instantiated User Controls are kept in a private dictionary in the Main Form. 对实例化用户控件的引用保存在主窗体的私有字典中。 When navigation for that control is raised, the previous user control is removed and the existing reference added with the snippet above. 当引发该控件的导航时,将删除先前的用户控件,并使用上面的代码段添加现有引用。
  • Reacting to the user event of changing the language: 对用户更改语言的事件做出反应:

     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); } 
  • Applying the resources to all controls in the form: 将资源应用于表单中的所有控件:

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

Many thanks in advance for any pointers! 非常感谢任何指针!

I can suggest the following custom extension method: 我可以建议以下自定义扩展方法:

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

The essential changes are two. 基本的变化是两个。

First, since the stored location/sizes are relative to the container design size (before being docked), we preserve the Dock property, reset it to None during the ApplyResources calls to the control and its children, and finally restore it to the current value. 首先,由于存储的位置/大小是相对于容器设计大小(在停靠之前),我们保留Dock属性,在对控件及其子控件的ApplyResources调用期间将其重置为None ,最后将其恢复为当前值。

This basically resolves the issue with right anchor. 这基本上解决了右锚的问题。 However, since Windows Forms designer doesn't save property values having default values, and the default value for Anchor property is AnchorStyles.Top | AnchorStyles.Left 但是,由于Windows窗体设计器不保存具有默认值的属性值,因此Anchor属性的默认值为AnchorStyles.Top | AnchorStyles.Left AnchorStyles.Top | AnchorStyles.Left , it's not stored and hence is not set correctly (when going from German to English in your sample). AnchorStyles.Top | AnchorStyles.Left ,它没有存储,因此没有正确设置(当你的样本从德语到英语时)。

So the second fix is to simply reset it to its default value before ApplyResources call. 所以第二个修复是在ApplyResources调用之前将其重置为默认值。

The usage is simple: 用法很简单:

protected virtual void OnChangeCulture(CultureInfo newCulture)
{
    System.Threading.Thread.CurrentThread.CurrentCulture = newCulture;
    System.Threading.Thread.CurrentThread.CurrentUICulture = newCulture;

    SuspendLayout();
    this.ApplyResources(); // <--
    ResumeLayout(true);
}

Note that SuspendLayout and ResumeLayout calls are not essential - it works with or without them. 请注意, SuspendLayoutResumeLayout调用不是必需的 - 它可以使用或不使用它们。 They are used to eventually prevent flickering, which doesn't happen in your example. 它们用于最终防止闪烁,这在您的示例中不会发生。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM