应用程序管理概述

所有应用程序都可能会共享一组适用于实现和管理应用程序的常见功能。 本主题概述 Application 类中用于创建和管理应用程序的功能。

应用程序类

在 WPF 中,应用程序范围的常见功能封装在 Application 类中。 Application 类包括以下功能:

  • 对应用程序的生存期进行跟踪并与之进行交互。

  • 检索和处理命令行参数。

  • 检测和响应未经处理的异常。

  • 共享应用程序范围的属性和 资源。

  • 管理独立应用程序中的窗口。

  • 跟踪和管理导航。

如何使用 Application 类执行常见任务

如果没兴趣了解 Application 类的所有详情,下表列出了 Application 的一些相关常见任务及其完成方法。 通过查看相关的 API 和主题,可以找到详细信息和示例代码。

任务 方法
获取表示当前应用程序的对象 使用 Application.Current 属性。
将启动屏幕添加到应用程序中 请参阅将初始屏幕添加到 WPF 应用程序
启动应用程序 使用 Application.Run 方法。
停止应用程序 使用 Application.Current 对象的 Shutdown 方法。
从命令行获取参数 处理 Application.Startup 事件并使用 StartupEventArgs.Args 属性。 有关示例,请参阅 Application.Startup 事件。
获取和设置应用程序退出代码 Application.Exit 事件处理程序中设置 ExitEventArgs.ApplicationExitCode 属性,或调用 Shutdown 方法并传入一个整数。
检测和响应未经处理的异常 处理 DispatcherUnhandledException 事件。
获取和设置应用程序范围的资源 使用 Application.Resources 属性。
使用应用程序范围的资源字典 请参阅使用应用程序范围的资源字典
获取和设置应用程序范围的属性 使用 Application.Properties 属性。
获取和保存应用程序的状态 请参阅跨应用程序会话保持和还原应用程序范围的属性
管理非代码数据文件,包括资源文件、内容文件和源站点文件。 请参阅 WPF 应用程序资源、内容和数据文件
管理独立应用程序中的窗口 请参阅 WPF 窗口概述
跟踪和管理导航 请参阅导航概述

应用程序定义

若要利用 Application 类的功能,必须实现应用程序定义。 WPF 应用程序定义是一个类,该类派生自 Application 并使用特殊 MSBuild 设置来配置。

实现应用程序定义

典型的 WPF 应用程序定义使用标记和代码隐藏来实现。 因此,可以使用标记以声明方式设置应用程序的属性、资源和注册事件,同时还能处理事件并在代码隐藏中实现特定于应用程序的行为。

以下示例演示了如何使用标记和代码隐藏来实现应用程序定义:

<Application 
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
  x:Class="SDKSample.App" />
using System.Windows;

namespace SDKSample
{
    public partial class App : Application { }
}

Imports System.Windows

Namespace SDKSample
    Partial Public Class App
        Inherits Application
    End Class
End Namespace

要允许标记文件和代码隐藏文件协同工作,需要进行以下配置:

  • 在标记中,Application 元素必须包含 x:Class 属性。 生成应用程序时,标记文件中存在 x:Class 会使 MSBuild 创建派生自 Applicationpartial 类,并且名称由 x:Class 属性指定。 这要求为 XAML 架构 (xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml") 添加 XAML 命名空间声明。

  • 在代码隐藏中,类必须是 partial 类,其名称必须是标记中的 x:Class 属性指定的相同名称,并且它必须派生自 Application。 这样,代码隐藏文件就可以与生成应用程序时为标记文件生成的 partial 类(请参阅生成 WPF 应用程序)相关联。

注意

使用 Visual Studio 创建新的 WPF 应用程序项目或 WPF 浏览器应用程序项目时,应用程序定义会默认包含到其中,并使用标记和代码隐藏来定义。

至少要有这段代码,才能实现应用程序定义。 但是,在生成并运行应用程序之前,还需要对应用程序定义进行 MSBuild 配置。

针对 MSBuild 配置应用程序定义

独立应用程序和 XAML 浏览器应用程序 (XBAP) 需要先实现某个层次的基础结构,然后才能运行。 在该基础结构中,入口点是最重要的一个部分。 当用户启动某个应用程序时,操作系统会调用入口点,这是一个为人熟知的应用程序启动函数。

警告

XBAP 需要旧版浏览器(例如 Internet Explorer 和 Firefox)才能运行。 Windows 10 和 Windows 11 通常不支持这些较旧的浏览器版本。 由于安全风险,新式浏览器不再支持 XBAP 应用所需的技术。 不再支持启用 XBAP 的插件。

通常,开发者需要自行编写其中的部分或全部代码(具体取决于所采用的技术)。 但是,如以下 MSBuild 项目文件所示,如果将应用程序定义的标记文件配置为 MSBuild ApplicationDefinition 项,WPF 会代为生成这段代码:

<Project
  DefaultTargets="Build"
                        xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  ...
  <ApplicationDefinition Include="App.xaml" />
  <Compile Include="App.xaml.cs" />
  ...
</Project>

由于代码隐藏文件包含代码,因此会照常标记为 MSBuild Compile 项。

将这些 MSBuild 配置应用于应用程序定义的标记和代码隐藏文件会导致 MSBuild 生成如下代码:

using System;
using System.Windows;

namespace SDKSample
{
    public class App : Application
    {
        public App() { }
        [STAThread]
        public static void Main()
        {
            // Create new instance of application subclass
            App app = new App();

            // Code to register events and set properties that were
            // defined in XAML in the application definition
            app.InitializeComponent();

            // Start running the application
            app.Run();
        }

        public void InitializeComponent()
        {
            // Initialization code goes here.
        }
    }
}
Imports System.Windows

Namespace SDKSample
    Public Class App
        Inherits Application
        Public Sub New()
        End Sub
        <STAThread>
        Public Shared Sub Main()
            ' Create new instance of application subclass
            Dim app As New App()

            ' Code to register events and set properties that were
            ' defined in XAML in the application definition
            app.InitializeComponent()

            ' Start running the application
            app.Run()
        End Sub

        Public Sub InitializeComponent()
            ' Initialization code goes here.	
        End Sub
    End Class
End Namespace

所生成的代码利用额外的基础结构代码对应用程序定义进行了补充,其中包括入口点方法 Main。 系统将 STAThreadAttribute 属性应用于 Main 方法,以指明 WPF 应用程序的主 UI 线程是 STA 线程,而这是 WPF 应用程序都需要满足的一点。 被调用时,Main 会先创建一个新的 App 实例,然后再调用 InitializeComponent 方法以注册事件并设置标记中实现的属性。 由于 InitializeComponent 已生成,因此无需像实现 PageWindow 那样从应用程序定义中显式调用 InitializeComponent。 最后,调用 Run 方法以启动应用程序。

获取当前应用程序

由于 Application 类的功能会在整个应用程序中共享,因此每个 AppDomain 只能有一个 Application 类实例。 为了强制实施这一点,Application 类会作为单一实例类来实现(请参阅在 C# 中实现单一实例),它会为自身创建单个实例,并通过 staticCurrent 属性来提供该实例的共享访问权限。

以下代码演示了如何针对当前 AppDomain 获取对 Application 对象的引用。

// Get current application
Application current = App.Current;
' Get current application
Dim current As Application = App.Current

Current 会返回对 Application 类实例的引用。 如以下示例所示,如果需要对 Application 派生类的引用,就必须强制转换 Current 属性的值。

// Get strongly-typed current application
App app = (App)App.Current;
' Get strongly-typed current application
Dim appCurrent As App = CType(App.Current, App)

Current 的值在 Application 对象的生存期内随时可供检查。 但是,检查时要小心。 在 Application 类实例化后,有一段时间 Application 对象的状态会不一致。 为了运行代码,Application 会在此期间执行各种必要的初始化任务,其中包括生成应用程序基础结构、设置属性以及注册事件。 如果尝试在此期间使用 Application 对象,代码可能会产生意外结果,尤其是当代码依赖于正在设置的各个 Application 属性时。

Application 的生存期在其完成初始化工作后才真正开始。

应用程序生存期

WPF 应用程序的生存期会通过 Application 引发的几个事件来加以标记,用以明示应用程序分别于何时启动、激活、停用和关闭。

初始屏幕

从 .NET Framework 3.5 SP1 开始,可以指定启动窗口或初始屏幕中所用的图像。 利用 SplashScreen 类,可以轻松地在应用程序加载时显示启动窗口。 SplashScreen 窗口将在 Run 被调用前创建并显示。 有关详细信息,请参阅应用程序启动时间将初始屏幕添加到 WPF 应用程序

启动应用程序

调用 Run 并初始化应用程序后,应用程序即可运行。 当引发 Startup 事件时,即表示这一刻已经来临:

using System.Windows;

namespace SDKSample
{
    public partial class App : Application
    {
        void App_Startup(object sender, StartupEventArgs e)
        {
            // Application is running
        }
    }
}

Namespace SDKSample
    Partial Public Class App
        Inherits Application
        Private Sub App_Startup(ByVal sender As Object, ByVal e As StartupEventArgs)
            ' Application is running
            '</SnippetStartupCODEBEHIND1>
    End Class
End Namespace
'</SnippetStartupCODEBEHIND2>

在生存期中的这一个时间点,应用程序最常执行的操作就是显示 UI。

显示用户界面

大多数的独立 Windows 应用程序会在开始运行时打开一个 Window。 如以下代码所示,这一操作可通过 Startup 事件处理程序来执行。

<Application
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  x:Class="SDKSample.App" 
  Startup="App_Startup" />
using System.Windows;

namespace SDKSample
{
    public partial class App : Application
    {
        void App_Startup(object sender, StartupEventArgs e)
        {
            // Open a window
            MainWindow window = new MainWindow();
            window.Show();
        }
    }
}

Imports System.Windows

Namespace SDKSample
    Partial Public Class App
        Inherits Application
        Private Sub App_Startup(ByVal sender As Object, ByVal e As StartupEventArgs)
            ' Open a window
            Dim window As New MainWindow()
            window.Show()
        End Sub
    End Class
End Namespace

注意

默认情况下,在独立应用程序中完成实例化的第一个 Window 会成为主应用程序窗口。 此 Window 对象由 Application.MainWindow 属性引用。 如果应该让第一个实例化 Window 之外的窗口成为主窗口,可通过编程方式更改 MainWindow 属性的值。

当 XBAP 首次启动时,它很可能会导航到某个 Page。 以下代码对此进行了演示。

<Application 
  x:Class="SDKSample.App"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  Startup="App_Startup" />
using System;
using System.Windows;
using System.Windows.Navigation;

namespace SDKSample
{
    public partial class App : Application
    {
        void App_Startup(object sender, StartupEventArgs e)
        {
            ((NavigationWindow)this.MainWindow).Navigate(new Uri("HomePage.xaml", UriKind.Relative));
        }
    }
}

Imports System.Windows
Imports System.Windows.Navigation

Namespace SDKSample
    Partial Public Class App
        Inherits Application
        Private Sub App_Startup(ByVal sender As Object, ByVal e As StartupEventArgs)
            CType(Me.MainWindow, NavigationWindow).Navigate(New Uri("HomePage.xaml", UriKind.Relative))
        End Sub
    End Class
End Namespace

如果要将 Startup 处理为只打开某个 Window 或导航到某个 Page,则可转而设置标记中的 StartupUri 属性。

以下示例演示如何在独立应用程序中使用 StartupUri 来打开某个 Window

<Application
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    StartupUri="MainWindow.xaml" />

以下示例演示如何在 XBAP 中使用 StartupUri 导航到某个 Page

<Application
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    StartupUri="HomePage.xaml" />

此标记的效果等同于用于打开窗口的上一段代码。

注意

有关导航的详细信息,请参阅导航概述

如果存在以下需求,则需处理 Startup 事件以使其打开某个 Window:需要使用非无参数构造函数来实例化窗口;在显示窗口之前,需要先设置其属性或订阅其事件;或是,需要处理应用程序启动时所提供的任意命令行参数。

处理命令行参数

在 Windows 中,可以从命令提示符或桌面启动独立应用程序。 在这两种情况下,命令行参数都可以传递至应用程序。 以下示例展示了一个通过单个命令行参数“/StartMinimized”来启动的应用程序:

wpfapplication.exe /StartMinimized

在应用程序初始化期间,WPF 会从操作系统中检索命令行参数,并通过 StartupEventArgs 参数的 Args 属性将这些命令行参数传递到 Startup 事件处理程序。 可以使用如下代码来检索和存储命令行参数。

<Application
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  x:Class="SDKSample.App"
  Startup="App_Startup" />
using System.Windows;

namespace SDKSample
{
    public partial class App : Application
    {
        void App_Startup(object sender, StartupEventArgs e)
        {
            // Application is running
            // Process command line args
            bool startMinimized = false;
            for (int i = 0; i != e.Args.Length; ++i)
            {
                if (e.Args[i] == "/StartMinimized")
                {
                    startMinimized = true;
                }
            }

            // Create main application window, starting minimized if specified
            MainWindow mainWindow = new MainWindow();
            if (startMinimized)
            {
                mainWindow.WindowState = WindowState.Minimized;
            }
            mainWindow.Show();
        }
    }
}

Imports System.Windows

Namespace SDKSample
    Partial Public Class App
        Inherits Application
        Private Sub App_Startup(ByVal sender As Object, ByVal e As StartupEventArgs)
            ' Application is running
            ' Process command line args
            Dim startMinimized As Boolean = False
            Dim i As Integer = 0
            Do While i <> e.Args.Length
                If e.Args(i) = "/StartMinimized" Then
                    startMinimized = True
                End If
                i += 1
            Loop

            ' Create main application window, starting minimized if specified
            Dim mainWindow As New MainWindow()
            If startMinimized Then
                mainWindow.WindowState = WindowState.Minimized
            End If
            mainWindow.Show()
        End Sub
    End Class
End Namespace

这段代码会处理 Startup 以检查是否有提供 /StartMinimized 命令行参数;如有提供,则会按照 WindowStateMinimized 状态来打开主窗口。 请注意,由于 WindowState 属性必须通过编程方式来设置,因此主 Window 必须在代码中显式打开。

XBAP 无法检索和处理命令行参数,因为这些程序通过 ClickOnce 部署来启动(请参阅部署 WPF 应用程序)。 但是,它们可以检索和处理来自用于启动它们的 URL 的查询字符串参数。

应用程序激活和停用

Windows 允许用户在应用程序间切换。 最常见的方法是使用 ALT + TAB 键组合。 仅当应用程序拥有可供用户选择的可视化 Window 时,才能切换到该应用程序。 当前所选的 Window 为活动窗口(也称为前台窗口),也是接收用户输入的 Window。 具有活动窗口的应用程序为活动应用程序(或称为前台应用程序)。 应用程序会在以下情况下变为活动应用程序:

  • 应用程序启动并显示 Window

  • 用户通过选择 Window 从另一应用程序切换到该窗口所属的应用程序。

通过处理 Application.Activated 事件,可以检测到某个应用程序在何时变为活动应用程序。

同样地,应用程序会在以下情况下变为非活动状态:

  • 用户从当前应用程序切换到另一应用程序。

  • 应用程序关闭后。

通过处理 Application.Deactivated 事件,可以检测到某个应用程序在何时变为非活动状态。

以下代码演示如何处理 ActivatedDeactivated 事件,以确定应用程序是否处于活动状态。

<Application 
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  x:Class="SDKSample.App"
  StartupUri="MainWindow.xaml"
  Activated="App_Activated" 
  Deactivated="App_Deactivated" />
using System;
using System.Windows;

namespace SDKSample
{
    public partial class App : Application
    {
        bool isApplicationActive;

        void App_Activated(object sender, EventArgs e)
        {
            // Application activated
            this.isApplicationActive = true;
        }

        void App_Deactivated(object sender, EventArgs e)
        {
            // Application deactivated
            this.isApplicationActive = false;
        }
    }
}

Imports System.Windows

Namespace SDKSample
    Partial Public Class App
        Inherits Application
        Private isApplicationActive As Boolean

        Private Sub App_Activated(ByVal sender As Object, ByVal e As EventArgs)
            ' Application activated
            Me.isApplicationActive = True
        End Sub

        Private Sub App_Deactivated(ByVal sender As Object, ByVal e As EventArgs)
            ' Application deactivated
            Me.isApplicationActive = False
        End Sub
    End Class
End Namespace

也可以激活和停用 Window。 有关更多信息,请参见Window.ActivatedWindow.Deactivated

注意

对于 XBAP,既不会引发 Application.Activated,也不会引发 Application.Deactivated

应用程序关闭

应用程序的生存期会在其关闭时结束,出现这一情况可能是因为:

  • 用户关闭了所有 Window

  • 用户关闭了主 Window

  • 用户通过注销或关闭操作结束了 Windows 会话。

  • 满足了特定于应用程序的条件。

为了帮助管理应用程序的关闭操作,Application 提供了 Shutdown 方法、ShutdownMode 属性以及 SessionEndingExit 事件。

注意

Shutdown 只能通过拥有 UIPermission 的应用程序来调用。 独立 WPF 应用程序始终拥有这一权限。 但是,在 Internet 区域部分信任安全沙盒中运行的 XBAP 没有这一权限。

关闭模式

大多数应用程序会在所有窗口都关闭后或在主窗口关闭后关闭。 但是,有时,应用程序会在何时关闭取决于特定于应用程序的其他条件。 通过使用以下某一 ShutdownMode 枚举值来设置 ShutdownMode,可以指定应用程序关闭的条件:

ShutdownMode 的默认值为 OnLastWindowClose,该值意味着应用程序会在用户关闭其最后一个窗口时关闭。 但是,如果应用程序应该在主窗口关闭时关闭,且 ShutdownMode 已设置为 OnMainWindowClose,则 WPF 会自动实施这一关闭操作。 这在下面的示例中显示。

<Application
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    x:Class="SDKSample.App"
    ShutdownMode="OnMainWindowClose" />

当存在特定于应用程序的关闭条件时,ShutdownMode 应设置为 OnExplicitShutdown。 在这种情况下,必须通过显式调用 Shutdown 方法来关闭应用程序;否则,即使所有窗口都已关闭,应用程序仍将继续运行。 请注意,当 ShutdownModeOnLastWindowCloseOnMainWindowClose 时,Shutdown 会被隐式调用。

注意

ShutdownMode 可以通过 XBAP 来设置,但该设置会被忽略;XBAP 总是会在导航离开某个浏览器时或在托管该 XBAP 的浏览器关闭时关闭。 有关详细信息,请参阅 导航概述

会话结束

ShutdownMode 属性描述的关闭条件特定于应用程序。 不过,在某些情况下,应用程序可能会因外部条件而关闭。 最常见的外部条件就是用户通过以下操作结束 Windows 会话时:

  • 注销

  • 关闭

  • 正在重启

  • 休眠

若要检测 Windows 会话在何时结束,可以按以下示例所示来处理 SessionEnding 事件。

<Application 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    x:Class="SDKSample.App"
    StartupUri="MainWindow.xaml"
    SessionEnding="App_SessionEnding" />
using System.Windows;

namespace SDKSample
{
    public partial class App : Application
    {
        void App_SessionEnding(object sender, SessionEndingCancelEventArgs e)
        {
            // Ask the user if they want to allow the session to end
            string msg = string.Format("{0}. End session?", e.ReasonSessionEnding);
            MessageBoxResult result = MessageBox.Show(msg, "Session Ending", MessageBoxButton.YesNo);

            // End session, if specified
            if (result == MessageBoxResult.No)
            {
                e.Cancel = true;
            }
        }
    }
}

Imports System.Windows

Namespace SDKSample
    Partial Public Class App
        Inherits Application
        Private Sub App_SessionEnding(ByVal sender As Object, ByVal e As SessionEndingCancelEventArgs)
            ' Ask the user if they want to allow the session to end
            Dim msg As String = String.Format("{0}. End session?", e.ReasonSessionEnding)
            Dim result As MessageBoxResult = MessageBox.Show(msg, "Session Ending", MessageBoxButton.YesNo)

            ' End session, if specified
            If result = MessageBoxResult.No Then
                e.Cancel = True
            End If
        End Sub
    End Class
End Namespace

在此示例中,代码会检查 ReasonSessionEnding 属性,以确定 Windows 会话的结束方式。 它会根据该值向用户显示确认消息。 如果用户不希望会话结束,这段代码会将 Cancel 设置为 true,以防止 Windows 会话结束。

注意

对于 XBAP,不会引发 SessionEnding

退出

应用程序在关闭时可能需要执行一些最终处理,如保持应用程序状态。 在这类情况下,可以像以下示例中的 App_Exit 事件处理程序那样处理 Exit 事件。 它在 App.xaml 文件中定义为事件处理程序。 其实现在 App.xaml.cs 和 Application.xaml.vb 文件中突出显示。

<Application
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    x:Class="SDKSample.App"
    StartupUri="MainWindow.xaml" 
    Startup="App_Startup" 
    Exit="App_Exit">
    <Application.Resources>
        <SolidColorBrush x:Key="ApplicationScopeResource" Color="White"></SolidColorBrush>
    </Application.Resources>
</Application>
using System.Windows;
using System.IO;
using System.IO.IsolatedStorage;

namespace SDKSample
{
    public partial class App : Application
    {
        string filename = "App.txt";

        public App()
        {
            // Initialize application-scope property
            this.Properties["NumberOfAppSessions"] = 0;
        }

        private void App_Startup(object sender, StartupEventArgs e)
        {
            // Restore application-scope property from isolated storage
            IsolatedStorageFile storage = IsolatedStorageFile.GetUserStoreForDomain();
            try
            {
                using (IsolatedStorageFileStream stream = new IsolatedStorageFileStream(filename, FileMode.Open, storage))
                using (StreamReader reader = new StreamReader(stream))
                {
                    // Restore each application-scope property individually
                    while (!reader.EndOfStream)
                    {
                        string[] keyValue = reader.ReadLine().Split(new char[] {','});
                        this.Properties[keyValue[0]] = keyValue[1];
                    }
                }
            }
            catch (FileNotFoundException ex)
            {
                // Handle when file is not found in isolated storage:
                // * When the first application session
                // * When file has been deleted
            }
        }

        private void App_Exit(object sender, ExitEventArgs e)
        {
            // Persist application-scope property to isolated storage
            IsolatedStorageFile storage = IsolatedStorageFile.GetUserStoreForDomain();
            using (IsolatedStorageFileStream stream = new IsolatedStorageFileStream(filename, FileMode.Create, storage))
            using (StreamWriter writer = new StreamWriter(stream))
            {
                // Persist each application-scope property individually
                foreach (string key in this.Properties.Keys)
                {
                    writer.WriteLine("{0},{1}", key, this.Properties[key]);
                }
            }
        }
    }
}
Imports System.IO
Imports System.IO.IsolatedStorage

Namespace SDKSample
    Partial Public Class App
        Inherits Application
        Private filename As String = "App.txt"

        Public Sub New()
            ' Initialize application-scope property
            Me.Properties("NumberOfAppSessions") = 0
        End Sub

        Private Sub App_Startup(ByVal sender As Object, ByVal e As StartupEventArgs)
            ' Restore application-scope property from isolated storage
            Dim storage As IsolatedStorageFile = IsolatedStorageFile.GetUserStoreForDomain()
            Try
                Using stream As New IsolatedStorageFileStream(filename, FileMode.Open, storage)
                Using reader As New StreamReader(stream)
                    ' Restore each application-scope property individually
                    Do While Not reader.EndOfStream
                        Dim keyValue() As String = reader.ReadLine().Split(New Char() {","c})
                        Me.Properties(keyValue(0)) = keyValue(1)
                    Loop
                End Using
                End Using
            Catch ex As FileNotFoundException
                ' Handle when file is not found in isolated storage:
                ' * When the first application session
                ' * When file has been deleted
            End Try
        End Sub

        Private Sub App_Exit(ByVal sender As Object, ByVal e As ExitEventArgs)
            ' Persist application-scope property to isolated storage
            Dim storage As IsolatedStorageFile = IsolatedStorageFile.GetUserStoreForDomain()
            Using stream As New IsolatedStorageFileStream(filename, FileMode.Create, storage)
            Using writer As New StreamWriter(stream)
                ' Persist each application-scope property individually
                For Each key As String In Me.Properties.Keys
                    writer.WriteLine("{0},{1}", key, Me.Properties(key))
                Next key
            End Using
            End Using
        End Sub
    End Class
End Namespace

有关完整示例,请参阅跨应用程序会话保持和还原应用程序范围的属性

Exit 可由独立应用程序和 XBAP 处理。 对于 XBAP,在以下情况下会引发 Exit

  • 导航离开 XBAP。

  • 在 Internet Explorer 中,关闭托管 XBAP 的选项卡时。

  • 关闭浏览器时。

退出代码

在大多数情况下,应用程序由操作系统根据用户的请求来启动。 但是,应用程序也可由另一应用程序启动,以执行某项特定任务。 当被启动的应用程序关闭时,执行启动操作的应用程序可能想要了解导致被启动应用程序关闭的条件。 在这类情况下,Windows 允许应用程序在关闭时返回一个应用程序退出代码。 默认情况下,WPF 应用程序返回的退出代码值为 0。

注意

从 Visual Studio 调试时,应用程序退出代码显示在“输出”窗口中,当应用程序关闭时,显示在如下所示的一则消息中:

The program '[5340] AWPFApp.vshost.exe: Managed' has exited with code 0 (0x0).

通过单击“视图”菜单中的“输出”,可打开“输出”窗口。

若要更改退出代码,可以调用 Shutdown(Int32) 重载,以接受整数参数作为退出代码:

// Shutdown and return a non-default exit code
Application.Current.Shutdown(-1);
' Shutdown and return a non-default exit code
Application.Current.Shutdown(-1)

通过处理 Exit 事件,可以检测和更改退出代码的值。 系统会向 Exit 事件处理程序传递 ExitEventArgs,该参数提供对具有 ApplicationExitCode 属性的退出代码的访问权限。 有关详细信息,请参阅 Exit

注意

退出代码可以在独立应用程序和 XBAP 中进行设置。 但是,对于 XBAP,退出代码值会被忽略。

未经处理的异常

有时,应用程序可能会在异常条件下关闭,如引发意外异常时。 在这种情况下,应用程序可能没有可用于检测和处理异常的代码。 这类异常就是未经处理的异常;应用程序会在关闭之前显示一个与下图所示内容类似的通知。

Screenshot that shows an unhandled exception notification.

从用户体验的角度来看,应用程序最好通过执行以下部分或全部操作来避免这种默认行为:

  • 显示对用户友好的信息。

  • 尝试让应用程序保持运行。

  • 在 Windows 事件日志中记录详尽且对开发人员友好的异常信息。

这一支持能否实现取决于能否检测到会引发 DispatcherUnhandledException 事件的未经处理的异常。

<Application
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  x:Class="SDKSample.App"
  StartupUri="MainWindow.xaml"
  DispatcherUnhandledException="App_DispatcherUnhandledException" />
using System.Windows;
using System.Windows.Threading;

namespace SDKSample
{
    public partial class App : Application
    {
        void App_DispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e)
        {
            // Process unhandled exception

            // Prevent default unhandled exception processing
            e.Handled = true;
        }
    }
}
Imports System.Windows
Imports System.Windows.Threading

Namespace SDKSample
    Partial Public Class App
        Inherits Application
        Private Sub App_DispatcherUnhandledException(ByVal sender As Object, ByVal e As DispatcherUnhandledExceptionEventArgs)
            ' Process unhandled exception

            ' Prevent default unhandled exception processing
            e.Handled = True
        End Sub
    End Class
End Namespace

系统会向 DispatcherUnhandledException 事件处理程序传递 DispatcherUnhandledExceptionEventArgs 参数,该参数包含与未经处理的异常相关的上下文信息,其中包括异常本身 (DispatcherUnhandledExceptionEventArgs.Exception)。 这些信息可用来确定如何处理异常。

处理 DispatcherUnhandledException 时,应将 DispatcherUnhandledExceptionEventArgs.Handled 属性设置为 true;否则,WPF 仍会将异常视为未经处理并恢复为上文所述的默认行为。 如果引发了未经处理的异常且未处理 DispatcherUnhandledException 事件,或是处理了该事件但 Handled 设置为 false,那么应用程序会立即关闭。 此外,不会引发其他 Application 事件。 因此,如果应用程序含有在关闭前必须要运行的代码,则需处理 DispatcherUnhandledException

正如下一节所述,虽然应用程序可能会因未经处理的异常而关闭,但通常都是应用户请求而关闭的。

应用程序生存期事件

独立应用程序和 XBAP 的生存期并非完全相同。 下图展示了独立应用程序生存期内的各个关键事件及其引发顺序。

Standalone Application - Application Object Events

同样地,下图展示了 XBAP 生存期内的各个关键事件及其引发顺序。

XBAP - Application Object Events

另请参阅