1

I'm asking this as a possible workaround to this other question. The problem revolves around a recursive call to ResourceManager.ApplyResources() on all Controls in a Form, which results in all anchoring/layout getting reset to the sizes defined in the designer (i.e. the default values in the ResourceManager). While the other question seeks to resolve the problem by attempting to reapply the layout after the resources have been applied, an alternative would be to steer the behavior of ApplyResources so that only the localized strings are applied to the Controls instead of Size and Location properties, which are the cause of the undesired behavior.

The resource files for the different locales were created automatically by the designer by setting the Form's Localizable property to true, switching the Form's language to the locale, and setting the Control's text to the translation in that particular locale.

So, is this possible without having to manually set the properties one by one with ResourceManager.GetString()?

Thanks in advance!

user5877732
  • 371
  • 3
  • 19

1 Answers1

2

One way I can think of is to filter the content of the resource manager.

Here is the implementation of the above idea encapsulated in a custom extension method:

using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.Resources;

namespace System.Windows.Forms
{
    public static partial class Extensions
    {
        public static void ApplyResources(this Control target, Func<KeyValuePair<string, object>, bool> filter, CultureInfo culture = null)
        {
            ApplyResources(new FilteringComponentResourceManager(target.GetType(), filter), target, "$this", culture);
        }

        static void ApplyResources(FilteringComponentResourceManager resourceManager, Control target, string name, CultureInfo culture = null)
        {
            // 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, resourceManager.Filter, culture);
                else
                    ApplyResources(resourceManager, child, child.Name, culture);
            }
        }

        class FilteringComponentResourceManager : ComponentResourceManager
        {
            ComponentResourceManager source;
            Func<KeyValuePair<string, object>, bool> filter;
            public FilteringComponentResourceManager(Type type, Func<KeyValuePair<string, object>, bool> filter)
            {
                this.source = new ComponentResourceManager(type);
                this.filter = filter;
            }
            public Func<KeyValuePair<string, object>, bool> Filter { get { return filter; } }
            protected override ResourceSet InternalGetResourceSet(CultureInfo culture, bool createIfNotExists, bool tryParents)
            {
                var sourceSet = source.GetResourceSet(culture, createIfNotExists, tryParents);
                return sourceSet != null ? new FilteredResourceSet(sourceSet, filter) : null;
            }
            class FilteredResourceSet : ResourceSet
            {
                public FilteredResourceSet(ResourceSet source, Func<KeyValuePair<string, object>, bool> filter)
                {
                    foreach (DictionaryEntry entry in source)
                    {
                        if (filter(new KeyValuePair<string, object>((string)entry.Key, entry.Value)))
                            Table.Add(entry.Key, entry.Value);
                    }
                }
            }
        }
    }
}

Filtering is achieved with two custom classes - one derived from ComponentResourceManager and one derived from ResourceSet. The first class overrides the InternalGetResourceSet in order to create and return an instance of the second type, which performs the actual filtering.

Sample usages:

To apply only properties named Text:

this.ApplyResources(entry => entry.Key.EndsWith(".Text"));

To apply only properties of type string:

this.ApplyResources(entry => entry.Value is string);
Ivan Stoev
  • 195,425
  • 15
  • 312
  • 343