WPF新手村教程(四)中Application类如何?

摘要:WPF个人文档(四)—— Application类 前言 这一块其实本来是不太想讲的,你不太了解,不影响你写代码,遇到不会的随用随查即可 但是,对这个类有一定的了解之后,会帮助我们更快的了解WPF应用程序的结构,对应理解架构会有很多帮助 但
WPF个人文档(四)—— Application类 前言 这一块其实本来是不太想讲的,你不太了解,不影响你写代码,遇到不会的随用随查即可 但是,对这个类有一定的了解之后,会帮助我们更快的了解WPF应用程序的结构,对应理解架构会有很多帮助 但是,即便如此,实际开发当中使用的并不会特别多,没人会去在乎你对这个类了解的有多好 没人会去在乎你这个小小的员工架构能力有多强,也没人去在乎你会的东西有多么多 公司只会在乎你能不能赚取到足够的价值 一.Application类定义 Application 位于命名空间: System.Windows.Application; 它是一个 单例 类,一个 WPF 应用通常只有一个 Application 实例,可以通过 Application.Current 获取当前实例 该类跟踪在应用程序中打开的所有窗口,决定何时关闭应用程序,并引发可执行初始化和清除操作的应用程序事件 (出自《WPF编程宝典:使用C# 2012和.NET 4.5 第4版》) 其主要职责: 1.启动和退出应用程序 2.管理全局资源(资源字典) 3.管理主窗口和打开的所有窗口 4.处理全局异常 5.支持命令和消息循环 二.ShutdownMode —— Application 类控制程序关闭的方式 WPF 中 Application 类控制程序关闭的三种方式,也就是 ShutdownMode 属性的三种枚举值 1.OnMainWindowClose 当 Application.Current.MainWindow 被关闭时,应用程序退出 适合单窗口应用或有明确主窗口的多窗口应用 2.OnLastWindowClose 当应用程序打开的最后一个窗口被关闭时,应用程序退出 适合多窗口应用,不需要明确主窗口 3.OnExplicitShutdown 应用程序不会自动退出,必须手动调用 Application.Current.Shutdown() 才会结束 适合需要后台逻辑或延迟关闭的场景 示例代码:(C#或者xaml) // 方式1:主窗口关闭时退出 Application.Current.ShutdownMode = ShutdownMode.OnMainWindowClose; // 方式2:最后一个窗口关闭时退出 Application.Current.ShutdownMode = ShutdownMode.OnLastWindowClose; // 方式3:手动关闭 Application.Current.ShutdownMode = ShutdownMode.OnExplicitShutdown; // ... 什么时候需要退出,再调用 Application.Current.Shutdown(); <Application x:Class="Routing.App" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:Routing" <!-- Application类关闭程序的三种方法 --> ShutdownMode="OnExplicitShutdown" <!--ShutdownMode="OnLastWindowClose"--> <!--ShutdownMode="OnExplicitShutdown"--> StartupUri="Route.xaml"> <Application.Resources> </Application.Resources> </Application> [!TIP] WPF中,程序关闭的几种方式: 1.Application.Currrent.Shutdown(); 2.Close(); 3.Environment.Exit(0); 4.Process对象实例.Kill(); 5.Environment类的FailFast // WPF中,程序关闭的几种方式: // 1.WPF官方正途 Application.Currrent.Shutdown(); // 2.这只是关闭窗口,不一定能够关闭程序 Close(); // 3.已经跳出 WPF 了,进入 CLR 级别终止 —— 一般不推荐为正常退出方法 Environment.Exit(0); // 4.操作系统级的强制终止 => 等价于任务管理器 → 结束任务 // Process对象实例.Kill(); Process.Kill(); // 5.一般是给"内存损坏 / 严重不可恢复错误"准备的,即异常终止层面 Environment.FailFast(); # 程序关闭从温柔乡到核弹打击的排行顺序(如果不是以为嫌麻烦,我应该会排一个从夯到拉的表......) Shutdown() ↓ Close() ↓ Environment.Exit() ↓ Process.Kill() ↓ FailFast() 参考:52.第7章_应用程序关闭方式_哔哩哔哩_bilibili 三.事件 Application 类提供了丰富的全局事件 事件 说明 Startup 应用程序启动时触发 Exit 应用程序退出时触发 DispatcherUnhandledException UI 线程未捕获异常处理 SessionEnding 用户注销或关机前触发 Activated / Deactivated 应用程序获得 / 失去焦点时触发 ....... ...... 代码示例:(事件订阅) public App() { Startup += App_Startup; // DispatcherUnhandledException += App_DispatcherUnhandledException; // Exit += App_Exit; } private void App_Startup(object sender, StartupEventArgs e) { // 启动逻辑 } [!TIP] 当然,你不一定非要使用上面的事件,也可以现在重写下面的事件(事件重写) // 应用程序启动时调用,早于任何窗口显示 protected override void OnStartup(StartupEventArgs e) // 应用程序即将退出时调用 protected override void OnExit(ExitEventArgs e) // 当用户会话即将结束(注销或关机)时调用,可取消 protected override void OnSessionEnding(SessionEndingCancelEventArgs e) // 当应用程序成为当前活动程序(获得焦点)时调用 protected override void OnActivated(EventArgs e) // 当应用程序失去焦点时调用 protected override void OnDeactivated(EventArgs e) // ...... 四.程序集资源和资源字典 1.程序集资源 —— Assembly Resource 程序集资源:程序集资源,是指嵌入程序内部的二进制资源,如文本、图片、视频、声音和松散文件(Loose file)等等, 对于这些资源项我们可以将其存储为松散文件或者编译进程序集中 程序集资源 = 被嵌入到 .exe/.dll 内部,由 CLR 统一管理的数据文件 换句话说:程序集资源 = 被编译进 .exe 或 .dll 内部的文件数据 [!TIP] 如果你还是不太好理解,可以这样想象: 程序集 = 一个压缩包 资源 = 压缩包里的文件 从本质上讲一个 .NET 程序集内部包含:IL 代码、元数据(类型信息)、清单(Manifest)、资源(Resources) 资源本质上是“二进制数据块”,可以是:图片、音频、文本、json、xml、图标、任何文件 它们被打包在程序集内部,而不是放在外部目录 程序集资源文件使用示例: 1.首先,我们先在项目中建立一个资源文件夹Resources,然后添加目标资源文件(可以是图片,也可以是音频) 2.然后右击1.png文件,选择属性,在属性中找到生成操作,选择资源 3.这里示例一下程序集资源的使用,你可以在xaml代码中使用,也可以在C#代码中使用 ImageSource="/Resource/1.png"代表图片资源的目录(相对路径) <Grid Margin="20"> <Grid.Background> <ImageBrush ImageSource="/Resource/1.png" Stretch="UniformToFill"/> </Grid.Background> </Grid> [!IMPORTANT] 如何在C#代码中使用程序集资源: 先埋个雷,问:注释掉的代码和没注释掉的代码,哪一个可以正常使用?为什么? namespace Routing { /// <summary> /// Route.xaml 的交互逻辑 /// </summary> public partial class Route : Window { public Route() { InitializeComponent(); // var image = new BitmapImage( // new Uri("Resource/1.png", UriKind.Relative)); var image = new BitmapImage(new Uri("pack://application:,,,/Resource/1.png")); // 创建了一个 URI 对象但没有用,也不会影响加载 new Uri("Resource/1.png", UriKind.Relative); var brush = new ImageBrush(image); brush.Stretch = Stretch.UniformToFill; RootGrid.Background = brush; } } } 答案:第一段代码将无法运行,第二段代码可以正常运行 1.第一段代码不能正常加载 Resource 类型的图片: UriKind.Relative 表示相对路径 WPF 的相对路径是 相对于运行时当前工作目录,或者相对于 Pack URI 的默认规则 当图片 Build Action = Resource 时,WPF 不会把它放在 bin/Debug 目录 也就是说这个相对路径在运行时找不到文件 → 图片加载失败 2.第二段代码使用了 完整 Pack URI : pack://application:,,,/Resource/1.png明确告诉 WPF 去 程序集内部查找资源 Build Action = Resource 的文件可以被找到 代码 能否找到资源 原因 new Uri("Resource/1.png", UriKind.Relative) ❌ Resource 类型文件在程序集内部,相对路径指向磁盘(相对于运行时当前工作目录)找不到 new Uri("pack://application:,,,/Resource/1.png") ✅ Pack URI 明确告诉 WPF 从程序集内部读取资源 2.资源字典 —— ResourceDictionary 字典:键值对 一开始从C/C++过来时,差点被这个词哄住,然后发现,不就是一个键值对吗 资源字典:就是一张键值表(Dictionary),用来存放可复用对象,并提供按作用域查找的机制 键(Key) → 对象名称(x:Key) 值(Value) → 任意对象(Brush、Style、Template、Converter、图片、甚至普通类) [!IMPORTANT] 静态资源StaticResource和动态资源DynamicResource StaticResource → 编译/加载时查找,之后不变 DynamicResource → 运行时查找,可响应资源变化(主题切换) 使用示例: Route.xaml 局部资源字典: # 它属于这个 Window 的作用域 # 查找链是:控件 → Window → Application → 系统 <Window.Resources> <SolidColorBrush x:Key="PrimaryBrush" Color="DodgerBlue"/> </Window.Resources> <Window x:Class="Routing.Route" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:Routing" mc:Ignorable="d" Title="Route" Height="450" Width="800"> <!--局部资源字典--> <Window.Resources> <SolidColorBrush x:Key="PrimaryBrush" Color="DodgerBlue"/> </Window.Resources> <Grid x:Name="RootGrid" Margin="20"> <StackPanel> <!--全局资源字典引用--> <Button Height="50" Width="50" Style="{StaticResource my_style1}"/> <!--局部资源字典引用--> <Button Height="50" Width="50" Background="{StaticResource PrimaryBrush}" /> </StackPanel> </Grid> </Window> 资源字典文件T_Dic.xaml <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> <!--我们现在写的这个只是一个"资源容器文件"--> <!--如果你不在某处合并它,WPF 根本不会加载它--> <Style x:Key="my_style1" TargetType="Button"> <Setter Property="Background" Value="Red"/> <Setter Property="FontSize" Value="45"/> </Style> </ResourceDictionary> App.xaml <Application x:Class="Routing.App" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:Routing" StartupUri="Route.xaml"> <!-- 合并资源字典文件,在这里合并会成为全局资源字典 --> <Application.Resources> <ResourceDictionary Source="/T_Dic.xaml"/> </Application.Resources> </Application> 五.资源本地化 这东西完全就是痛苦面具,相当痛苦啊,这个本地化啊,那是一坨啊,谁负责本地化,谁就痛苦面具到项目结束吧 网上你可以查到一堆本地化的方式,但是一写一个不吱声,一写一堆错,写一行掉一行泪啊 然后就是,资源本地化压根就没有什么最优选的方案,你可能使用的每一种方法都有问题,使用的每一种本地化都是痛苦面具 而且官方推荐的本地化方式也是一坨啊,问题还不少.......甚至官方本地化工具还不是完全开放的,意义何在啊喂 本地化:什么是本地化?说白了就是语言适配版本设置, 你打开你的手机,主题语言切换到英文,这就是英文的本地化 再选择中文切回去,这就是中文的本地化 这里找的很多资料,但是感觉都有问题,最终在B站找到十月寒流大佬的教学视频,是目前我能找到的最好的教学了 所以这一小节基本上都是up的视频相关内容,有必要十分推荐去看看视频 参考视频:如何在WPF中实现支持运行时切换语言的本地化效果_哔哩哔哩_bilibili up主页:十月的寒流的个人空间-十月的寒流个人主页-哔哩哔哩视频 教学视频中都是点到为止,简单讲解了几种不同的本地化方法,没有最好的方法,只有你用起来最顺手的方法 0.本地化前源代码 <Window x:Class="LocalizeDemo.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:LocalizeDemo" mc:Ignorable="d" Title="MainWindow" Height="450" Width="800"> <Window.Resources> <Style TargetType="Button"> <Setter Property="Padding" Value="8,5"/> <Setter Property="FontSize" Value="18"/> </Style> </Window.Resources> <Grid> <StackPanel HorizontalAlignment="Center" VerticalAlignment="Center"> <TextBlock Text="欢迎使用本示例软件" FontSize="32" /> <StackPanel Margin="0,16,0,0" HorizontalAlignment="Center" Orientation="Horizontal"> <Button Content="英文" /> <Separator Width="10" Opacity="0" /> <Button Content="中文" /> </StackPanel> <Button Name="MessageButton" Margin="0,16,0,0" HorizontalAlignment="Center" Content="显示消息" Click="ShowMessageButton_Click"/> </StackPanel> <TextBlock Margin="20" HorizontalAlignment="Right" VerticalAlignment="Bottom" Text="@十月的寒流" FontSize="28" Foreground="LightGray" /> </Grid> </Window> using System.Text; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; namespace LocalizeDemo { /// <summary> /// Interaction logic for MainWindow.xaml /// </summary> public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); } private void ShowMessageButton_Click(object sender, RoutedEventArgs e) { MessageBox.Show("这是一条神秘的消息"); } } } 1.本地化方法一 :资源字典(动态资源) 该方法仅适合小型项目或演示项目,如果要适用于商业大型项目,你就偷着哭吧 优点🌱 说明 简单直观 使用 XAML ResourceDictionary,无需复杂的配置或第三方库 支持动态切换 运行时可即时切换语言,无需重启应用(DynamicResource) 集中管理 所有翻译文本在专属目录(I18N)集中存储,易于维护 类型安全减少 使用 Key-Value 方式,减少硬编码字符串 XAML 原生支持 {DynamicResource} 是 WPF 原生机制,无性能开销 易于扩展 增加新语言只需创建新的 ResourceDictionary 文件 缺点🌱 说明 文件数量多,维护困难 每种语言需要专用的 XAML 文件,维护成本随语言增加而增长 你也不想你有一万处代码,需要修改一万个地方吧 没有翻译管理工具,缺少语言回退 纯手动编辑 XAML,容易遗漏或出错,无法使用专业翻译工具 无法检测系统语言 应用启动时不能自动检测系统语言,需手动指定 不支持复杂数据 ResourceDictionary 只能存储简单字符串,无法处理复杂语法规则 版本控制不便 二进制 XAML (.baml) 编译难以进行文本对比 缺少代码提示以及编译时代码提示 大量代码处无法出现代码提示,仅能复制粘贴,容易出错 如果出现不存在的资源,它不会报错,只会给一个空的数据,排查困难 三方库或类库本地化难以进行本地化 如果你能用这个方法对他们进行本地化,一定记得私聊我,大佬,求教! 优化建议: 不一定非要在xaml代码文件中添加资源,也可以在例如App.xaml.cs中添加代码 或者使用Excel等文件在项目启动时,导入文件中的数据,生成资源字典 示例代码: # 项目代码Tree状图 LocalizeDemo ├── App.xaml ├── App.xaml.cs ├── MainWindow.xaml ├── MainWindow.xaml.cs └── I18N # I18N是指国际化的意思,但是I18N和本地化其实差不太多,别管定义怎么样,反正都得难受 ├── String.en.xaml └── String.zh.xaml MainWindow.xaml <Window x:Class="LocalizeDemo.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:LocalizeDemo" mc:Ignorable="d" Title="{DynamicResource WindowTitle}" Height="450" Width="800" WindowStartupLocation="CenterScreen"> <Window.Resources> <Style TargetType="Button"> <Setter Property="Padding" Value="8,5"/> <Setter Property="FontSize" Value="18"/> </Style> </Window.Resources> <Grid> <StackPanel HorizontalAlignment="Center" VerticalAlignment="Center"> <TextBlock Text="{DynamicResource WelcomeMessage}" FontSize="32" /> <StackPanel Margin="0,16,0,0" HorizontalAlignment="Center" Orientation="Horizontal"> <Button Content="{DynamicResource EnglishButtonContent}" Tag="en" Click="ChangeLanguage_Click"/> <Separator Width="10" Opacity="0" /> <Button Content="{DynamicResource ChineseButtonContent}" Tag="zh" Click="ChangeLanguage_Click"/> </StackPanel> <Button Name="MessageButton" Margin="0,16,0,0" HorizontalAlignment="Center" Content="{DynamicResource ShowMessageButtonContent}" Click="ShowMessageButton_Click"/> </StackPanel> <TextBlock Margin="20" HorizontalAlignment="Right" VerticalAlignment="Bottom" Text="{DynamicResource LogoText}" FontSize="28" Foreground="LightGray" /> </Grid> </Window> MainWindow.xaml.cs using System.Text; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; namespace LocalizeDemo { /// <summary> /// Interaction logic for MainWindow.xaml /// </summary> public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); } private void ShowMessageButton_Click(object sender, RoutedEventArgs e) { MessageBox.Show( Application.Current.TryFindResource("MessageContent").ToString(), Application.Current.TryFindResource("MessageCaption").ToString(), MessageBoxButton.OK ); } private void ChangeLanguage_Click(object sender, RoutedEventArgs e) { var culture = ((Button)sender).Tag.ToString(); var resourceDictionary = new ResourceDictionary { Source = new Uri($"I18N/String.{culture}.xaml", UriKind.Relative) }; Application.Current.Resources.MergedDictionaries.Add(resourceDictionary); } } } String.en.xaml <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:sys="clr-namespace:System;assembly=mscorlib"> <!--assembly=System.Core--> <sys:String x:Key="WindowTitle">WPF I18N Demo</sys:String> <sys:String x:Key="WelcomeMessage">Welcome to this demo software</sys:String> <sys:String x:Key="EnglishButtonContent">English</sys:String> <sys:String x:Key="ChineseButtonContent">Chinese</sys:String> <sys:String x:Key="ShowMessageButtonContent">Show Message</sys:String> <sys:String x:Key="MessageCaption">Notice</sys:String> <sys:String x:Key="MessageContent">This is a message from code behind.</sys:String> <sys:String x:Key="LogoText">@ColdWind</sys:String> </ResourceDictionary> String.zh.xaml <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:sys="clr-namespace:System;assembly=mscorlib"> <!--assembly=System.Core--> <sys:String x:Key="WindowTitle">WPF 本地化演示</sys:String> <sys:String x:Key="WelcomeMessage">欢迎使用此演示软件</sys:String> <sys:String x:Key="EnglishButtonContent">英文</sys:String> <sys:String x:Key="ChineseButtonContent">中文</sys:String> <sys:String x:Key="ShowMessageButtonContent">显示消息</sys:String> <sys:String x:Key="MessageCaption">通知</sys:String> <sys:String x:Key="MessageContent">这是来着后台的神秘消息.</sys:String> <sys:String x:Key="LogoText">@十月的寒流</sys:String> </ResourceDictionary> App.xaml <Application x:Class="LocalizeDemo.App" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:LocalizeDemo" StartupUri="MainWindow.xaml"> <Application.Resources> <ResourceDictionary> <ResourceDictionary.MergedDictionaries> <!-- 默认加载一种语言,这里是中文 --> <!--<ResourceDictionary Source="/I18N/String.en.xaml"/>--> <ResourceDictionary Source="/I18N/String.zh.xaml"/> </ResourceDictionary.MergedDictionaries> </ResourceDictionary> </Application.Resources> </Application> 2.🌱本地化方法二 :resx资源文件(官方推荐 ) [!IMPORTANT] .resx文件和.Resource文件 在此之前,我们先来简单介绍一下.resx文件和.Resource文件 .resx文件 .resx 是 可读的 XML 资源文件,可用文本编辑器打开,看见 <data> 节点 它存在的目的,是让程序员方便编辑资源 典型用途:多语言字符串,小图标,配置文本,本地化内容 当编译项目时,.resx 会被编译成 .resources 二进制文件 # 它们的关系有点类似C#代码和DLL文件: .cs → .dll .resx → .resources .Resource文件 .resources 是 二进制资源文件,给 CLR 使用的,也不会有人吃饱了没事干手动创建它 所以和我们就没有任何关系了,干脆一笔带过吧 它通常由.resx 编译生成或用 resgen.exe 工具生成 加载方式是通过ResourceManager加载的( .NET 标准资源系统) 换句话说:.resx 是“源代码”.resources 是其“编译后的产物” 项目 .resx 文件 .resources 文件 WPF Resource (Build Action) 文件性质 XML 文本文件 二进制文件 任意文件(图片/XAML等) 是否可直接编辑 可以 不可以 原文件可编辑 主要用途 本地化字符串、资源管理 编译后供 CLR 加载 UI 图片、样式、XAML 等 所属系统 .NET 标准资源系统 .NET 标准资源系统 WPF Pack URI 资源系统 加载方式 ResourceManager 自动加载 ResourceManager 加载 pack://application:,,,/ 访问 是否支持自动 Culture 匹配 支持 支持 不支持(需手动管理) 与其他的关系 编译后生成 .resources 由 .resx 生成 独立体系,不通过 .resx 典型使用场景 多语言文本 程序内部资源读取 界面图片、样式字典 优点 说明 Microsoft 官方推荐 是 .NET Framework/Core 的标准本地化方式,得到原生支持 强类型访问 自动生成 Lang.Designer.cs,编译时检查,避免魔法字符串,支持智能提示 文件编辑友好 .resx 是 XML,可用 Visual Studio 界面编辑器,直观易用 卫星程序集支持 可编译为独立的 .resources.dll,实现 DLL 级本地化,支持部署灵活性 自动化代码生成 PublicResXFileCodeGenerator 自动生成访问代码(Lang.WindowTitle 等) 工具链完整 与翻译管理工具(Resharper、LocBaml)集成良好 性能较优 编译时生成二进制资源,运行时加载速度快 文化fallback 自动支持文化回退链(如无 zh-CN,自动回退到 zh,再回退到默认) 缺点 说明 XAML 绑定困难 WPF XAML 中难以直接绑定 resx 资源(需要 Converter 或代码) 无动态切换 必须重启应用或重新加载程序集来切换语言,不支持实时切换 冗余文件 需要为每种语言创建独立的 .resx 文件,必须手义维护结构一致 复杂部署 使用卫星程序集时需配置特定的目录结构和文化标识符 二进制编译 .resx 编译后是二进制,版本控制和合并冲突处理困难 示例代码 但是,在我们写代码之前先创建两个resx文件 1.创建一个名叫Resources的文件夹(但是也不是必须叫这个),选择 添加 => 新建项 => 资源文件 2.我们这里的本地化有两种,一直是中文(默认),一种是英文,所以需要创建两个.resx资源文件 这里创建的两个资源文件分别叫:Lang.resx和Lang.zh-CN.resx [!WARNING] 特别注意一下,如果另一个资源文件不是 规定的格式(这里xxx.yy-CN.resx中,CN代表的中文的意思) 在资源浏览器中,不会出现以下的格式(不会出现红色选框标记的表头),也就是不能快捷编辑本地化了 3.待我们将需要的资源全部编辑以后,才开始写我们的代码,以下代码均可复制粘贴直接使用 MainWindow.xaml <Window x:Class="LocalizeDemo.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:LocalizeDemo" xmlns:lang="clr-namespace:LocalizeDemo.Resources" mc:Ignorable="d" Title="{x:Static lang:Lang.WindowTitle}" Height="450" Width="800" WindowStartupLocation="CenterScreen"> <Window.Resources> <Style TargetType="Button"> <Setter Property="Padding" Value="8,5"/> <Setter Property="FontSize" Value="18"/> </Style> </Window.Resources> <Grid> <StackPanel HorizontalAlignment="Center" VerticalAlignment="Center"> <TextBlock Text="{x:Static lang:Lang.WelcomeMessage}" FontSize="32" /> <StackPanel Margin="0,16,0,0" HorizontalAlignment="Center" Orientation="Horizontal"> <Button Content="{x:Static lang:Lang.EnglishButtonContent}" Tag="en" Click="ChangeLanguage_Click"/> <Separator Width="10" Opacity="0" /> <Button Content="{x:Static lang:Lang.ChineseButtonContent}" Tag="zh-CN" Click="ChangeLanguage_Click"/> </StackPanel> <Button Margin="0,16,0,0" HorizontalAlignment="Center" Content="{x:Static lang:Lang.ShowMessageButtonContent}" Click="ShowMessageButton_Click"/> </StackPanel> <TextBlock Margin="20" HorizontalAlignment="Right" VerticalAlignment="Bottom" Text="{x:Static lang:Lang.LogoText}" FontSize="28" Foreground="LightGray" /> </Grid> </Window MainWindow.xaml.cs using LocalizeDemo.Resources; using System.Globalization; using System.Text; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; namespace LocalizeDemo { /// <summary> /// Interaction logic for MainWindow.xaml /// </summary> public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); } private void ShowMessageButton_Click(object sender, RoutedEventArgs e) { //MessageBox.Show( // Application.Current.TryFindResource("MessageContent").ToString(), // Application.Current.TryFindResource("MessageCaption").ToString(), // MessageBoxButton.OK //); MessageBox.Show(Lang.MessageContent, Lang.MessageCaption, MessageBoxButton.OK); } // 切换语言 private void ChangeLanguage_Click(object sender, RoutedEventArgs e) { var culture = ((Button)sender).Tag.ToString(); //var resourceDictionary = new ResourceDictionary //{ // Source = new Uri($"I18N/String.{culture}.xaml", UriKind.Relative) //}; //Application.Current.Resources.MergedDictionaries.Add(resourceDictionary); var cultureInfo = new CultureInfo(culture!); Thread.CurrentThread.CurrentCulture = cultureInfo; Thread.CurrentThread.CurrentUICulture = cultureInfo; var newWindow = new MainWindow(); newWindow.Show(); Application.Current.MainWindow = newWindow; this.Close(); } } } App.xaml.cs using System.Configuration; using System.Data; using System.Globalization; using System.Windows; namespace LocalizeDemo { /// <summary> /// Interaction logic for App.xaml /// </summary> public partial class App : Application { public App() { var culture = new CultureInfo("en"); Thread.CurrentThread.CurrentCulture = culture; Thread.CurrentThread.CurrentUICulture = culture; } } } 虽然但是啊,这里up给的代码中有一个非常大的问题,new一个新窗口,打开新窗口,关闭旧窗口,这样开销会非常大 但是如果你选择实时刷新的话,又要将对应的UI文本全部列出来,虽然可以实现文字瞬间切换,无延迟和闪烁,且状态保留 但是,总感觉和第一种本地化的方法非常像 // MainWindow.xaml.cs var newWindow = new MainWindow(); newWindow.Show(); Application.Current.MainWindow = newWindow; this.Close(); 如果实现UI实时刷新,那么还需要给每一个需要变化的控件命名,还是挺麻烦的,下面是需要更改的代码文件 MainWindow.xaml.cs using LocalizeDemo.Resources; using System.Globalization; using System.Text; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; namespace LocalizeDemo { /// <summary> /// Interaction logic for MainWindow.xaml /// </summary> public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); } private void ShowMessageButton_Click(object sender, RoutedEventArgs e) { //MessageBox.Show( // Application.Current.TryFindResource("MessageContent").ToString(), // Application.Current.TryFindResource("MessageCaption").ToString(), // MessageBoxButton.OK //); MessageBox.Show(Lang.MessageContent, Lang.MessageCaption, MessageBoxButton.OK); } // 切换语言 - 只需6行代码 private void ChangeLanguage_Click(object sender, RoutedEventArgs e) { var culture = ((Button)sender).Tag.ToString()!; Thread.CurrentThread.CurrentCulture = new CultureInfo(culture); Thread.CurrentThread.CurrentUICulture = new CultureInfo(culture); // 实时刷新 UI 文本 WelcomeText.Text = Lang.WelcomeMessage; EnBtn.Content = Lang.EnglishButtonContent; ZhBtn.Content = Lang.ChineseButtonContent; ShowBtn.Content = Lang.ShowMessageButtonContent; LogoText.Text = Lang.LogoText; Title = Lang.WindowTitle; } } } MainWindow.xaml <Window x:Class="LocalizeDemo.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:LocalizeDemo" mc:Ignorable="d" Title="{local:Localize WindowTitle}" Height="450" Width="800" WindowStartupLocation="CenterScreen"> <Window.Resources> <Style TargetType="Button"> <Setter Property="Padding" Value="8,5"/> <Setter Property="FontSize" Value="18"/> </Style> </Window.Resources> <Grid> <StackPanel HorizontalAlignment="Center" VerticalAlignment="Center"> <TextBlock x:Name="WelcomeText" Text="{local:Localize WelcomeMessage}" FontSize="32" /> <StackPanel Margin="0,16,0,0" HorizontalAlignment="Center" Orientation="Horizontal"> <Button x:Name="EnBtn" Content="{local:Localize EnglishButtonContent}" Tag="en" Click="ChangeLanguage_Click"/> <Separator Width="10" Opacity="0" /> <Button x:Name="ZhBtn" Content="{local:Localize ChineseButtonContent}" Tag="zh-CN" Click="ChangeLanguage_Click"/> </StackPanel> <Button x:Name="ShowBtn" Margin="0,16,0,0" HorizontalAlignment="Center" Content="{local:Localize ShowMessageButtonContent}" Click="ShowMessageButton_Click"/> </StackPanel> <TextBlock x:Name="LogoText" Margin="20" HorizontalAlignment="Right" VerticalAlignment="Bottom" Text="{local:Localize LogoText}" FontSize="28" Foreground="LightGray" /> </Grid> </Window> LocalizeExtension.cs using System.Globalization; using System.Windows.Markup; namespace LocalizeDemo { /// <summary> /// 本地化标记扩展 - 提供初始文本 /// 用法: {local:Localize WelcomeMessage} /// </summary> public class LocalizeExtension : MarkupExtension { // 资源键 private string _key; // 接收资源键 public LocalizeExtension(string key) { _key = key; } // 通过资源管理器获取当前 UI 文化的字符串值 public override object ProvideValue(IServiceProvider serviceProvider) { return Resources.Lang.ResourceManager.GetString(_key, Thread.CurrentThread.CurrentUICulture) ?? _key; } } } 3.🍁地化方法三:NetGet顶级包 —— WPFLocalizeExtension(非常好用,但是只能用于WPF) 1.打开NetGet包管理器,搜索WPFLocalizeExtension,选择下载 2.在本地化方案二中第一套代码的基础上修改: MainWindow.xaml.cs using LocalizeDemo.Resources; using System.Globalization; using System.Text; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; using WPFLocalizeExtension.Engine; namespace LocalizeDemo { /// <summary> /// Interaction logic for MainWindow.xaml /// </summary> public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); } private void ShowMessageButton_Click(object sender, RoutedEventArgs e) { MessageBox.Show(Lang.MessageContent, Lang.MessageCaption, MessageBoxButton.OK); } // 切换语言 private void ChangeLanguage_Click(object sender, RoutedEventArgs e) { var culture = ((Button)sender).Tag.ToString(); var cultureInfo = new CultureInfo(culture!); Thread.CurrentThread.CurrentCulture = cultureInfo; Thread.CurrentThread.CurrentUICulture = cultureInfo; LocalizeDictionary.Instance.Culture = cultureInfo; } } } MainWindow.xaml <Window x:Class="LocalizeDemo.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:LocalizeDemo" xmlns:lang="clr-namespace:LocalizeDemo.Resources" xmlns:lex="http://wpflocalizeextension.codeplex.com" lex:ResxLocalizationProvider.DefaultAssembly="LocalizeDemo" lex:ResxLocalizationProvider.DefaultDictionary="Lang" lex:LocalizeDictionary.DesignCulture="zh-CN" Title="{lex:Loc WindowTitle}" mc:Ignorable="d" Height="450" Width="800" WindowStartupLocation="CenterScreen"> <Window.Resources> <Style TargetType="Button"> <Setter Property="Padding" Value="8,5"/> <Setter Property="FontSize" Value="18"/> </Style> </Window.Resources> <Grid> <StackPanel HorizontalAlignment="Center" VerticalAlignment="Center"> <TextBlock Text="{lex:Loc WelcomeMessage}" FontSize="32" /> <StackPanel Margin="0,16,0,0" HorizontalAlignment="Center" Orientation="Horizontal"> <!--<Button Content="{lex:Loc EnglishButtonContent}" Tag="en" Click="ChangeLanguage_Click"/> <Separator Width="10" Opacity="0" /> <Button Content="{lex:Loc ChineseButtonContent}" Tag="zh-CN" Click="ChangeLanguage_Click"/>--> <!-- SetCultureCommand代替Click --> <Button Content="{lex:Loc EnglishButtonContent}" Tag="en" Command="{Binding Source={x:Static lex:LocalizeDictionary.Instance}, Path=SetCultureCommand}" CommandParameter="en"/> <Separator Width="10" Opacity="0" /> <Button Content="{lex:Loc ChineseButtonContent}" Tag="zh-CN" Command="{Binding Source={x:Static lex:LocalizeDictionary.Instance}, Path=SetCultureCommand}" CommandParameter="zh-CN"/> </StackPanel> <Button Margin="0,16,0,0" HorizontalAlignment="Center" Content="{lex:Loc ShowMessageButtonContent}" Click="ShowMessageButton_Click"/> </StackPanel> <TextBlock Margin="20" HorizontalAlignment="Right" VerticalAlignment="Bottom" Text="{lex:Loc LogoText}" FontSize="28" Foreground="LightGray" /> </Grid> </Window> 这里,我们所使用的NetGet包为我们提供了很多方便的功能,这里仅简单说一两个 1.这里给我们提供了一些特定的语法 lex:ResxLocalizationProvider.DefaultAssembly="LocalizeDemo" lex:ResxLocalizationProvider.DefaultDictionary="Lang" lex:LocalizeDictionary.DesignCulture="zh-CN" Title="{lex:Loc WindowTitle}" # 默认从 LocalizeDemo.dll(或者你项目里的 LocalizeDemo 项目)里去找资源文件(.resx) # 如果你的资源在主程序里,也可以写成 DefaultAssembly="{x:Type local:App}" 形式 lex:ResxLocalizationProvider.DefaultAssembly="LocalizeDemo" # 指定默认资源字典名,这里 Lang 就是字典名,省去每个控件都写完整路径 lex:ResxLocalizationProvider.DefaultDictionary="Lang" # 初始显示的语言 # 设置了 zh-CN,在 Visual Studio Designer 里就能看到中文界面 lex:LocalizeDictionary.DesignCulture="zh-CN" # lex:Loc 是 WPFLocalizeExtension 的标记扩展(MarkupExtension),用于从资源文件中取值 # WindowTitle 是资源键(Key),对应你 Lang.resx 中的项 Title="{lex:Loc WindowTitle}" # 如果续写上面的代码,我们就需要写: Title="{x:Static lang:Lang.WindowTitle}" <StackPanel Margin="0,16,0,0" HorizontalAlignment="Center" Orientation="Horizontal"> <!--<Button Content="{lex:Loc EnglishButtonContent}" Tag="en" Click="ChangeLanguage_Click"/> <Separator Width="10" Opacity="0" /> <Button Content="{lex:Loc ChineseButtonContent}" Tag="zh-CN" Click="ChangeLanguage_Click"/>--> <!-- SetCultureCommand代替Click --> <Button Content="{lex:Loc EnglishButtonContent}" Tag="en" Command="{Binding Source={x:Static lex:LocalizeDictionary.Instance}, Path=SetCultureCommand}" CommandParameter="en"/> <Separator Width="10" Opacity="0" /> <Button Content="{lex:Loc ChineseButtonContent}" Tag="zh-CN" Command="{Binding Source={x:Static lex:LocalizeDictionary.Instance}, Path=SetCultureCommand}" CommandParameter="zh-CN"/> </StackPanel> 4.🍁地化方法三:NetGet顶级包 —— Antelcat.I18N.WPF(国人出品,必属精品,I18N有多套框架版本) I18N包作者B站主页:羚宴的个人空间-羚宴个人主页-哔哩哔哩视频 项目Github地址:GitHub - Antelcat/I18N: Reactive language support for WPF/Avalonia applications when using .resx file. · GitHub 虽然看起来非常好用, 但是,这个up的视频中会的代码会出现WPFLocalizeExtension和Antelcat.I18N.WPF代码冲突, 然后无法使用......我太会解决NetGet包的冲突, 再然后网上对于这个的教程视频非常少,然后我去GitHub上面找了源代码和示例代码 发现Github上的有版本限制:C# >= 9.0,嗯.......如果以后会用到再查查资料吧 随笔参考: 1.WPF 程序集资源 - Min.Xiaoshuang - 博客园 2.17. 程序集_03_资源和附属程序集 - 知乎 3.创建资源文件 - .NET | Microsoft Learn 4.C#(99):资源与本地化 System.Resources - springsnow - 博客园 5.56.第7章_自定义本地化_哔哩哔哩_bilibili 6.57.第7章_微软推荐的本地化_哔哩哔哩_bilibili 7.如何在WPF中实现支持运行时切换语言的本地化效果_哔哩哔哩_bilibili