应用程序管理概述

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

应用程序类

在 WPF 中,常见的应用程序范围功能封装在 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-Scope 资源字典
获取和设置应用程序范围内的属性 使用 Application.Properties 属性。
获取和保存应用程序的状态 请参阅 持久化和还原 Application-Scope 跨应用程序会话的属性。
管理非代码数据文件,包括资源文件、内容文件和源站点文件。 请参阅 WPF 应用程序资源、内容和数据文件
在独立应用程序中管理窗口 请参阅 WPF Windows 概述
跟踪和管理导航 请参阅 导航概述

应用程序定义

若要利用 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")添加 XML 命名空间声明。

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

注意

使用 Visual Studio 创建新的 WPF 应用程序项目或 WPF 浏览器应用程序项目时,默认会包含一个应用程序定义,并使用标记语言和后台代码进行定义。

此代码是实现应用程序定义所需的最低代码。 但是,在生成和运行应用程序之前,需要对应用程序定义进行额外的 MSBuild 配置。

为 MSBuild 配置应用程序定义

独立应用程序和 XAML 浏览器应用程序(XBAP)需要实现特定级别的基础结构,然后才能运行。 此基础结构最重要的部分是入口点。 当用户启动应用程序时,操作系统将调用入口点,这是启动应用程序的已知函数。

警告

XBAP 要求旧版浏览器运行,例如 Internet Explorer 和旧版 Firefox。 这些较旧的浏览器通常在 Windows 10 和 Windows 11 上不受支持。 由于安全风险,新式浏览器不再支持 XBAP 应用所需的技术。 不再支持启用 XBAP 的插件。 有关详细信息,请参阅 有关 WPF 浏览器托管应用程序(XBAP)的常见问题解答。

传统上,开发人员需要自行编写部分或全部代码,具体取决于技术。 但是,当应用程序定义的标记文件配置为 MSBuild ApplicationDefinition 项时,WPF 将生成此代码,如以下 MSBuild 项目文件中所示:

<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

生成的代码使用其他基础结构代码来增强应用程序定义,其中包括入口点方法 MainSTAThreadAttribute 属性应用于 Main 方法,以指示 WPF 应用程序的主 UI 线程是 WPF 应用程序所需的 STA 线程。 调用时,Main 在调用 InitializeComponent 方法以注册事件并设置在标记中实现的属性之前创建 App 的新实例。 由于 InitializeComponent 为你生成,因此无须像对 PageWindow 实现那样,从应用程序定义中显式调用 InitializeComponent。 最后,调用 Run 方法以启动应用程序。

获取当前应用程序

由于 Application 类的功能在应用程序中共享,因此每个 AppDomain只能有一个 Application 类的实例。 若要强制实施此操作,Application 类将实现为单一实例类(请参阅 在 C#中实现单一实例),该类创建自身的单个实例,并使用 staticCurrent 属性提供对其共享访问权限。

以下代码演示如何获取对当前 AppDomainApplication 对象的引用。

// 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)

可以在 Application 对象的生存期内的任何时间点检查 Current 的值。 但是,你应该小心。 实例化 Application 类后,存在 Application 对象状态不一致的时间段。 在此期间,Application 执行代码运行所需的各种初始化任务,包括建立应用程序基础结构、设置属性和注册事件。 如果在此期间尝试使用 Application 对象,则代码可能会产生意外的结果,尤其是在它依赖于所设置的各种 Application 属性时。

Application 完成其初始化工作时,其生存期将真正开始。

应用程序生存期

WPF 应用程序的生命周期通过 Application 触发的多个事件进行标记,以通知您应用程序何时启动、被激活和停用,以及何时已关闭。

启动画面

从 .NET Framework 3.5 SP1 开始,您可以指定在启动窗口或 启动屏幕使用的图像。 使用 SplashScreen 类,可以轻松地在加载应用程序时显示启动窗口。 在调用 Run 之前,将创建并显示 SplashScreen 窗口。 有关详细信息,请参阅 应用程序启动时间,以及 为 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>

在应用程序的生命周期内,最常见的做法是显示用户界面。

显示用户界面

大多数独立 Windows 应用程序在开始运行时会打开一个 WindowStartup 事件处理程序是一个可从中执行此操作的位置,如以下代码所示。

<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

处理 Command-Line 参数

在 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.ActivatedApplication.Deactivated

应用程序关闭

应用程序关闭后,应用程序的生命期将结束,原因如下:

  • 用户关闭每个 Window

  • 用户关闭了主要部分 Window

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

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

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

注意

只能从具有 UIPermission的应用程序调用 Shutdown。 独立 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

注意

可以从 XBAP 设置 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

退出

当应用程序关闭时,可能需要执行一些最终处理,例如保留应用程序状态。 对于这些情况,可以处理 Exit 事件,如以下示例中 App_Exit 事件处理程序所做的那样。 它在 App.xaml 文件中定义为事件处理程序。 其实现已在 App.xaml.csApplication.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

有关完整示例,请参阅 持久化和还原 Application-Scope 属性 跨应用程序会话

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 事件来检测退出代码的值并对其进行更改。 将 ExitEventArgs 传递给 Exit 事件处理程序,该对象通过 ApplicationExitCode 属性提供对退出代码的访问权限。 有关详细信息,请参阅 Exit

注意

可以在独立应用程序和 XBAP 中设置退出代码。 但是,XBAP 将忽略退出代码值。

未经处理的异常

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

显示未经处理的异常通知的屏幕截图。

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

  • 显示用户友好信息。

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

  • 在 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 的生存期不完全相同。 下图说明了独立应用程序生命周期内的关键事件,并显示了它们被触发的顺序。

独立应用程序 - 应用程序对象事件

同样,下图展示了 XBAP 生命周期内的关键事件,并显示它们触发的顺序。

XBAP - 应用程序对象事件

另请参阅