/// <summary>
            /// Adds a localized value to the manager.
            /// </summary>
            /// <param name="value"></param>
            /// <exception cref="InvalidOperationException">The method is called on a thread different from the <see cref="Dispatcher"/>'s thread.</exception>
            public void Add(LocalizedSetterValue value)
            {
                if (value == null)
                {
                    throw new ArgumentNullException(nameof(value));
                }

                Dispatcher.VerifyAccess();

                _localizedValueList.AddLast(value);
            }
Example #2
0
        internal static void Add(LocalizedSetterValue value)
        {
            if (value == null)
            {
                throw new ArgumentNullException(nameof(value));
            }

            var dispatcher = value.Dispatcher;

            if (dispatcher == null)
            {
                // The dependency object has been GC
                return;
            }

            if (false == dispatcher.CheckAccess())
            {
                // COMMENT This restriction can be lifted if the DispatcherHandler.Add method becomes thread-safe
                throw new InvalidOperationException($"This method must be called on the UI thread of the {nameof(DependencyObject)} whose value is localized.");
            }

            GetOrCreateDispatcherHander(dispatcher).Add(value);
        }
Example #3
0
        public override object ProvideValue(IServiceProvider serviceProvider)
        {
            if (serviceProvider == null)
            {
                throw new ArgumentNullException(nameof(serviceProvider));
            }

            // Other useful services:
            // - IDestinationTypeProvider
            // - IRootObjectProvider

            if (!(serviceProvider.GetService(typeof(IProvideValueTarget)) is IProvideValueTarget service))
            {
                return(null);
            }

            if (service.TargetObject is DependencyObject depObj)
            {
                if (Binding != null || _bindings?.Count > 0)
                {
                    if (false == (service.TargetProperty is DependencyProperty))
                    {
                        // The what bindings are implemented in WPF provides no way to obtain the value
                        // produced by the binging. The only way is to update the property directly. Therefore,
                        // the extension cannot support bindings on non-dependency properties (same as WPF).
                        throw new NotSupportedException("Bindings are supported only on dependency properties.");
                    }
                }

                LocalizableProperty localizableProperty;

                if (service.TargetProperty is DependencyProperty depProperty)
                {
                    localizableProperty = new LocalizableDepProperty(depProperty);
                }
                else if (service.TargetProperty is PropertyInfo propertyInfo)
                {
                    localizableProperty = new LocalizableNonDepProperty(propertyInfo);
                }
                else
                {
                    throw new NotSupportedException($"The extension supports only dependency and non-dependency properties of dependency objects. Properties of type {service.TargetProperty.GetType()} are not supported.");
                }

                var options = new LocalizationOptions()
                {
                    Key                = Key,
                    StringFormat       = StringFormat,
                    Binding            = Binding,
                    Bindings           = _bindings,
                    Callback           = Callback,
                    CallbackParameter  = CallbackParameter,
                    Converter          = Converter,
                    ConverterParameter = ConverterParameter,
                };

                var localizedValue = LocalizedValue.Create(new DependencyObjectProperty(depObj, localizableProperty), options);

                LocalizationManager.Add(localizedValue);

                if (localizedValue.BindingExpression != null)
                {
                    // The value uses bindings
                    return(localizedValue.BindingExpression);
                }
                else
                {
                    // The value does not use bindings

                    if (localizedValue.IsInDesignMode)
                    {
                        // At design time VS designer does not set the parent of any control
                        // before its properties are set. For this reason the correct values
                        // of inherited attached properties cannot be obtained.
                        // Therefore, to display the correct localized value it must be updated
                        // later after the parent of the control has been set.

                        depObj.Dispatcher.BeginInvoke(
                            DispatcherPriority.ApplicationIdle,
                            new SendOrPostCallback(x => ((LocalizedValue)x).UpdateValue()),
                            localizedValue
                            );

                        return(localizableProperty.DefaultValue);
                    }
                    else
                    {
                        return(localizedValue.ProduceValue());
                    }
                }
            }
            else if (service.TargetObject is Setter)
            {
                var rootObjectProvider = (IRootObjectProvider)serviceProvider.GetService(typeof(IRootObjectProvider));

                // IRootObjectProvider is never null
                // IRootObjectProvider.RootObject is null when the style is located in a separate resource dictionary file
                // or the application's resource dictionary in App.xaml
                // Otherwise, the value can be Window or UserControl depending on the where the style is declared.
                // Therefore, it can be assumed that once root object (if any) is disposed the localized value
                // can be disposed as well.
                var rootObject = rootObjectProvider?.RootObject as DependencyObject;

                if (rootObject == null)
                {
                    // There is no way to access the resource manager (apart from the resource manager of the entry assembly).
                    // In order to avoid misunderstanding and bugs localizing setters is not supported unless they are delcared inside a Window or a UserControl
                    throw new NotSupportedException("Setters are supported only inside a Window or a UserControl. They are not supported inside a ResourceDictionary file or App.xaml.");
                }

                var options = new LocalizationOptions()
                {
                    Key                = Key,
                    StringFormat       = StringFormat,
                    Binding            = Binding,
                    Bindings           = _bindings,
                    Callback           = Callback,
                    CallbackParameter  = CallbackParameter,
                    Converter          = Converter,
                    ConverterParameter = ConverterParameter,
                };

                var localizedValueTuple = LocalizedSetterValue.Create(rootObject, options);

                LocalizationManager.Add(localizedValueTuple.Item1);

                return(localizedValueTuple.Item2.ProvideValue(serviceProvider));
            }
            else if (service.TargetProperty is DependencyProperty || service.TargetProperty is PropertyInfo)
            {
                // The extension is used in a template and will be evaluated once the template is instantiated
                return(this);
            }
            else
            {
                throw new NotSupportedException($"The localization extension can be used only with {nameof(DependencyObject)}s.");
            }
        }
        public override object ProvideValue(IServiceProvider serviceProvider)
        {
            if (serviceProvider == null)
            {
                throw new ArgumentNullException(nameof(serviceProvider));
            }

            // Other useful services:
            // - IDestinationTypeProvider
            // - IRootObjectProvider

            if (!(serviceProvider.GetService(typeof(IProvideValueTarget)) is IProvideValueTarget service))
            {
                return(null);
            }

            if (service.TargetObject is DependencyObject depObj)
            {
                if (Binding != null || _bindings?.Count > 0)
                {
                    if (false == (service.TargetProperty is DependencyProperty))
                    {
                        // The what bindings are implemented in WPF provides no way to obtain the value
                        // produced by the binging. The only way is to update the property directly. Therefore,
                        // the extension cannot support bindings on non-dependency properties (same as WPF).
                        throw new NotSupportedException("Bindings are supported only on dependency properties.");
                    }
                }

                LocalizedProperty localizedProperty;

                if (service.TargetProperty is DependencyProperty depProperty)
                {
                    localizedProperty = new LocalizedDepProperty(depProperty);
                }
                else if (service.TargetProperty is PropertyInfo propertyInfo)
                {
                    localizedProperty = new LocalizedNonDepProperty(propertyInfo);
                }
                else
                {
                    throw new NotSupportedException($"The default property provider supports only dependency and non-dependency properties of dependency objects. Properties of type {service.TargetProperty.GetType()} are not supported.");
                }

                var localizedValue = new LocalizedValue(depObj, localizedProperty)
                {
                    Key                = this.Key,
                    StringFormat       = this.StringFormat,
                    Callback           = this.Callback,
                    CallbackParameter  = this.CallbackParameter,
                    Binding            = this.Binding,
                    Bindings           = this._bindings,
                    Converter          = this.Converter,
                    ConverterParameter = this.ConverterParameter,
                };

                LocalizationManager.Add(localizedValue);

                if (localizedValue.IsInDesignMode)
                {
                    // At design time VS designer does not set the parent of any control
                    // before its properties are set. For this reason the correct values
                    // of inherited attached properties cannot be obtained.
                    // Therefore, to display the correct localized value it must be updated
                    // later after the parent of the control has been set.

                    depObj.Dispatcher.BeginInvoke(
                        DispatcherPriority.ApplicationIdle,
                        new SendOrPostCallback(x => ((LocalizedValue)x).UpdateValue()),
                        localizedValue
                        );

                    return(localizedProperty.DefaultValue);
                }
                else
                {
                    return(localizedValue.GetValue(depObj));
                }
            }
            else if (service.TargetObject is Setter)
            {
                /* // DEPRECATED
                 * if (Binding != null || _bindings?.Count > 0)
                 * {
                 *  throw new NotSupportedException("Bindings are not supported in style setters.");
                 * }
                 */

                var rootObjectProvider = (IRootObjectProvider)serviceProvider.GetService(typeof(IRootObjectProvider));

                Debug.Assert(rootObjectProvider != null);

                var rootObject = rootObjectProvider.RootObject as DependencyObject;

                Debug.Assert(rootObject != null);

#if DEPRECATED // Unfortunately "GetDestinationType" throws NullReferenceException therefore, this approach is not applicable for retrieving the property's type
                var destinationTypeProvider = (IDestinationTypeProvider)serviceProvider.GetService(typeof(IDestinationTypeProvider));

                Debug.Assert(destinationTypeProvider != null);

                var targetPropertyType = destinationTypeProvider.GetDestinationType();

                var localizedValue = new SetterLocalizedValue(rootObject, targetPropertyType)
                {
                    Key                = this.Key,
                    Callback           = this.Callback,
                    CallbackParameter  = this.CallbackParameter,
                    Converter          = this.Converter,
                    ConverterParameter = this.ConverterParameter,
                };
#endif

                var localizedValue = new LocalizedSetterValue(rootObject)
                {
                    Key                = this.Key,
                    StringFormat       = this.StringFormat,
                    Callback           = this.Callback,
                    CallbackParameter  = this.CallbackParameter,
                    Binding            = this.Binding,
                    Bindings           = this._bindings,
                    Converter          = this.Converter,
                    ConverterParameter = this.ConverterParameter,
                };

                BindingBase finalBinding;

                if (Binding != null || _bindings?.Count > 0)
                {
                    var binding = new MultiBinding()
                    {
                        Mode      = BindingMode.OneWay,
                        Converter = localizedValue,
                    };
                    if (Binding != null)
                    {
                        binding.Bindings.Add(Binding);
                        if (_bindings?.Count > 0)
                        {
                            foreach (var item in Bindings)
                            {
                                binding.Bindings.Add(item);
                            }
                        }
                    }
                    finalBinding = binding;
                }
                else
                {
                    finalBinding = new Binding(nameof(LocalizedSetterValue.Value))
                    {
                        Source    = localizedValue,
                        Mode      = BindingMode.OneWay,
                        Converter = localizedValue,
                    };
                }

                LocalizationManager.Add(localizedValue);

                return(finalBinding);
            }
            else if (service.TargetProperty is DependencyProperty || service.TargetProperty is PropertyInfo)
            {
                // The extension is used in a template and will be evaluated once the template is instantiated
                return(this);
            }
            else
            {
                throw new NotSupportedException($"The localization extension can be used only with {nameof(DependencyObject)}s.");
            }
        }
        /// <summary>
        /// Creates a new localized value.
        /// </summary>
        /// <returns></returns>
        public static Tuple <LocalizedSetterValue, BindingBase> Create(
            DependencyObject rootObject,
            LocalizationOptions options
            )
        {
            if (options == null)
            {
                throw new ArgumentNullException(nameof(options));
            }

            var localizedValue = new LocalizedSetterValue(rootObject)
            {
                Key                = options.Key,
                StringFormat       = options.StringFormat,
                Callback           = options.Callback,
                CallbackParameter  = options.CallbackParameter,
                Converter          = options.Converter,
                ConverterParameter = options.ConverterParameter,
            };

            BindingBase finalBinding;

            if (options.Binding != null || options.Bindings?.Count > 0)
            {
                // Create a binding
                var localizedBinding = new MultiBinding()
                {
                    Mode = BindingMode.OneWay,
                };
                if (options.Binding != null)
                {
                    localizedBinding.Bindings.Add(options.Binding);
                }
                if (options.Bindings?.Count > 0)
                {
                    foreach (var item in options.Bindings)
                    {
                        localizedBinding.Bindings.Add(item);
                    }
                }

                // Add a binding that can be used to update the value
                localizedBinding.Bindings.Add(new Binding()
                {
                    Mode   = BindingMode.OneWay,
                    Source = localizedValue,
                    Path   = new PropertyPath(nameof(Value)),
                });

                localizedBinding.Converter = localizedValue;

                finalBinding = localizedBinding;
            }
            else
            {
                // Create a binding
                var localizedBinding = new Binding()
                {
                    Mode   = BindingMode.OneWay,
                    Source = localizedValue,
                    Path   = new PropertyPath(nameof(Value)),
                };

                localizedBinding.Converter = localizedValue;

                finalBinding = localizedBinding;
            }

            return(Tuple.Create(localizedValue, finalBinding));
        }