很遗憾,没有现成的简单解决方案可以适用于所有情况。
该决定取决于您认为对其实施有效的内容以及您如何实施绑定。
- 假设您拥有到 Window 数据上下文的所有绑定。
而实际上你需要在所有 Windows 上调用 DataContext 视图的渲染。
然后你就可以在 Code Behind App 中使用这个方法了:
public static async void RerenderAllDataContext()
{
var windows = Current.Windows.OfType<Window>().ToList();
var dataContextes = windows.ToDictionary(w => w, w => w.DataContext);
var dispatcher = Current.Dispatcher;
await dispatcher.InvokeAsync(() => windows.ForEach(w => w.DataContext = null));
await dispatcher.InvokeAsync(() => windows.ForEach(w => w.DataContext = dataContextes[w]));
}
但是这种方法有它的缺点:它重绘所有窗口,UI元素的状态被重置(例如,光标在TextBox或SelectedItem中的位置),可能有没有绑定到Data Context等。
- 如果您需要考虑文化的绑定并不多,或者它们不仅是针对数据上下文创建的,那么剩下的就是调用所有此类绑定的重绘。
通过使用 MiltiBinding 而不是 Binding,只需进行少量更改即可完成此操作,其中一个绑定(通常是最后一个)将使用当前区域性的属性。
对于这样的MiltiBinding,转换器返回接收到的值数组的第一个值(如果有两个)。
为简单起见,您可以从 MiltiBinding 派生一个类,以使其应用程序尽可能接近常规 Binding。
以另一种方式补充答案:
- 您可以使用 FrameworkElement.Language 属性来设置区域性。
这是一个依赖属性,因此您可以绑定它。
属性值由子级继承(类似于 DataContext)。
如果将其设置为 Window,则其中的所有元素也将采用相同的值。
或者,您可以将其设置为某个特定元素。
可以声明其他类型以减少 XAML 代码。
示例。
全局设置文化的静态类:
using System;
using System.Windows.Markup;
namespace Wpf.Data
{
public static class LanguageAware
{
public static XmlLanguage CurrentLanguage { get; private set; } = XmlLanguage.Empty;
public static event EventHandler CurrentLanguageChanged;
public static void SetCurrentLanguage(XmlLanguage currentLanguage)
{
if (currentLanguage == null)
{
currentLanguage = XmlLanguage.Empty;
}
if (!Equals(CurrentLanguage, currentLanguage))
{
CurrentLanguage = currentLanguage;
CurrentLanguageChanged?.Invoke(null, EventArgs.Empty);
}
}
}
}
其使用示例:
<Window.Resources>
<sys:DateTime x:Key="date">12.31.2021 15:47</sys:DateTime>
</Window.Resources>
<UniformGrid Columns="1">
<TextBlock Text="{Binding Path=(wpfdata:LanguageAware.CurrentLanguage)}" />
<TextBox Text="{Binding Mode=OneWay, Source={StaticResource date}, StringFormat=\{0:F\}}"
Language="{Binding Path=(wpfdata:LanguageAware.CurrentLanguage)}"/>
<TextBox Text="{Binding Mode=OneWay, Source={StaticResource date}, StringFormat=\{0:F\}}"/>
<Button Content="Russian" Click="OnCultureClick" CommandParameter="RU" Margin="10"/>
<Button Content="English-USA" Click="OnCultureClick" CommandParameter="En-Us" Margin="10"/>
</UniformGrid>
<x:Code><![CDATA[
private void OnCultureClick(object sender, RoutedEventArgs e)
{
string lang = ((Button)sender).CommandParameter as string;
if (lang == null)
LanguageAware.SetCurrentLanguage(null);
else
LanguageAware.SetCurrentLanguage(XmlLanguage.GetLanguage(lang));
}]]>
</x:Code>
用命令替换 XAML 中的点击器的标记扩展:
using System;
using System.Globalization;
using System.Windows;
using System.Windows.Input;
using System.Windows.Markup;
using System.Windows.Threading;
namespace Wpf.Data
{
[MarkupExtensionReturnType(typeof(ICommand))]
public class LanguageCommandExtension : MarkupExtension
{
private static readonly LanguageCommand command = new LanguageCommand();
public void RaiseCanExecuteChanged() => command.RaiseCanExecuteChanged();
public override object ProvideValue(IServiceProvider serviceProvider)
{
return command;
}
private class LanguageCommand : ICommand
{
private readonly EventHandler requerySuggested;
/// <inheritdoc cref="ICommand.CanExecuteChanged"/>
public event EventHandler CanExecuteChanged;
private static readonly Dispatcher dispatcher = Application.Current.Dispatcher;
/// <summary> The method that raises the event <see cref="CanExecuteChanged"/>.</summary>
public void RaiseCanExecuteChanged()
{
if (dispatcher.CheckAccess())
{
invalidate();
}
else
{
_ = dispatcher.BeginInvoke(invalidate);
}
}
private readonly Action invalidate;
public LanguageCommand()
{
invalidate = () => CanExecuteChanged?.Invoke(this, EventArgs.Empty);
requerySuggested = (o, e) => CanExecuteChanged?.Invoke(this, EventArgs.Empty);
CommandManager.RequerySuggested += requerySuggested;
}
/// <inheritdoc cref="ICommand.CanExecute(object)"/>
public bool CanExecute(object parameter)
{
if (parameter == null || parameter is XmlLanguage)
{
return true;
}
try
{
if (parameter is string str)
{
return XmlLanguage.GetLanguage(str) != null;
}
if (parameter is CultureInfo culture)
{
str = culture.Name;
return XmlLanguage.GetLanguage(str) != null;
}
}
catch (Exception)
{ }
return false;
}
/// <inheritdoc cref="ICommand.Execute(object)"/>
public void Execute(object parameter)
{
if (parameter is XmlLanguage language)
{ }
else if (parameter == null)
{
language = null;
}
else
{
try
{
if (parameter is string str)
{
language = XmlLanguage.GetLanguage(str);
}
else if (parameter is CultureInfo culture)
{
str = culture.Name;
language = XmlLanguage.GetLanguage(str);
}
else
{
throw new InvalidCastException(nameof(parameter));
}
}
catch (Exception)
{
throw new ArgumentException(nameof(parameter));
}
}
LanguageAware.SetCurrentLanguage(language);
}
}
}
}
设置面板语言的使用示例:
<Window.Resources>
<sys:DateTime x:Key="date">12.31.2021 15:47</sys:DateTime>
</Window.Resources>
<UniformGrid Columns="1"
Language="{Binding Path=(wpfdata:LanguageAware.CurrentLanguage)}">
<TextBlock Text="{Binding Path=(wpfdata:LanguageAware.CurrentLanguage)}" />
<TextBox Text="{Binding Mode=OneWay, Source={StaticResource date}, StringFormat=\{0:F\}}"/>
<TextBox Text="{Binding Mode=OneWay, Source={StaticResource date}, StringFormat=\{0:F\}}"/>
<Button Content="Russian" Command="{wpfdata:LanguageCommand}" CommandParameter="RU" Margin="10"/>
<Button Content="English-USA" Command="{wpfdata:LanguageCommand}" CommandParameter="En-Us" Margin="10"/>
</UniformGrid>
语言属性的标记扩展:
using System.Globalization;
using System.Windows;
using System.Windows.Data;
using System.Windows.Markup;
namespace Wpf.Data
{
[MarkupExtensionReturnType(typeof(Binding))]
public class LanguageAwareExtension : Binding
{
public LanguageAwareExtension()
{
Path = new PropertyPath(
"(0)",
typeof(LanguageAware).GetProperty(nameof(LanguageAware.CurrentLanguage)));
ConverterCulture = CultureInfo.CurrentCulture;
}
}
}
设置窗口语言的使用示例:
<Window ----------------------------
----------------------------
Language="{wpfdata:LanguageAware}">
<Window.Resources>
<sys:DateTime x:Key="date">12.31.2021 15:47</sys:DateTime>
</Window.Resources>
<UniformGrid Columns="1">
<TextBlock Text="{Binding Path=(wpfdata:LanguageAware.CurrentLanguage)}" />
<TextBox Text="{Binding Mode=OneWay, Source={StaticResource date}, StringFormat=\{0:F\}}"/>
<TextBox Text="{Binding Mode=OneWay, Source={StaticResource date}, StringFormat=\{0:F\}}"/>
<Button Content="Russian" Command="{wpfdata:LanguageCommand}" CommandParameter="RU" Margin="10"/>
<Button Content="English-USA" Command="{wpfdata:LanguageCommand}" CommandParameter="En-Us" Margin="10"/>
</UniformGrid>
</Window>