resx资源文件
单个resx扩展
using System; using System.Collections.Generic; using System.ComponentModel; using System.Globalization; using System.Linq; using System.Runtime.CompilerServices; using System.Text; using System.Threading.Tasks; using System.Windows.Data; using System.Windows.Markup; /// <summary> /// XAML 用法: /// xmlns:ext="clr-namespace:自己的命名空间.Resources" /// {ext:Localize MainPage_Menu_Start} /// Model用法: /// LocalizeExtension.Instance["MainPage_Menu_DataPacking"]; /// </summary> internal sealed class LocalizeExtension : MarkupExtension, INotifyPropertyChanged { public static LocalizeExtension Instance { get; } = new(); public static void ChangeLanguage(CultureInfo ci) { Resources.Langs.Lang.Culture = ci; OnLanguageChanged?.Invoke(); } public static event Action? OnLanguageChanged; private string Key { get; } public string Result => GetString(Key); public string this[string key] => GetString(key); public static string GetString(string key) { return Resources.Langs.Lang.ResourceManager.GetString(key, Resources.Langs.Lang.Culture)?.Replace("\\n", "\n") ?? $"#{key}#"; } private LocalizeExtension() { Key = string.Empty; } public LocalizeExtension(string key) { Key = key; OnLanguageChanged += () => { OnPropertyChanged(nameof(Result)); }; } public override object ProvideValue(IServiceProvider serviceProvider) { var binding = new Binding(nameof(Result)) { Source = this, Mode = BindingMode.OneWay }; return binding.ProvideValue(serviceProvider); } public event PropertyChangedEventHandler? PropertyChanged; private void OnPropertyChanged([CallerMemberName] string? propertyName = null) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } }多个resx资源扩展
using System; using System.Collections.Generic; using System.ComponentModel; using System.Globalization; using System.Runtime.CompilerServices; using System.Windows.Data; using System.Windows.Markup; { /// <summary> /// XAML 用法: /// xmlns:ext="clr-namespace:自己的命名空间.Resources" /// 1:{ext:Localize Key=Main_Title, Source=GUI} /// 2:{ext:Localize MainPage_Menu_Start} --默认资源组(Lang) /// Model用法: /// 1:LocalizeExtension.Instance["MainPage_Menu_DataPacking"]; /// 2:LocalizeExtension.Instance["GUI", nameof(Layout.Main_Nav_Title)]; /// </summary> public sealed class LocalizeExtension : MarkupExtension, INotifyPropertyChanged { public static LocalizeExtension Instance { get; } = new LocalizeExtension(); public static event Action? OnLanguageChanged; private const string DefaultSource = "Lang"; private static readonly Dictionary<string, System.Resources.ResourceManager> ResourceManagers = new(StringComparer.OrdinalIgnoreCase) { ["Lang"] = Lang.ResourceManager, ["GUI"] = Layout.ResourceManager, }; public LocalizeExtension(string key) { Key = key; Source = DefaultSource; OnLanguageChanged += RaiseResultChanged; } public LocalizeExtension() { Key = string.Empty; Source = DefaultSource; OnLanguageChanged += RaiseResultChanged; } public string Key { get; set; } = string.Empty; public string Source { get; set; } = DefaultSource; public string this[string key] => GetString(key, DefaultSource); public string this[string source, string key] => GetString(key, source); public string Result => GetString(Key, Source); public override object ProvideValue(IServiceProvider serviceProvider) { var pvt = serviceProvider.GetService(typeof(IProvideValueTarget)) as IProvideValueTarget; var targetObject = pvt?.TargetObject; var targetProperty = pvt?.TargetProperty; // targetObject 是 Binding,必须返回“对象”,不能返回 BindingExpression if (targetObject is Binding) { return this; } // 正常给 DependencyProperty 赋值,返回内部绑定实现动态刷新 if (targetObject is DependencyObject && targetProperty is DependencyProperty) { var binding = new Binding(nameof(Result)) { Source = this, Mode = BindingMode.OneWay }; return binding.ProvideValue(serviceProvider); } return Result; } public static void ChangeLanguage(CultureInfo culture) { Lang.Culture = culture; Layout.Culture = culture; OnLanguageChanged?.Invoke(); } public static string GetString(string key, string? source = null) { if (string.IsNullOrWhiteSpace(key)) return string.Empty; var src = string.IsNullOrWhiteSpace(source) ? DefaultSource : source; if (!ResourceManagers.TryGetValue(src!, out var rm)) return $"#InvalidSource:{src}#{key}#"; var value = rm.GetString(key, Lang.Culture); return value?.Replace("\\n", "\n") ?? $"#{src}:{key}#"; } public static void RegisterSource(string source, System.Resources.ResourceManager manager) { if (string.IsNullOrWhiteSpace(source)) throw new ArgumentException("source is required.", nameof(source)); ResourceManagers[source] = manager ?? throw new ArgumentNullException(nameof(manager)); } public event PropertyChangedEventHandler? PropertyChanged; private void RaiseResultChanged() { OnPropertyChanged(nameof(Result)); } private void OnPropertyChanged([CallerMemberName] string? propName = null) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName)); } }