/// <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); }
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); }
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)); }