diff --git a/src/Engine/InstanceLocator.cs b/src/Engine/InstanceLocator.cs new file mode 100644 index 00000000..c424d1b2 --- /dev/null +++ b/src/Engine/InstanceLocator.cs @@ -0,0 +1,158 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading; +using System.Windows; +using System.Windows.Threading; + +namespace WPFLocalizeExtension.Engine +{ + internal static class InstanceLocator + { + /// + /// Holds a SyncRoot to be thread safe + /// + private static readonly object SyncRoot = new object(); + + /// + /// Holds the threaded instances per type + /// + private static Dictionary> _instances = new Dictionary>(); + + /// + /// Get a singleton or per thread instance + /// + /// Type of the requested instance. Has to implement a public parameterless constructor. + /// instance of type + public static T Resolve() where T : class, new() + { + var instances = GetTypeInstances(); + return GetInstance(instances); + } + + /// + /// Get a singleton or per thread instance + /// + /// Type of the requested instance. Has to implement a public parameterless constructor. + /// instance of + public static object Resolve(Type type) + { + var instances = GetTypeInstances(type); + return GetInstance(instances, type); + } + + /// + /// Get the instance dictionary for + /// + /// instance type + /// The new / existing instance collection for the requested type + public static Dictionary GetTypeInstances() where T : class + { + var type = typeof(T); + return GetTypeInstances(type); + } + + /// + /// Get the instance dictionary for + /// + /// instance type + /// The new / existing instance collection for the requested type + public static Dictionary GetTypeInstances(Type type) + { + if (!_instances.ContainsKey(type)) + { + lock (SyncRoot) + { + _instances.Add(type, new Dictionary()); + } + } + + return _instances[type]; + } + + /// + /// Get a instance of type + /// + /// instance type + /// instances dictionary + /// The new / existing instance of type + private static T GetInstance(Dictionary instances) where T : class, new() + { + return GetInstance(instances, typeof(T)) as T; + } + + /// + /// Get a instance of + /// + /// instances dictionary + /// instance type + /// The new / existing instance of type + private static object GetInstance(Dictionary instances, Type type) + { + object result = null; + var threadId = Thread.CurrentThread.ManagedThreadId; + + // when UseThreadInstances is activated the requested type has to implement the ILocalizeInstance interface + // otherwise we will use a singleton instance + if (LocalizeSettings.Instance.UseThreadInstances && typeof(ILocalizeInstance).IsAssignableFrom(type)) + { + if (instances.ContainsKey(threadId)) + result = instances[threadId]; + } + else + result = instances.FirstOrDefault().Value; + + if (result == null) + { + lock (SyncRoot) + { + result = Activator.CreateInstance(type); + CheckShutdownEvent(Thread.CurrentThread); + instances.Add(threadId, result); + } + } + + return result; + } + + /// + /// Check if the provided thread populates a dispatcher object, subsribe to the event and dissolve instances for this thread. + /// + /// + private static void CheckShutdownEvent(Thread thread) + { + var dis = Dispatcher.FromThread(thread); + if (dis != null && !_instances.Any(x => x.Value.Any(y => y.Key == thread.ManagedThreadId))) + { + dis.ShutdownStarted += (o, e) => + { + foreach (var entry in _instances.SelectMany(x => x.Value.Where(y => y.Key == thread.ManagedThreadId)).ToList()) + Dissolve(entry.Value); + }; + } + } + + /// + /// Remove a instance from the collection + /// + /// instance + internal static void Dissolve(object obj) + { + var instances = GetTypeInstances(obj.GetType()); + if (instances.Any(x => x.Value == obj)) + { + var entry = instances.Single(x => x.Value == obj); + lock (SyncRoot) + { + instances.Remove(entry.Key); + } + } + } + } + + /// + /// A empty interface - just to indicate a class is useable for the thread / instance logic + /// + public interface ILocalizeInstance { } +} diff --git a/src/Engine/LocalizeDictionary.cs b/src/Engine/LocalizeDictionary.cs index d1565598..15edf48c 100644 --- a/src/Engine/LocalizeDictionary.cs +++ b/src/Engine/LocalizeDictionary.cs @@ -41,7 +41,7 @@ namespace WPFLocalizeExtension.Engine /// /// Represents the culture interface for localization /// - public sealed class LocalizeDictionary : DependencyObject, INotifyPropertyChanged + public sealed class LocalizeDictionary : DependencyObject, INotifyPropertyChanged, ILocalizeInstance { #region INotifyPropertyChanged Implementation /// @@ -102,7 +102,7 @@ internal void RaisePropertyChanged(string property) "Separation", typeof(string), typeof(LocalizeDictionary), - new PropertyMetadata(DefaultSeparation, SetSeparationFromDependencyProperty)); + new PropertyMetadata(LocalizeSettings.Instance.Separation, SetSeparationFromDependencyProperty)); /// /// A flag indicating that the invariant culture should be included. @@ -112,7 +112,7 @@ internal void RaisePropertyChanged(string property) "IncludeInvariantCulture", typeof(bool), typeof(LocalizeDictionary), - new PropertyMetadata(true, SetIncludeInvariantCultureFromDependencyProperty)); + new PropertyMetadata(LocalizeSettings.Instance.IncludeInvariantCulture, SetIncludeInvariantCultureFromDependencyProperty)); /// /// A flag indicating that the cache is disabled. @@ -122,7 +122,7 @@ internal void RaisePropertyChanged(string property) "DisableCache", typeof(bool), typeof(LocalizeDictionary), - new PropertyMetadata(false, SetDisableCacheFromDependencyProperty)); + new PropertyMetadata(LocalizeSettings.Instance.DisableCache, SetDisableCacheFromDependencyProperty)); /// /// A flag indicating that missing keys should be output. @@ -132,7 +132,7 @@ internal void RaisePropertyChanged(string property) "OutputMissingKeys", typeof(bool), typeof(LocalizeDictionary), - new PropertyMetadata(true, SetOutputMissingKeysFromDependencyProperty)); + new PropertyMetadata(LocalizeSettings.Instance.OutputMissingKeys, SetOutputMissingKeysFromDependencyProperty)); #endregion #region Dependency Property Callbacks @@ -419,11 +419,6 @@ public static void SetDesignCulture(DependencyObject obj, string value) /// private static readonly object SyncRoot = new object(); - /// - /// Holds the instance of singleton - /// - private static LocalizeDictionary _instance; - /// /// Holds the current chosen /// @@ -432,22 +427,22 @@ public static void SetDesignCulture(DependencyObject obj, string value) /// /// Holds the separation char/string. /// - private string _separation = DefaultSeparation; + private string _separation = LocalizeSettings.Instance.Separation; /// /// Determines, if the contains the invariant culture. /// - private bool _includeInvariantCulture = true; + private bool _includeInvariantCulture = LocalizeSettings.Instance.IncludeInvariantCulture; /// /// Determines, if the cache is disabled. /// - private bool _disableCache = true; + private bool _disableCache = LocalizeSettings.Instance.DisableCache; /// /// Determines, if missing keys should be output. /// - private bool _outputMissingKeys = true; + private bool _outputMissingKeys = LocalizeSettings.Instance.OutputMissingKeys; /// /// A default provider. @@ -457,7 +452,7 @@ public static void SetDesignCulture(DependencyObject obj, string value) /// /// Determines, if the CurrentThread culture is set along with the Culture property. /// - private bool _setCurrentThreadCulture = true; + private bool _setCurrentThreadCulture = LocalizeSettings.Instance.SetCurrentThreadCulture; /// /// Determines if the code is run in DesignMode or not. @@ -471,10 +466,10 @@ public static void SetDesignCulture(DependencyObject obj, string value) /// Prevents a default instance of the class from being created. /// Static Constructor /// - private LocalizeDictionary() + public LocalizeDictionary() { - DefaultProvider = ResxLocalizationProvider.Instance; - SetCultureCommand = new CultureInfoDelegateCommand(SetCulture); + DefaultProvider = InstanceLocator.Resolve(LocalizeSettings.Instance.LocalizationProviderType) as ILocalizationProvider; + SetCultureCommand = new CultureInfoDelegateCommand((c) => LocalizeSettings.Instance.Culture = c); } private void AvailableCulturesCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) @@ -509,9 +504,14 @@ private void AvailableCulturesCollectionChanged(object sender, NotifyCollectionC /// ~LocalizeDictionary() { - LocExtension.ClearResourceBuffer(); - FELoc.ClearResourceBuffer(); - BLoc.ClearResourceBuffer(); + // do not clear the resource buffer on multi threaded applications + if (!LocalizeSettings.Instance.UseThreadInstances) + { + LocExtension.ClearResourceBuffer(); + FELoc.ClearResourceBuffer(); + BLoc.ClearResourceBuffer(); + } + InstanceLocator.Dissolve(this); } #endregion @@ -521,11 +521,6 @@ private void AvailableCulturesCollectionChanged(object sender, NotifyCollectionC /// public static CultureInfo DefaultCultureInfo => CultureInfo.InvariantCulture; - /// - /// Gets the default separation char/string. - /// - public static string DefaultSeparation => "_"; - /// /// Gets the singleton. /// If the underlying instance is null, a instance will be created. @@ -534,26 +529,8 @@ public static LocalizeDictionary Instance { get { - // check if the underlying instance is null - if (_instance == null) - { - // if it is null, lock the syncroot. - // if another thread is accessing this too, - // it have to wait until the syncroot is released - lock (SyncRoot) - { - // check again, if the underlying instance is null - if (_instance == null) - { - // create a new instance - _instance = new LocalizeDictionary(); - } - } - } - - // return the existing/new instance - return _instance; - } + return InstanceLocator.Resolve(); + } } /// @@ -972,13 +949,21 @@ internal MissingKeyEventArgs OnNewMissingKeyEvent(object sender, string key) #endregion #region DictionaryEvent (using weak references) - internal static class DictionaryEvent + internal class DictionaryEvent : ILocalizeInstance { /// /// The list of listeners /// - private static readonly List Listeners = new List(); - private static readonly object ListenersLock = new object(); + private readonly List Listeners = new List(); + private readonly object ListenersLock = new object(); + + private static DictionaryEvent Instance + { + get + { + return InstanceLocator.Resolve(); + } + } /// /// Fire the event. @@ -988,16 +973,17 @@ internal static class DictionaryEvent internal static void Invoke(DependencyObject sender, DictionaryEventArgs args) { var list = new List(); + var instance = Instance; - lock (ListenersLock) + lock (instance.ListenersLock) { - foreach (var wr in Listeners.ToList()) + foreach (var wr in instance.Listeners.ToList()) { var targetReference = wr.Target; if (targetReference != null) list.Add((IDictionaryEventListener)targetReference); else - Listeners.Remove(wr); + instance.Listeners.Remove(wr); } } @@ -1014,23 +1000,25 @@ internal static void AddListener(IDictionaryEventListener listener) if (listener == null) return; + var instance = Instance; + // Check, if this listener already was added. bool listenerExists = false; - lock (ListenersLock) + lock (instance.ListenersLock) { - foreach (var wr in Listeners.ToList()) + foreach (var wr in instance.Listeners.ToList()) { var targetReference = wr.Target; if (targetReference == null) - Listeners.Remove(wr); + instance.Listeners.Remove(wr); else if (targetReference == listener) listenerExists = true; } // Add it now. if (!listenerExists) - Listeners.Add(new WeakReference(listener)); + instance.Listeners.Add(new WeakReference(listener)); } } @@ -1043,15 +1031,17 @@ internal static void RemoveListener(IDictionaryEventListener listener) if (listener == null) return; - lock (ListenersLock) + var instance = Instance; + + lock (instance.ListenersLock) { - foreach (var wr in Listeners.ToList()) + foreach (var wr in instance.Listeners.ToList()) { var targetReference = wr.Target; if (targetReference == null) - Listeners.Remove(wr); + instance.Listeners.Remove(wr); else if ((IDictionaryEventListener)targetReference == listener) - Listeners.Remove(wr); + instance.Listeners.Remove(wr); } } } @@ -1063,14 +1053,15 @@ internal static void RemoveListener(IDictionaryEventListener listener) /// An enumeration of listeners. internal static IEnumerable EnumerateListeners() { - lock (ListenersLock) + var instance = Instance; + lock (instance.ListenersLock) { - foreach (var wr in Listeners.ToList()) + foreach (var wr in instance.Listeners.ToList()) { var targetReference = wr.Target; if (targetReference == null) - Listeners.Remove(wr); + instance.Listeners.Remove(wr); else if (targetReference is T) yield return (T)targetReference; } @@ -1080,11 +1071,6 @@ internal static IEnumerable EnumerateListeners() #endregion #region CultureInfoDelegateCommand - private void SetCulture(CultureInfo c) - { - Culture = c; - } - /// /// A class for culture commands. /// diff --git a/src/Engine/LocalizeSettings.cs b/src/Engine/LocalizeSettings.cs new file mode 100644 index 00000000..fc06b49c --- /dev/null +++ b/src/Engine/LocalizeSettings.cs @@ -0,0 +1,485 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Text; +using System.Windows; +using WPFLocalizeExtension.Providers; + +namespace WPFLocalizeExtension.Engine +{ + /// + /// A class to hold all global settings + /// + public sealed class LocalizeSettings + { + #region Attached Dependency Properties + /// + /// A flag indicating to use singleton or per thread based instance + /// + public static readonly DependencyProperty UseThreadInstancesProperty = + DependencyProperty.RegisterAttached("UseThreadInstances", typeof(bool), typeof(LocalizeSettings), new PropertyMetadata(Settings.DefaultUseThreadInstances, UseThreadInstancesChangedCallback)); + + /// + /// to set the localization Culture + /// + public static readonly DependencyProperty CultureProperty = + DependencyProperty.RegisterAttached("Culture", typeof(CultureInfo), typeof(LocalizeSettings), new PropertyMetadata(null, CultureChangedCallback)); + + /// + /// A flag indicating that the cache is disabled. + /// + public static readonly DependencyProperty DisableCacheProperty = + DependencyProperty.RegisterAttached("DisableCache", typeof(bool), typeof(LocalizeSettings), new PropertyMetadata(Settings.DefaultDisableCache, DisableCacheChangedCallback)); + + /// + /// A flag indicating that the invariant culture should be included. + /// + public static readonly DependencyProperty IncludeInvariantCultureProperty = + DependencyProperty.RegisterAttached("IncludeInvariantCulture", typeof(bool), typeof(LocalizeSettings), new PropertyMetadata(Settings.DefaultIncludeInvariantCulture, IncludeInvariantCultureChangedCallback)); + + /// + /// A flag indicating that missing keys should be output. + /// + public static readonly DependencyProperty OutputMissingKeysProperty = + DependencyProperty.RegisterAttached("OutputMissingKeys", typeof(bool), typeof(LocalizeSettings), new PropertyMetadata(Settings.DefaultOutputMissingKeys, OutputMissingKeysChangedCallback)); + + /// + /// to set the separation character/string for resource name patterns. + /// + public static readonly DependencyProperty SeparationProperty = + DependencyProperty.RegisterAttached("Separation", typeof(string), typeof(LocalizeSettings), new PropertyMetadata(Settings.DefaultSeparation, SeparationChangedCallback)); + + /// + /// to set the default type. + /// + public static readonly DependencyProperty LocalizationProviderTypeProperty = + DependencyProperty.RegisterAttached("LocalizationProviderType", typeof(Type), typeof(LocalizeSettings), new PropertyMetadata(Settings.DefaultLocalizatinProviderType, LocalizationProviderTypeChangedCallback)); + + #endregion + + #region Attached Dependency Properties Management + /// + /// Get + /// + /// + /// + public static bool GetUseThreadInstances(DependencyObject obj) + { + return Instance.UseThreadInstances; + } + + /// + /// Set + /// + /// not used + /// + public static void SetUseThreadInstances(DependencyObject obj, bool value) + { + Instance.UseThreadInstances = value; + } + + /// + /// Get + /// + /// + /// + public static CultureInfo GetCulture(DependencyObject obj) + { + return Instance.Culture; + } + + /// + /// Set + /// + /// not used + /// + public static void SetCulture(DependencyObject obj, CultureInfo value) + { + Instance.Culture = value; + } + + /// + /// Get + /// + /// + /// + public static bool GetDisableCache(DependencyObject obj) + { + return Instance.DisableCache; + } + + /// + /// Set + /// + /// not used + /// + public static void SetDisableCache(DependencyObject obj, bool value) + { + Instance.DisableCache = value; + } + + /// + /// Get + /// + /// + /// + public static bool GetIncludeInvariantCulture(DependencyObject obj) + { + return (bool)obj.GetValue(IncludeInvariantCultureProperty); + } + + /// + /// Set + /// + /// not used + /// + public static void SetIncludeInvariantCulture(DependencyObject obj, bool value) + { + obj.SetValue(IncludeInvariantCultureProperty, value); + } + + /// + /// Get + /// + /// + /// + public static bool GetOutputMissingKeys(DependencyObject obj) + { + return Instance.OutputMissingKeys; + } + + /// + /// Set + /// + /// not used + /// + public static void SetOutputMissingKeys(DependencyObject obj, bool value) + { + Instance.OutputMissingKeys = value; + } + + /// + /// Get + /// + /// + /// + public static string GetSeparation(DependencyObject obj) + { + return Instance.Separation; + } + + /// + /// Set + /// + /// not used + /// + public static void SetSeparation(DependencyObject obj, string value) + { + Instance.Separation = value; + } + + /// + /// Get + /// + /// + /// + public static Type GetLocalizationProviderType(DependencyObject obj) + { + return Instance.LocalizationProviderType; + } + + /// + /// Set + /// + /// not used + /// + public static void SetLocalizationProviderType(DependencyObject obj, Type value) + { + Instance.LocalizationProviderType = value; + } + #endregion + + #region Attached Dependency Properties Callback + private static void UseThreadInstancesChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + if (e.NewValue is bool b) + Instance.UseThreadInstances = b; + } + + private static void CultureChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + if (e.NewValue is CultureInfo c) + Instance.Culture = c; + } + + private static void DisableCacheChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + if (e.NewValue is bool b) + Instance.DisableCache = b; + } + + private static void IncludeInvariantCultureChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + if (e.NewValue is bool b) + Instance.IncludeInvariantCulture = b; + } + + private static void OutputMissingKeysChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + if (e.NewValue is bool b) + Instance.OutputMissingKeys = b; + } + + private static void SeparationChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + if (e.NewValue is string s) + Instance.Separation = s; + } + + private static void LocalizationProviderTypeChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + if (e.NewValue is Type t) + Instance.LocalizationProviderType = t; + } + #endregion + + #region Static Properties + + private static Settings _instance; + + /// + /// Singleton settings instance + /// + public static Settings Instance + { + get + { + return _instance ?? (_instance = new Settings()); + } + } + #endregion + + #region Settings Class + /// + /// Global settings class + /// + public class Settings + { + /// + /// protected constructor + /// + internal Settings() { } + + #region Default values + /// + /// The default separation key for + /// + public const string DefaultSeparation = "_"; + + /// + /// Default value for + /// + public const bool DefaultUseThreadInstances = false; + + /// + /// Default value for + /// + public const bool DefaultDisableCache = false; + + /// + /// Default value for + /// + public const bool DefaultIncludeInvariantCulture = true; + + /// + /// Default value for + /// + public const bool DefaultOutputMissingKeys = true; + + /// + /// Default type for + /// + public static readonly Type DefaultLocalizatinProviderType = typeof(ResxLocalizationProvider); + #endregion + + #region Private Properties + private CultureInfo _culture; + private bool _disableCache = DefaultDisableCache; + private bool _includeInvariantCulture = DefaultIncludeInvariantCulture; + private bool _outputMissingKeys = DefaultOutputMissingKeys; + private string _separation = DefaultSeparation; + private Type _LocalizationProviderType = DefaultLocalizatinProviderType; + private bool _setCurrentThreadCulture = true; + + #endregion + + #region Public Properties + /// + /// A flag to determine the behaivor of this application on multithreaded applications. + /// When set to false a singleton instances will be used + /// otherwise every thread get's his own instance. + /// + public bool UseThreadInstances { get; set; } = DefaultUseThreadInstances; + + /// + /// Global culture + /// + public CultureInfo Culture + { + get + { + return _culture; + } + set + { + if(_culture != value) + { + _culture = value; + + foreach (var instance in GetDictionaries()) + instance.Dispatcher.BeginInvoke(new Action(() => instance.Culture = _culture)); + } + } + } + + /// + /// A flag to disable the caching logic + /// + public bool DisableCache + { + get + { + return _disableCache; + } + set + { + if(_disableCache != value) + { + _disableCache = value; + + foreach (var instance in GetDictionaries()) + instance.Dispatcher.BeginInvoke(new Action(() => instance.DisableCache = _disableCache)); + } + } + } + + /// + /// Determines, if the contains the invariant culture. + /// + public bool IncludeInvariantCulture + { + get + { + return _includeInvariantCulture; + } + set + { + if (_includeInvariantCulture != value) + { + _includeInvariantCulture = value; + + foreach (var instance in GetDictionaries()) + instance.Dispatcher.BeginInvoke(new Action(() => instance.IncludeInvariantCulture = _includeInvariantCulture)); + } + } + } + + /// + /// Determines, if missing keys should be output. + /// + public bool OutputMissingKeys + { + get + { + return _outputMissingKeys; + } + set + { + if(_outputMissingKeys != value) + { + _outputMissingKeys = value; + + foreach (var instance in GetDictionaries()) + instance.Dispatcher.BeginInvoke(new Action(() => instance.OutputMissingKeys = _outputMissingKeys)); + } + } + } + + /// + /// Holds the separation char/string. + /// + public string Separation + { + get + { + return _separation; + } + set + { + if(_separation != value) + { + _separation = value; + + foreach (var instance in GetDictionaries()) + instance.Dispatcher.BeginInvoke(new Action(() => instance.Separation = _separation)); + } + } + } + + /// + /// Holds the default localization provider type + /// This type has to implementd + /// + public Type LocalizationProviderType + { + get + { + return _LocalizationProviderType; + } + set + { + if (value != null) + { + if (typeof(ILocalizationProvider).IsAssignableFrom(value)) + _LocalizationProviderType = value; + else + throw new NotSupportedException($"The type {value} can't be used as {nameof(LocalizationProviderType)}, it has to implement the interface {nameof(ILocalizationProvider)}!"); + } + else + _LocalizationProviderType = DefaultLocalizatinProviderType; + } + } + + /// + /// Gets or sets a flag that determines, if the CurrentThread culture should be changed along with the Culture property. + /// + public bool SetCurrentThreadCulture + { + get + { + return _setCurrentThreadCulture; + } + set + { + if(value != _setCurrentThreadCulture) + { + _setCurrentThreadCulture = value; + + foreach (var instance in GetDictionaries()) + instance.Dispatcher.BeginInvoke(new Action(() => instance.SetCurrentThreadCulture = _setCurrentThreadCulture)); + } + } + } + #endregion + + #region Helper + private List GetDictionaries() + { + return InstanceLocator.GetTypeInstances().Select(x => x.Value as LocalizeDictionary).ToList(); + } + #endregion + } + #endregion + } +} diff --git a/src/Providers/CSVEmbeddedLocalizationProvider.cs b/src/Providers/CSVEmbeddedLocalizationProvider.cs index 0eb69fce..ad53b366 100644 --- a/src/Providers/CSVEmbeddedLocalizationProvider.cs +++ b/src/Providers/CSVEmbeddedLocalizationProvider.cs @@ -26,7 +26,7 @@ namespace WPFLocalizeExtension.Providers /// /// A singleton CSV provider that uses attached properties and the Parent property to iterate through the visual tree. /// - public class CSVEmbeddedLocalizationProvider : CSVLocalizationProviderBase + public class CSVEmbeddedLocalizationProvider : CSVLocalizationProviderBase, ILocalizeInstance { #region Dependency Properties /// @@ -116,16 +116,6 @@ public static void SetDefaultAssembly(DependencyObject obj, string value) #endregion #region Singleton Variables, Properties & Constructor - /// - /// The instance of the singleton. - /// - private static CSVEmbeddedLocalizationProvider _instance; - - /// - /// Lock object for the creation of the singleton instance. - /// - private static readonly object InstanceLock = new object(); - /// /// Gets the singleton. /// @@ -133,24 +123,14 @@ public static CSVEmbeddedLocalizationProvider Instance { get { - if (_instance == null) - { - lock (InstanceLock) - { - if (_instance == null) - _instance = new CSVEmbeddedLocalizationProvider(); - } - } - - // return the existing/new instance - return _instance; + return InstanceLocator.Resolve(); } } /// - /// The singleton constructor. + /// The instance constructor. /// - private CSVEmbeddedLocalizationProvider() + public CSVEmbeddedLocalizationProvider() { ResourceManagerList = new Dictionary(); AvailableCultures = new ObservableCollection { CultureInfo.InvariantCulture }; diff --git a/src/Providers/CSVLocalizationProvider.cs b/src/Providers/CSVLocalizationProvider.cs index 0bfce66f..a3fd2fd1 100644 --- a/src/Providers/CSVLocalizationProvider.cs +++ b/src/Providers/CSVLocalizationProvider.cs @@ -21,7 +21,7 @@ namespace WPFLocalizeExtension.Providers /// /// A singleton CSV provider that uses attached properties and the Parent property to iterate through the visual tree. /// - public class CSVLocalizationProvider : CSVLocalizationProviderBase + public class CSVLocalizationProvider : CSVLocalizationProviderBase, ILocalizeInstance { #region Dependency Properties /// @@ -81,16 +81,6 @@ public static void SetDefaultDictionary(DependencyObject obj, string value) #endregion #region Singleton Variables, Properties & Constructor - /// - /// The instance of the singleton. - /// - private static CSVLocalizationProvider _instance; - - /// - /// Lock object for the creation of the singleton instance. - /// - private static readonly object InstanceLock = new object(); - /// /// Gets the singleton. /// @@ -98,24 +88,14 @@ public static CSVLocalizationProvider Instance { get { - if (_instance == null) - { - lock (InstanceLock) - { - if (_instance == null) - _instance = new CSVLocalizationProvider(); - } - } - - // return the existing/new instance - return _instance; + return InstanceLocator.Resolve(); } } /// - /// The singleton constructor. + /// The instance constructor. /// - private CSVLocalizationProvider() + public CSVLocalizationProvider() { AvailableCultures = new ObservableCollection { CultureInfo.InvariantCulture }; } diff --git a/src/Providers/InheritingResxLocalizationProvider.cs b/src/Providers/InheritingResxLocalizationProvider.cs index cc81474c..1c8beaf7 100644 --- a/src/Providers/InheritingResxLocalizationProvider.cs +++ b/src/Providers/InheritingResxLocalizationProvider.cs @@ -14,13 +14,14 @@ namespace WPFLocalizeExtension.Providers using System.Globalization; using System.Resources; using System.Windows; + using WPFLocalizeExtension.Engine; using XAMLMarkupExtensions.Base; #endregion /// /// A singleton RESX provider that uses inheriting attached properties. /// - public class InheritingResxLocalizationProvider : ResxLocalizationProviderBase + public class InheritingResxLocalizationProvider : ResxLocalizationProviderBase, ILocalizeInstance { #region Dependency Properties /// @@ -103,16 +104,6 @@ public static void SetDefaultAssembly(DependencyObject obj, string value) #endregion #region Singleton Variables, Properties & Constructor - /// - /// The instance of the singleton. - /// - private static InheritingResxLocalizationProvider _instance; - - /// - /// Lock object for the creation of the singleton instance. - /// - private static readonly object InstanceLock = new object(); - /// /// Gets the singleton. /// @@ -120,24 +111,14 @@ public static InheritingResxLocalizationProvider Instance { get { - if (_instance == null) - { - lock (InstanceLock) - { - if (_instance == null) - _instance = new InheritingResxLocalizationProvider(); - } - } - - // return the existing/new instance - return _instance; + return InstanceLocator.Resolve(); } } /// - /// The singleton constructor. + /// The instance constructor. /// - private InheritingResxLocalizationProvider() + public InheritingResxLocalizationProvider() { ResourceManagerList = new Dictionary(); AvailableCultures = new ObservableCollection { CultureInfo.InvariantCulture }; diff --git a/src/Providers/ResxLocalizationProvider.cs b/src/Providers/ResxLocalizationProvider.cs index f482c627..1e1be4da 100644 --- a/src/Providers/ResxLocalizationProvider.cs +++ b/src/Providers/ResxLocalizationProvider.cs @@ -21,7 +21,7 @@ namespace WPFLocalizeExtension.Providers /// /// A singleton RESX provider that uses attached properties and the Parent property to iterate through the visual tree. /// - public class ResxLocalizationProvider : ResxLocalizationProviderBase + public class ResxLocalizationProvider : ResxLocalizationProviderBase, ILocalizeInstance { #region Dependency Properties /// @@ -175,16 +175,6 @@ public static void SetIgnoreCase(DependencyObject obj, bool value) #endregion #region Singleton Variables, Properties & Constructor - /// - /// The instance of the singleton. - /// - private static ResxLocalizationProvider _instance; - - /// - /// Lock object for the creation of the singleton instance. - /// - private static readonly object InstanceLock = new object(); - /// /// Gets the singleton. /// @@ -192,25 +182,7 @@ public static ResxLocalizationProvider Instance { get { - if (_instance == null) - { - lock (InstanceLock) - { - if (_instance == null) - _instance = new ResxLocalizationProvider(); - } - } - - // return the existing/new instance - return _instance; - } - - set - { - lock (InstanceLock) - { - _instance = value; - } + return InstanceLocator.Resolve(); } } @@ -219,17 +191,24 @@ public static ResxLocalizationProvider Instance /// public static void Reset() { - Instance = null; + var instance = Instance; + InstanceLocator.Dissolve(instance); + instance = null; } /// - /// The singleton constructor. + /// The instance constructor. /// - protected ResxLocalizationProvider() + public ResxLocalizationProvider() { ResourceManagerList = new Dictionary(); AvailableCultures = new ObservableCollection { CultureInfo.InvariantCulture }; } + + ~ResxLocalizationProvider() + { + InstanceLocator.Dissolve(this); + } #endregion #region Abstract assembly & dictionary lookup