WPF运行时区域设置更改,重新评估ValueConverters UI

在大型WPF应用程序中,我们可以在运行时更改语言。 我们使用WPF Localize Extension和resx文件进行本地化,除了UI中使用的转换器外,它工作得很好。 如果在绑定中ValueConverter是特定于文化的,则不会在语言更改时更新生成的文本。

如何让WPF在应用程序范围内更新所有转换后的绑定?

编辑:目前我们已经通过制作ValueConverters MultiValueConverters并将区域设置添加为额外值进行了实验。 这样,值源值会更改,并且结果会更新。 但这很麻烦而且很难看。

     

相关: 绑定中的运行时文化更改和IValueConverter (我没有手动为每个字段引发propertychanged的选项)

作为一个选项 – 您可以围绕Binding创建包装器标记扩展,如下所示:

 public class LocBindingExtension : MarkupExtension { public BindingBase Binding { get; set; } public override object ProvideValue(IServiceProvider serviceProvider) { if (Binding == null) return null; // Binding is by itself MarkupExtension // Call its ProvideValue var expression = Binding.ProvideValue(serviceProvider) as BindingExpressionBase; if (expression != null) { // if got expression - create weak reference // you don't want for this to leak memory by preventing binding from GC var wr = new WeakReference(expression); PropertyChangedEventHandler handler = null; handler = (o, e) => { if (e.PropertyName == nameof(LocalizeDictionary.Instance.Culture)) { BindingExpressionBase target; // when culture changed and our binding expression is still alive - update target if (wr.TryGetTarget(out target)) target.UpdateTarget(); else // if dead - unsubsribe LocalizeDictionary.Instance.PropertyChanged -= handler; } }; LocalizeDictionary.Instance.PropertyChanged += handler; return expression; } // return self if there is no binding target (if we use extension inside a template for example) return this; } } 

使用这样:

  

您可以提供任何绑定(包括MultiBinding )并使用可以应用绑定的任何属性。

如果您认为即使这样过于冗长 – 您也可以通过镜像标记扩展所需的Binding类的所有属性并将它们转发到底层绑定来以不同的方式包装绑定。 在这种情况下,您将需要编写更多代码,并且您需要为Binding和MultiBinding创建单独的类(如果您还需要MultiBinding )。 最好的方法是inheritanceBinding并覆盖它的ProvideValue ,但它不是虚拟的,因此不可能这样做,而且我没有找到任何其他方法可以覆盖以实现结果。 这是一个只有2个绑定属性的草图:

 public class LocBindingExtension : MarkupExtension { private readonly Binding _inner; public LocBindingExtension() { _inner = new Binding(); } public LocBindingExtension(PropertyPath path) { _inner = new Binding(); this.Path = path; } public IValueConverter Converter { get { return _inner.Converter; } set { _inner.Converter = value; } } public PropertyPath Path { get { return _inner.Path; } set { _inner.Path = value; } } public override object ProvideValue(IServiceProvider serviceProvider) { // Binding is by itself MarkupExtension // Call its ProvideValue var expression = _inner.ProvideValue(serviceProvider) as BindingExpressionBase; if (expression != null) { // if got expression - create weak reference // you don't want for this to leak memory by preventing binding from GC var wr = new WeakReference(expression); PropertyChangedEventHandler handler = null; handler = (o, e) => { if (e.PropertyName == nameof(LocalizeDictionary.Instance.Culture)) { BindingExpressionBase target; // when culture changed and our binding expression is still alive - update target if (wr.TryGetTarget(out target)) target.UpdateTarget(); else // if dead - unsubsribe LocalizeDictionary.Instance.PropertyChanged -= handler; } }; LocalizeDictionary.Instance.PropertyChanged += handler; return expression; } // return self if there is no binding target (if we use extension inside a template for example) return this; } } 

然后使用简化为:

  

您可以根据需要添加更多属性(如Mode等)。

这是我们的解决方案。 我希望我理解你想要改变的问题,例如DateTime

Converter是一个简单的IValueConverter ,它将值转换为当前语言。 Translator是一个静态类,它将(例如) CurrentLanguageen-en / de-de )保存为string

如果语言已更改,则需要Behavior来更新Bindings。 我们在孔程序中只需要这个实现3-4次,因为它只用于DateTime格式化。 所有其他文本都保存在动态资源中..

但我认为根据您的需要, Behavior是正确的。

变流器

 public class CultureConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { if (value != null) { DateTime dateTime; if(DateTime.TryParse(value.ToString(), out dateTime)) { if(parameter != null) { return dateTime.ToString(parameter.ToString(), new CultureInfo(Translator.CurrentLanguage)); } return dateTime.ToString(new CultureInfo(Translator.CurrentLanguage)); } return null; } return null; } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { return null; } } 

行为

 public class CultureConverter : Behavior { private FrameworkElement _HostingControl; private DependencyProperty _HostingControlDependencyProperty; protected override void OnAttached() { base.OnAttached(); _HostingControl = AssociatedObject; _InitHostingControl(); Translator.LanguageChanged += Translator_LanguageChanged; } protected override void OnDetaching() { Translator.LanguageChanged -= Translator_LanguageChanged; base.OnDetaching(); } private void Translator_LanguageChanged(string languageCode) { if(_HostingControlDependencyProperty != null) _HostingControl.GetBindingExpression(_HostingControlDependencyProperty).UpdateTarget(); } private void _InitHostingControl() { if(_HostingControl is TextBlock) { _HostingControlDependencyProperty = TextBlock.TextProperty; } else if (typeof(TextBox) == _HostingControl.GetType()) _HostingControlDependencyProperty = TextBox.TextProperty; } 

XAML

             

预习

预习