WPF程序都是由 System.Windows.Application 类表示的一个实例,该类跟踪所有窗口,决定何时关闭程序,并引发可执行初始化和清除操作的程序事件。
程序生命周期
本质上,Visual Studio为 Application类使用的模板和窗口模板相同,默认该模板命名为 App.xaml .
<Application x:Class="WindowDemo.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WindowDemo"
StartupUri="MainWindow.xaml">
<Application.Resources>
</Application.Resources>
</Application>
与窗口一样,应用程序类也在两个独立部分中定义,在编译时融合到一起。自动生成的代码部分在项目中不可见,看起来如下:
using System;
using System.Windows;
public partial class App : Application
{
[STAThread()]
public static void Main()
{
WindowDemo.App app = new WindowDemo.App();
app.InitializeComponent();
app.Run();
}
public void InitializeComponent()
{
// 自动生成的代码并没有使用 MainWindow 属性
this.StartupUri = new Uri("MainWindow.xaml", System.UriKind.Relative);
}
}
如果想查看 XAML 模板创建的代码,可查找 obj\Debug 文件夹里的 App.g.cs 文件。
第二部分代码存储在 App.xaml.cs 文件中,包含开发人员添加的处理事件的代码,最初是空的:
public partial class App : Application
{
// 这部分代码 会和自动生成的代码融合到一起
}
关闭程序
一般,只要还有窗口没关闭,Application 类就保持程序处于有效状态,可通过 Application.ShutdownMode 属性来修改。
名称 | 说明 |
---|---|
OnLastWindowClose | 默认行为,如果关闭了主窗口,Application.MainWindow仍引用已关闭窗口对象 |
OnMainWindowClose | 只要主窗口处于打开状态,程序就保持运行状态 |
OnExplicitShutdown | 除非调用 Application.Shutdown() 方法,否则程序不会结束 |
当调用Shutdown() 方法时,Application对象在 Run() 方法返回前自动关闭其他所有窗口,然后方法返回。
应用程序事件
最初,App.xaml.cs 文件不包含任何代码,但可添加处理应用程序事件。
名称 | 说明 |
---|---|
Startup | 调用 Run() 方法之后,主窗口显示之前发生。可使用该事件检查命令行参数,还可创建和显示主窗体(而不是用 StartUri属性) |
Exit | 程序关闭,并在 Run() 即将返回之前发生,此时不能取消关闭,但可重新启动 |
SessionEnding | 在Widnows对话结束时发生-比如注销或关闭计算机。可设置 SessionEndingEventArgs.Cancel=true来取消关闭,否则将调用 Shutdown() |
Activated | 激活窗口时发生 |
Deactivated | 取消激活窗口时发生 |
DispatcherUnhandledException | 未处理的异常 |
处理事件时有两种选择:关联事件处理程序或重写相应的受保护方法。
Application类的任务
当第一次启动程序时会有一些延迟,CLR需要初始化 .NET 环境,然后启动程序。
显示初始界面
WPF 提供了添加初始界面的方法:
- 为项目添加图形文件(通常是.bmp,.png或.jpg)
- 在 Solution Explorer 中选择图像文件
- 将 Build Action 修改为 SplashScreen .
下次运行程序时,图像会立即显示屏幕中央,一旦准备好运行时环境,而且 Application_Startup 方法执行完毕,第一个窗口就将显示出来,这时初始界面会很快消失。
当添加初始界面时,WPF编译器会自动生成 App.g.cs 文件添加类似下面的代码:
SplashScreen splashScreen = new SplashScreen("splashScreenImage.png");
// ture 表示第一个窗体弹出后,自动关掉初始界面
splsahScreen.Show(true);
MyApplication.App app = new MyApplication.App();
app.InitializeComponent();
app.Run();
// 初始界面关闭
可以改变初始界面隐藏的速度。调用 SplashScreen.Show(false) ,当调用 SplashScreen.Close() 方法时隐藏,并可以提供 TimeSpan 表示经过过长时间淡出初始界面。
处理命令行参数
需要相应 Application.Startup 事件,命令行参数通过 StartupEventArgs.Args 属性作为字符串数组提供的。
private static void App_Startup(object sender, StartupEventArgs e)
{
FileViewer win = new FileViewer();
if(e.Agrs.Length > 0)
{
string file = e.Args[0];
if(System.IO.File.Exists(file))
{
win.LoadFile(file);
}
else
{
win.Show();
}
}
}
程序集资源
程序集资源时为项目添加文件,从而 Visual Studio 可将其签入到编译过的应用程序的 EXE 或 DLL 文件中。程序集资源又称为 二进制资源 ,因为它们作为不透明的二进制数据被嵌入到已编译的程序集中。
每次编译程序时,XAML 文件都会转换为解析效率更高的 BAML 文件作为独立资源嵌入到程序集中。
添加资源
在项目中添加文件,并在 Properties 窗口中将其 Build Action 设置为 Resource 来添加自己的资源。WPF将它们和其他 BAML 资源合并到单独的流中。
检索资源
使用资源有多种方法,低级方法是检索封装数据的 StreamResourceInfo 对象:
Assembly assembly = Assembly.GetAssembly(this.GetType());
string resourceName = assembly.GetName().Name + ".g";
ResourceManager rm = new ResourceManager(resourceName, assembly);
using(ResourceSet set = rm.GetResourceSet(CultureInfo.CurrentCulture, true, ture))
{
UnmanagedMemoryStream s;
s = (UnmanagedMemoryStream) set.GetObject("image/winter.jpg", true);
}
WPF提供了几个专门使用资源的类,使用资源的名称访问资源:
<!--这里使用 正斜杠,这是使用 URL的约定-->
<Image Source="Images/happy.jpag"/>
pack URL
WPF 使用 pack URL寻址编译过的资源。上面使用图像的相对URL来引用资源等效于:
pack://application:,/Images/happy.jpg
pack URL语法来自 XPS 标准。三个逗号实际上是三个转义的斜杠,即 application:/// .
还可以检索嵌入到另一个库中的资源,语法如下:
<!--语法结构:pack://application:,,,/AssemblyName;component/ResourceName -->
<!--等价的相对URI ImageLibrary;component/Images/happy.jpg-->
<Image Source="pack://application:,,,/ImageLibrary;component/Images/happy.jpag" />
本地化
本地化程序的挑战在于整个工作过程-如何从项目中提取XAML文件,如何本地化这些XAML文件,如何将它们编译进附属程序集中,如何在应用中使用本地化的资源。
让项目支持本地化
在项目的 .csproj 文件中第一个 PropertyGroup 元素的任意地方添加以下元素:
<UICulture>en-US</UICulture>
编译程序时,会生成名为 en-US 的子文件夹,里面包含 附属程序集 ,与应用程序同名,扩展名为 .resources.dll .附属程序集包含了程序的所有编译过的 BAML 资源,以前这些资源保存在主应用程序的程序集中。