应用程序管理概述

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

应用程序类

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

  • 跟踪和与应用程序生命周期交互。

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

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

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

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

  • 跟踪和管理导航。

如何使用应用程序类执行常见任务

如果您对 Application 类的所有详细信息不感兴趣,下表列出了 Application 的一些常见任务以及如何完成它们。 通过查看相关的 API 和主题,可以找到详细信息和示例代码。

任务 方法
获取表示当前应用程序的对象 使用 Application.Current 属性。
将启动屏幕添加到应用程序 请参阅 向 WPF 应用程序添加初始屏幕
启动应用程序 使用 Application.Run 方法。
停止应用程序 使用Shutdown对象的Application.Current方法。
从命令行获取参数 处理Application.Startup事件并使用StartupEventArgs.Args属性。 有关示例,请参阅 Application.Startup 事件。
获取和设置应用程序退出代码 ExitEventArgs.ApplicationExitCodeApplication.Exit事件处理程序中设置属性或调用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 创建一个 partial 类,该类派生自 Application,并具有由 x:Class 属性指定的名称。 这需要为 XAML 架构添加 XML 命名空间声明(xmlns:x="http://schemas.microsoft.com/winfx/2006/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 的插件。 有关详细信息,请参阅 有关 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

生成的代码使用其他基础结构代码(包括入口点方法 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)

可以在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" />

以下示例演示如何使用StartupUri从一个XBAP跳转到Page

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

此标记与打开窗口的上一个代码的效果相同。

注释

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

若需要通过非无参数构造函数实例化Window,或需要在显示前设置其属性或订阅其事件,或需要处理启动应用程序时提供的任何命令行参数,则需要处理Startup事件。

处理 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命令行参数;如果是,主窗口则会以MinimizedWindowState方式打开。 请注意,由于 WindowState 属性必须以编程方式设置,因此必须在代码中显式打开 main 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

还可以激活和停用 A Window 。 有关详细信息,请参阅 Window.ActivatedWindow.Deactivated

注释

XBAP 既不引发 Application.Activated ,也不引发 Application.Deactivated

应用程序关闭

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

  • 用户关闭了所有 Window

  • 用户关闭主 Window

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

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

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

注释

Shutdown 只能从具有 UIPermission. 的应用程序调用 。 独立 WPF 应用程序始终具有此权限。 但是,在 Internet 区域部分信任安全沙盒中运行的 XBAP 不会。

关闭模式

应用程序通常在所有窗口关闭或者主窗口关闭时关闭。 但是,有时,其他特定于应用程序的条件可能会确定应用程序何时关闭。 您可以通过设置ShutdownMode为以下ShutdownMode枚举值之一来指定应用程序关闭的条件:

默认值 ShutdownModeOnLastWindowClose,这意味着当用户关闭应用程序的最后一个窗口时,应用程序会自动关闭。 但是,如果应用程序需要在主窗口关闭时关闭,只要您将 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 的浏览器关闭时,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 会话的结束方式。 它使用此值向用户显示确认消息。 如果用户不希望会话结束,则代码将设置为Canceltrue阻止 Windows 会话结束。

注释

SessionEnding 不针对 XBAPs 触发。

出口

当应用程序关闭时,可能需要执行一些最终处理,例如保留应用程序状态。 对于这些情况,可以像以下示例中的App_Exit事件处理程序那样处理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 事件来检测退出代码的值并对其进行更改。 Exit事件处理程序接收到一个ExitEventArgs,该ExitEventArgs提供了通过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 - 应用程序对象事件

另请参阅