Tip
To see when to use the ResXResourceReader, ResXResourceWriter, ResXResourceSet,
ResXResourceManager, HybridResourceManager and DynamicResourceManager
classes see the documentation of the KGySoft.Resources namespace.
[SerializableAttribute]
public class DynamicResourceManager : HybridResourceManager
<SerializableAttribute>
Public Class DynamicResourceManager
Inherits HybridResourceManager
[SerializableAttribute]
public ref class DynamicResourceManager : public HybridResourceManager
[<SerializableAttribute>]
type DynamicResourceManager =
class
inherit HybridResourceManager
end
DynamicResourceManager class is derived from HybridResourceManager and adds the functionality of automatic appending the resources with the non-translated and/or unknown entries as well as auto-saving the changes. This makes possible to automatically create the .resx files if the language of the application is changed to a language, which has no translation yet. See also the static LanguageSettings class. The strategy of auto appending and saving can be chosen by the AutoAppend and AutoSave properties (see AutoAppendOptions and AutoSaveOptions enumerations).
DynamicResourceManager combines the functionality of ResourceManager, ResXResourceManager, HybridResourceManager and extends these with the feature of auto expansion. It can be an ideal choice to use it as a resource manager of an application or a class library because it gives you freedom (or to the consumer of your library) to choose the strategy. If AutoAppend and AutoSave functionalities are completely disabled, then it is equivalent to a HybridResourceManager, which can handle resources both from compiled and XML sources but you must explicitly add new content and save it (see the example of the HybridResourceManager base class). If you restrict even the source of the resources, then you can get the functionality of the ResXResourceManager class (Source is ResXOnly), or the ResourceManager class (Source is CompiledOnly).
The following section highlights the additional features compared to HybridResourceManager:
Centralized vs. individual settings:
The behavior of DynamicResourceManager instances can be controlled in two ways, which can be configured by the UseLanguageSettings property.
Auto Appending:
The automatic expansion of the resources can be controlled by the AutoAppend property (or by LanguageSettings.DynamicResourceManagersAutoAppend,
property, if UseLanguageSettings is ), and it covers three different strategies, which can be combined:
using System;
using KGySoft.Resources;
class Example
{
static void Main(string[] args)
{
var manager = new DynamicResourceManager(typeof(Example));
manager.AutoAppend = AutoAppendOptions.AddUnknownToInvariantCulture;
// Without the option above the following line would throw a MissingManifestResourceException
Console.WriteLine(manager.GetString("UnknownString")); // prints [U]UnknownString
Console.WriteLine(manager.GetObject("UnknownObject")); // prints empty line
manager.SaveAllResources(compatibleFormat: false);
}
}
<?xml version="1.0"?>
<root>
<data name="UnknownString">
<value>[U]UnknownString</value>
</data>
<assembly alias="KGySoft.CoreLibraries" name="KGySoft.CoreLibraries, Version=3.6.3.1, Culture=neutral, PublicKeyToken=b45eba277439ddfe" />
<data name="UnknownObject" type="KGySoft.Resources.ResXNullRef, KGySoft.Libraries">
<value />
</data>
</root>
using System;
using System.Globalization;
using KGySoft;
using KGySoft.Resources;
class Example
{
private static CultureInfo enUS = CultureInfo.GetCultureInfo("en-US");
private static CultureInfo en = enUS.Parent;
private static CultureInfo inv = en.Parent;
static void Main(string[] args)
{
var manager = new DynamicResourceManager(typeof(Example));
// this will cause to copy the non-existing entries from invariant to "en"
manager.AutoAppend = AutoAppendOptions.AppendFirstNeutralCulture;
// preparation
manager.SetObject("TestString", "Test string in invariant culture", inv);
manager.SetObject("TestString", "Test string in English culture", en);
manager.SetObject("TestString2", "Another test string in invariant culture", inv);
manager.SetObject("TestObject", 42, inv);
manager.SetObject("DontCareObject", new byte[0], inv);
// setting the UI culture so we do not need to specify the culture in GetString/Object
LanguageSettings.DisplayLanguage = enUS;
Console.WriteLine(manager.GetString("TestString")); // already exists in en
Console.WriteLine(manager.GetString("TestString2")); // copied to en with prefix
Console.WriteLine(manager.GetObject("TestObject")); // copied to en
// Console.WriteLine(manager.GetObject("DontCareObject")); // not copied because not accessed
// saving the changes
manager.SaveAllResources(compatibleFormat: false);
}
}
<?xml version="1.0"?>
<root>
<data name="TestString">
<value>Test string in English culture</value>
</data>
<data name="TestString2">
<value>[T]Another test string in invariant culture</value>
</data>
<data name="TestObject" type="System.Int32">
<value>42</value>
</data>
</root>
using System;
using System.Globalization;
using System.Resources;
using KGySoft;
using KGySoft.Resources;
// we can tell what the language of the invariant resource is
[assembly: NeutralResourcesLanguage("en")]
class Example
{
static void Main(string[] args)
{
var manager = new DynamicResourceManager(typeof(Example));
// actually this is the default option:
manager.AutoAppend = AutoAppendOptions.AppendFirstNeutralCulture | AutoAppendOptions.AppendOnLoad;
// we prepare only the invariant resource
manager.SetObject("TestString", "Test string", CultureInfo.InvariantCulture);
manager.SetObject("TestString2", "Another test string", CultureInfo.InvariantCulture);
manager.SetObject("TestObject", 42, CultureInfo.InvariantCulture);
manager.SetObject("AnotherObject", new byte[0], CultureInfo.InvariantCulture);
// Getting an English resource will not create the en.resx file because this is
// the default language of our application thanks to the NeutralResourcesLanguage attribute
LanguageSettings.DisplayLanguage = CultureInfo.GetCultureInfo("en-US");
Console.WriteLine(manager.GetString("TestString")); // Displays "Test string", no resource is created
LanguageSettings.DisplayLanguage = CultureInfo.GetCultureInfo("fr-FR");
Console.WriteLine(manager.GetString("TestString")); // Displays "[T]Test string", resource "fr" is created
// saving the changes
manager.SaveAllResources(compatibleFormat: false);
}
}
<?xml version="1.0"?>
<root>
<data name="TestString">
<value>[T]Test string</value>
</data>
<data name="TestString2">
<value>[T]Another test string</value>
</data>
<data name="TestObject" type="System.Int32">
<value>42</value>
</data>
<data name="AnotherObject" type="System.Byte[]">
<value />
</data>
</root>
Auto Saving:
By setting the AutoSave property (or LanguageSettings.DynamicResourceManagersAutoSave,
if UseLanguageSettings is ), the DynamicResourceManager is able to save the dynamically created content automatically on specific events:
using System.Globalization;
using KGySoft.Resources;
class Example
{
static void Main(string[] args)
{
// thanks to the AutoSave = Dispose the Example.resx will be created at the end of the using block
using (var manager = new DynamicResourceManager(typeof(Example)) { AutoSave = AutoSaveOptions.Dispose })
{
manager.SetObject("Test string", "Test value", CultureInfo.InvariantCulture);
}
}
}
Considering SaveAllResources is indirectly called on auto save, you cannot set its parameters directly. However, by setting the CompatibleFormat property, you can tell whether the result .resx files should be able to be read by a System.Resources.ResXResourceReader instance and the Visual Studio Resource Editor. If it is the result .resx files are often shorter, and the values can be deserialized with better accuracy (see the remarks at ResXResourceWriter), but the result can be read only by the ResXResourceReader class.
Normally, if you save the changes by the SaveAllResources method, you can handle the possible exceptions locally. To handle errors occurred during auto save you can subscribe the AutoSaveError event.
A class library can be used by any consumers who want to use the features of that library. If it contains resources it can be useful if we allow the consumer of our class library to create translations for it in any language. In an application it can be the decision of the consumer whether generating new XML resource (.resx) files should be allowed or not. If so, we must be prepared for invalid files or malicious content (for example, the .resx file can contain serialized data of any type, whose constructor can run any code when deserialized). The following example takes all of these aspects into consideration.
using System;
using KGySoft;
using KGySoft.Resources;
namespace ClassLibrary1
{
internal static class Res
{
// internal resources for errors
private const string unavailableResource = "Resource ID not found: {0}";
private const string invalidResource = "Resource text is not valid for {0} arguments: {1}";
private static readonly DynamicResourceManager resourceManager =
// the name of the compiled resources must match (see also point 5)
new DynamicResourceManager("ClassLibrary1.Messages", typeof(Res).Assembly)
{
SafeMode = true,
UseLanguageSettings = true,
ThrowException = false,
// CompatibleFormat = true // use this if you want to edit the result files with VS resource editor
};
// Here will be the properties for the resources. This one is private because used from this class.
private static string NullReference => Get(nameof(NullReference));
// [...] Your resources can be added here (see point 8. and 9.)
private static string Get(string id) =>
resourceManager.GetString(id, LanguageSettings.DisplayLanguage) ?? String.Format(unavailableResource, id);
private static string Get(string id, params object[] args)
{
string format = Get(id);
return args == null || args.Length == 0 ? format : SafeFormat(format, args);
}
private static string SafeFormat(string format, object[] args)
{
try
{
int i = Array.IndexOf(args, null);
if (i >= 0)
{
string nullRef = Get(NullReference);
for (; i < args.Length; i++)
{
if (args[i] == null)
args[i] = nullRef;
}
}
return String.Format(LanguageSettings.FormattingLanguage, format, args);
}
catch (FormatException)
{
return String.Format(invalidResource, args.Length, format);
}
}
}
}
<!-- .NET Framework project: -->
<EmbeddedResource Include="Resource1.resx" >
<LogicalName>ClassLibrary1.Messages.resources</LogicalName>
</EmbeddedResource>
<!-- .NET Core project (note "Update" in place of "Include"): -->
<EmbeddedResource Update="Resource1.resx" >
<LogicalName>ClassLibrary1.Messages.resources</LogicalName>
</EmbeddedResource>
// simple resource: can be a property
internal static string MyResourceExample => Get(nameof(MyResourceExample));
// resource format string with placeholders: can be a method
internal static string MyResourceFormatExample(int arg1, string arg2) => Get(nameof(MyResourceFormatExample), arg1, arg2);
using System;
namespace ClassLibrary1
{
public class Example
{
public void SomeMethod(int someParameter)
{
if (someParameter == 42)
// simple resource
throw new InvalidOperationException(Res.MyResourceExample);
if (someParameter == -42)
// formatted resource - enough arguments must be specified for placeholders (though errors are handled in Res)
throw new Throw.ArgumentException(Res.MyResourceFormatExample(123, "x"), nameof(someParameter));
}
}
}
// this informs the resource manager about the language of the default culture
[assembly:NeutralResourcesLanguage("en")]
using System;
using System.Globalization;
using ClassLibrary1;
using KGySoft;
using KGySoft.Resources;
namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
// Enabling dynamic .resx creation for all DynamicResourceManager instances in this application,
// which are configured to use centralized settings
LanguageSettings.DynamicResourceManagersSource = ResourceManagerSources.CompiledAndResX;
// Setting the language of the application
LanguageSettings.DisplayLanguage = CultureInfo.GetCultureInfo("fr-FR");
LaunchMyApplication();
}
private static void LaunchMyApplication()
{
// if we use anything that has a reference to the Res class in ClassLibrary1, then
// a .resx file with the language of the application will be used or created if not exists yet.
var example = new Example();
try
{
example.SomeMethod(42);
}
catch (Exception e)
{
Console.WriteLine($"{e.GetType().Name}: {e.Message}");
}
}
}
}
<?xml version="1.0"?>
<root>
<data name="NullReference">
<value>[T]<null></value>
</data>
<data name="MyResourceExample">
<value>[T]This is a resource value will be used in my library</value>
</data>
<data name="MyResourceFormatExample">
<value>[T]This is a resource format string with two placeholders: {0}, {1}</value>
</data>
</root>
DynamicResourceManager(Type, String) | Creates a new instance of DynamicResourceManager class that looks up resources in compiled assemblies and resource XML files based on information from the specified Type object. |
DynamicResourceManager(String, Assembly, String) | Creates a new instance of DynamicResourceManager class that looks up resources in compiled assemblies and resource XML files based on information from the specified baseName and assembly. |
AutoAppend |
When UseLanguageSettings is ,
gets or sets the resource auto append options.
When UseLanguageSettings is ,
auto appending of resources is controlled by LanguageSettings.DynamicResourceManagersAutoAppend property.
Default value: AppendFirstNeutralCulture, AppendOnLoad |
AutoSave |
When UseLanguageSettings is ,
gets or sets the auto saving options.
When UseLanguageSettings is ,
auto saving is controlled by LanguageSettings.DynamicResourceManagersAutoSave property.
Default value: None |
CloneValues |
Gets or sets whether GetObject and GetMetaObject methods return always a new copy of the stored values.
Default value: . (Inherited from HybridResourceManager) |
CompatibleFormat |
Gets or sets whether the .resx files should use a compatible format when the resources are automatically saved.
Default value: |
IgnoreCase |
Gets or sets a value that indicates whether the resource manager allows case-insensitive resource lookups in the
GetString/GetMetaString
and GetObject/GetMetaObject methods.
Default value: . (Inherited from HybridResourceManager) |
IgnoreResXParseErrors |
Gets or sets whether .resx file errors should be ignored when attempting to load a resource set. If ,
then non-loadable resource sets are considered as missing ones; otherwise, an exception is thrown.
Default value: . (Inherited from HybridResourceManager) |
IsDisposed |
Gets whether this HybridResourceManager instance is disposed.
(Inherited from HybridResourceManager) |
IsModified |
Gets whether this HybridResourceManager instance has modified and unsaved data.
(Inherited from HybridResourceManager) |
NeutralResourcesCulture |
Gets the CultureInfo that is specified as neutral culture in the Assembly
used to initialized this instance, or the CultureInfo.InvariantCulture if no such culture is defined.
(Inherited from HybridResourceManager) |
ResXResourcesDir |
Gets or sets the relative path to .resx resource files.
Default value: Resources (Inherited from HybridResourceManager) |
SafeMode |
Gets or sets whether the HybridResourceManager works in safe mode. In safe mode the retrieved
objects returned from .resx sources are not deserialized automatically.
Default value: . (Inherited from HybridResourceManager) |
Source |
When UseLanguageSettings is ,
gets or sets the source, from which the resources should be taken.
When UseLanguageSettings is ,
the source is controlled by LanguageSettings.DynamicResourceManagersSource property.
Default value: CompiledAndResX (Overrides HybridResourceManagerSource) |
ThrowException |
Gets or sets whether a MissingManifestResourceException should be thrown when a resource
.resx file or compiled manifest is not found even for the neutral culture.
Default value: . (Inherited from HybridResourceManager) |
UseLanguageSettings |
Gets or sets whether values of AutoAppend, AutoSave and Source properties
are taken centrally from the LanguageSettings class.
Default value: . |
Dispose |
Disposes the resources of the current instance.
(Inherited from HybridResourceManager) |
Dispose(Boolean) |
Releases the resources used by this DynamicResourceManager instance.
(Overrides HybridResourceManagerDispose(Boolean)) |
EnsureInvariantResourcesMerged | Ensures that all invariant resource entries are merged for the specified culture. |
EnsureResourcesGenerated | Ensures that the resource sets are generated for the specified culture respecting the merging rules specified by the AutoAppend parameter. |
GetExpandoResourceSet |
Retrieves the resource set for a particular culture, which can be dynamically modified.
(Overrides HybridResourceManagerGetExpandoResourceSet(CultureInfo, ResourceSetRetrieval, Boolean)) |
GetMetaObject |
Returns the value of the specified non-string metadata for the specified culture.
(Inherited from HybridResourceManager) |
GetMetaStream |
Returns a MemoryStream instance from the metadata of the specified name and culture.
(Inherited from HybridResourceManager) |
GetMetaString |
Returns the value of the string metadata for the specified culture.
(Inherited from HybridResourceManager) |
GetObject(String) |
Returns the value of the specified resource.
(Overrides HybridResourceManagerGetObject(String)) |
GetObject(String, CultureInfo) |
Gets the value of the specified resource localized for the specified culture.
(Overrides HybridResourceManagerGetObject(String, CultureInfo)) |
GetResourceSet |
Retrieves the resource set for a particular culture.
(Overrides HybridResourceManagerGetResourceSet(CultureInfo, Boolean, Boolean)) |
GetStream(String) |
Returns a MemoryStream instance from the resource of the specified name.
(Overrides HybridResourceManagerGetStream(String)) |
GetStream(String, CultureInfo) |
Returns a MemoryStream instance from the resource of the specified name and culture.
(Overrides HybridResourceManagerGetStream(String, CultureInfo)) |
GetString(String) |
Returns the value of the specified string resource.
(Overrides HybridResourceManagerGetString(String)) |
GetString(String, CultureInfo) |
Returns the value of the string resource localized for the specified culture.
(Overrides HybridResourceManagerGetString(String, CultureInfo)) |
InternalGetResourceSet |
Provides the implementation for finding a resource set.
(Overrides HybridResourceManagerInternalGetResourceSet(CultureInfo, Boolean, Boolean)) |
ReleaseAllResources |
Tells the resource manager to call the ResourceSet.Close method on all ResourceSet objects and release all resources.
All unsaved resources will be lost.
(Overrides HybridResourceManagerReleaseAllResources) |
RemoveMetaObject |
Removes a metadata object from the current HybridResourceManager with the specified
name for the specified culture.
(Inherited from HybridResourceManager) |
RemoveObject |
Removes a resource object from the current DynamicResourceManager with the specified
name for the specified culture.
(Overrides HybridResourceManagerRemoveObject(String, CultureInfo)) |
SaveAllResources |
Saves all already loaded resources.
(Inherited from HybridResourceManager) |
SaveResourceSet |
Saves the resource set of a particular culture if it has been already loaded.
(Inherited from HybridResourceManager) |
SetMetaObject |
Adds or replaces a metadata object in the current HybridResourceManager with the specified
name for the specified culture.
(Inherited from HybridResourceManager) |
SetObject |
Adds or replaces a resource object in the current DynamicResourceManager with the specified
name for the specified culture.
(Overrides HybridResourceManagerSetObject(String, Object, CultureInfo)) |
AutoSaveError | Occurs when an exception is thrown on auto saving. If this event is not subscribed, the following exception types are automatically suppressed, as they can occur on save: IOException, SecurityException, UnauthorizedAccessException. If such an exception is suppressed some resources might remain unsaved. Though the event is a static one, the sender of the handler is the corresponding DynamicResourceManager instance. Thus the save failures of the non public DynamicResourceManager instances (e.g. resource managers of an assembly) can be tracked, too. |
Convert |
Converts an Object specified in the obj parameter to the desired targetType.
See the Examples section of the generic ConvertTTarget(Object, CultureInfo) overload for an example. (Defined by ObjectExtensions) |
ConvertTTarget |
Converts an Object specified in the obj parameter to the desired TTarget.
(Defined by ObjectExtensions) |
In |
Gets whether item is among the elements of set.
See the Examples section of the generic InT(T, T) overload for an example. (Defined by ObjectExtensions) |
TryConvert |
Tries to convert an Object specified in the obj parameter to the desired targetType.
See the Examples section of the ConvertTTarget(Object, CultureInfo) method for a related example. (Defined by ObjectExtensions) |
TryConvert |
Tries to convert an Object specified in the obj parameter to the desired targetType.
See the Examples section of the ConvertTTarget(Object, CultureInfo) method for a related example. (Defined by ObjectExtensions) |
TryConvertTTarget |
Tries to convert an Object specified in the obj parameter to the desired TTarget.
See the Examples section of the ConvertTTarget(Object, CultureInfo) method for a related example. (Defined by ObjectExtensions) |
TryConvertTTarget |
Tries to convert an Object specified in the obj parameter to the desired TTarget.
See the Examples section of the ConvertTTarget(Object, CultureInfo) method for a related example. (Defined by ObjectExtensions) |