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. 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.
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
. 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.
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
. 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
. 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. 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
InitializeComponent()
, I've tried calling PerformLayout()
to the Panel
container, the User Control, and the Form to no avail.SuspendLayout()
and ResumeLayout(true)
before and after the call to ApplyResources, also without success.Additional implementation details
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.
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
, it's not stored and hence is not set correctly (when going from German to English in your sample).
So the second fix is to simply reset it to its default value before ApplyResources
call.
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. They are used to eventually prevent flickering, which doesn't happen in your example.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With