每个运行中的WPF应用程序都由System.Windows.Application类的一个实例来表示。详细分析Application类,讨论如何使用该类执行类似捕获未处理的错误、显示初始屏幕以及检索命令行参数,理解Application类的基础结构,介绍如何创建和使用程序集资源(assemblyresources)。每个资源是一块可嵌入到编译过的应用程序中的二进制数据。
1.应用程序生命周期
1.1创建Application对象
- 起点为XAML模板,默认为App.xaml文件。
<Application x:Class="DarkAdminPanel.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:DarkAdminPanel"
xmlns:fa="http://schemas.awesome.incremented/wpf/xaml/fontawesome.sharp"
StartupUri="MainWindow.xaml">
</Application>
- 解释:
- Class特性创建派生自元素的类。因此创建了派生自Application的类
- 类名为TestApplication.App(TestApplication是项目名称,也是在其中定义类的名称空间,App是Visual Studio为派生自Application 的自定义类使用的名称。如果愿意,可将类名改为任何更有趣的内容)。
- 设置StartupUri属性来确定代表主窗的XAML文档。因此,不需要使用代码显式地实例化这个窗口——XAML解析器将自动完成这项工作。
- 与窗口一样,应用程序类也在两个独立部分中进行定义,在编译时融合到一起。自动生成的部分在项目中是不可见的,但该部分包含Main()入口处以及启动应用程序的代码。该代码在App.xaml→App.xaml.cs→App下(obj\Debug\App.g.cs文件)。
1.2关闭Application对象
通常,只要还有窗口尚未关闭,Application类就保持应用程序处于有效状态。可调整Application.ShutdownMode属性控制对象的关闭。
- OnLastWindowClose。这是默认行为—―只要至少还有一个窗口存在,应用程序就保持运行状态。如果关闭了主窗口,Application.MainWindow属性仍引用代表已关闭窗口的对象(也可以根据情况,使用代码将MainWindow属性重新指向另一个不同的窗口)
- OnMainWindowClose。这是传统方式---只要主窗口还处于打开状态,应用程序就保持运行状态。
- OnExplicitShutdown。除非调用Application.Shutdown( )方法,否则应用程序不会结束(即使所有窗口都已经关闭)。如果应用程序是长期运行的后台任务的前端,或者只是希望使用更复杂的逻辑来决定应用程序应当何时关闭,使用这种方法可能会有意义(这时将调用Application.Shutdown()方法)
- 无论使用哪种关闭方法,总可以使用Application.Shutdown( )方法立即终止应用程序。调用Application.Shutdown()方法会导致Application.Run()方法立即返回,但仍可继续运行Main()方法中的其他代码或者响应Application.Exit事件。
1.3应用程序事件
APP.xaml.cs文件默认不包含任何代码,但提供了非常有用的事件,下面介绍几个重要的。
- Startup。该事件在调用Application.Run()方法之后,并且在主窗口显示之前(如果把主窗口传递给Run()方法)发生。可使用该事件检查所有命令行参数,命令行参数是通过StartupEventArg.Args属性作为数组提供的。还可使用该事件创建和显示主窗口(而不是使用App.xaml文件中的StartUri属性)
- Exit。该事件在应用程序关闭时(不管是因为什么原因),并在 Run()方法即将返回之前发生。此时不能取消关闭,但可以通过代码在 Main()方法中重新启动应用程序。可使用Exit事件设置从 Run()方法返回的整数类型的退出代码。
- SessionEnding。该事件在Windows对话结束时发生-—例如,当用户注销或关闭计算机时(通过检查SessionEndingCancelEventArgs.ReasonSessionEnding 属性可以确定原因)。也可通过将SessionEndingEventArgs.Cancel属性设置为true来取消关闭应用程序。否则,当事件处理程序结束时,WPF将调用Application.Shutdown()方法。
- Activated。当激活应用程序中的窗口时发生该事件。当从另一个Windows程序切换到该应用程序时会发生该事件。当第一次显示窗口时也会发生该事件。
- Deactivated。当取消激活应用程序中的窗口时发生该事件。当切换到另一个Windows程序时也会发生该事件。
- DispatcherUnhandledException。在应用程序(主应用程序线程)中的任何位置,只要发生未处理的异常,就会发生该事件(应用程序调度程序会捕获这些异常)。通过响应该事件,可记录重要错误,甚至可选择不处理这些异常,并通过将 DispatcherUnhandledException-EventArgs.Handled属性设置为true继续运行应用程序。只有当可以确保应用程序仍然处于合法状态并且可以继续运行时,才可以这样处理。
2.Application类任务
2.1显示初始界面
WPF虽然运行快,但并不能瞬间启动,会有一些延迟。如果初始化更耗时,可使用初始界面特性,显得更专业。
- 初始界面添加方法
- (1)为项目添加图像文件(通常是.bmp、.png 或.jpg文件)。
- (2)在 Solution Explorer中选择图像文件。
- (3)将Build Action修改为SplashScreen。
- 以上3步操作完成后,会在App.g.i.cs文件中自动生成如下代码
public static void Main() {
SplashScreen splashScreen = new SplashScreen("images/backimage.jpg"); // 初始界面代码
splashScreen.Show(true); // 初始界面代码
LoginForm.App app = new LoginForm.App();
app.InitializeComponent();
app.Run();
}
添加后启动程序会显示初始界面,直到准备好运行时环境,而且Application_Startup方法执行完毕。
如果想控制初始界面关闭的时间,唯一的方法是向SplashScreen.Show()方法传递false(从而使WPF不会自动淡入初始界面)。然后由您负责通过调用SplashScreen.Close()方法在恰当的时机隐藏初始界面,并提供TimeSpan值来指示经过多长时间淡出初始界面。
2.2处理命令行参数
为处理命令行参数,需要响应 Application.Startup事件。命令行参数是通过StartupEventArgs.Args属性作为字符串数组提供的。
App.xaml.cs文件中添加如下代码。(原书中App_Startup方法有Static修饰符,此处去掉了)
App.xaml文件中更改如下
将原来的StartupUri="MainWindow.xaml",更改为Startup="App_Startup"。
2.3访问当前Application对象
通过静态的Application.Current 属性,可在应用程序的任何位置获取当前应用程序实例从而在窗口之间进行基本交互,因为任何窗口都可以访问当前Application对象,并通过Application对象获取主窗口的引用。
MainWindow main = (MainWindow)Application.Current.MainWindow;
MessageBox.Show(main.Title);
2.4窗口间交互
当在窗口之间进行交互时,不要忘记面向对象的特点。始终使用为窗口类添加的自定义方法、属性和事件层。永远不要直接向代码的其他部分公开窗体的字段或控件。如果这么做,那么很快就会受到紧耦合接口的困扰,在紧耦合接口中,一个窗口会直接深入到另一个窗口的内部工作,如果不消除它们之间这种晦涩的相互依赖关系,将无法增强任何一个类的功能。
MainWindow.xaml.cs中代码(与原例稍有不同)。
3.程序集资源
3.1添加资源
可通过向项目添加文件,并在 Properties窗口中将其Build Action属性设置为 Resource来添加自己的资源。这就是需要完成的全部工作。为了合理组织资源,可将资源放在不同的文件夹中,此种方法易于更新资源,只需替换后重新编译即可。
成功使用必注意:
- 不能将Build Action 属性错误地设置为Embedded Resource。尽管所有程序集资源都被定义为嵌入的资源,但Embedded Resource生成操作会在另一个更难访问的位置放置二进制数据。在 WPF应用程序中,假定总是使用Resource生成类型。
- 不要在 Project Properties窗口中使用Resource选项卡。WPF 不支持这种类型的资源URI。
- 文件名支持空格,编译时能够自动忽略空格。编译后文件名都变成小写的。
3.2检索资源
- 低级方法:使用静态Application.GetResourceStream()方法。该方法ContentType属性发挥数据类型字符串,改例中为image/jpg。Stream属性返回UnmanagedMemoryStream对象,可用该对象读取数据,一次读取一个字节。
StreamResourceInfo sri = Application.GetResourceStream(new Uri("images/winter.jpg", UriKind.Relative));
- 较复杂方法:访问AssemblyName.g.Resources资源流,并查找资源的方法。
- WPF特有方法
- 标记语言:<Image source="Images/Blue hills.jpg"></ Image>。此处建议使用正斜杠。
- 代码方法-绝对路径:img.Source = new BitmapImage (new Uri(@"d: \Photo\Backgroundslarch.jpg"));
- 代码方法-相对路径:img.source = new BitmapImage (new Uri ("images/winter.jpg",UriKind.Relative));
3.3内容文件
对于大的,不嵌入到程序集中的文件,可使用ContentFile特性,告诉每个内容文件的存在。为项目添加声音文件,在Solution Explorer中选择该文件,并在 Properties窗口中将 Build Action属性改为Content。确保将Copy to Output Directory属性设置为CopyAlways,以保证当生成项目时将声音文件复制到输出目录中。
<MediaElement Name="Sound" source="sounds/start.wav" LoadedBehavior="Manual"></MediaElement>