Reapply the layout of the dynamically added UserControl after calling ApplyResources

In the WinForms application, a Panel used as a placeholder to display a single user control as a navigation strategy: whenever a user wants to go to this area, the corresponding user control is added to the panel. Simplified:

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

As a result of a requirement that is not under my control, the form must support dynamic language switching. This is implemented and works fine using the Hans Passant answer , with a modification to using the user management resource manager, which correctly receives and applies localized text to the controls.

However, after applying resources from the user control resource file, the layout obtained as a result of DockStyle.Fill is lost for user control elements that are not themselves configured to have DockStyle.Fill . This leads to the fact that the controls are no longer stretched to fill the available area, and are limited to the original size defined in the constructor / resource file. Note that the Dock User Control property is still correctly set to DockStyle.Fill after applying resources.

I created an example application that illustrates / reproduces the problem: in the form below there is a panel to which the user control is added dynamically and installed on DockStyle.Fill . The user control has a label that is pinned in the upper left corner of the default locale and in the upper right corner in German. I would expect the form to snap a label anchored to the right of the right field of the form, but the size of the reset user control is equal to the value at design time. View source code .

> </a> <a href =

If I run the form in German, the label is correctly laid out against the right edge of the form:

enter image description here

I would like the layout to be saved after calling ApplyResources . Of course, I could just make a copy of the Location and Size control properties (as suggested in another answer to the same question mentioned above), but, unfortunately, the values โ€‹โ€‹of these properties differ between locales. So, after the localized string and positioning are applied, how can the user control be redirected to all its controls again?

What i tried

  • Studying InitializeComponent() , I tried to call PerformLayout() in the Panel container, User Control and the form to no avail.
  • Adding SuspendLayout() and ResumeLayout(true) before and after the ApplyResources call is also unsuccessful.

Additional Implementation Details

  • Links to user-created user controls are stored in a private dictionary in the main form. When the navigation for this control is raised, the previous user control is deleted, and the existing link is added with the snippet above.
  • Response to a custom language change event:

     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); } 
  • The use of resources for 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); } } 

Thanks a lot in advance for any pointers!

+5
source share
1 answer

I can suggest the following 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; } } } 

Significant changes - two.

First, since the saved location / size refers to the size of the containerโ€™s design (before docking), we save the Dock property, reset it None during ApplyResources calls for the control and its children, and finally restore it to its current value.

This basically fixes the problem with the correct binding. However, since the Windows Forms developer does not save property values โ€‹โ€‹that have default values, and the default value for the Anchor property is AnchorStyles.Top | AnchorStyles.Left AnchorStyles.Top | AnchorStyles.Left , it is not saved and, therefore, is not installed correctly (when switching from German to English in your example),

So, the second fix is โ€‹โ€‹to simply reset its default value before calling ApplyResources .

The use 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 the SuspendLayout and ResumeLayout calls are not significant - it works with or without them. They are used to prevent flickering, which does not happen in your example.

+2
source

Source: https://habr.com/ru/post/1275892/


All Articles