应用程序管理概述

更新: 2008 年 7 月

本主题概述用于创建和管理应用程序的 Windows Presentation Foundation (WPF) 服务。WPF 应用程序的内核是 Application 类,它支持各种核心应用程序服务。本主题介绍其中最重要的一些服务。

本主题包括下列各节。

  • 应用程序类
  • 应用程序定义
  • 获取当前的应用程序
  • 应用程序生存期
  • 其他应用程序服务
  • 相关主题

应用程序类

应用程序由很多个特定于应用程序的元素组成,其中包括用户界面 (UI)、业务逻辑、数据访问逻辑、控件以及数据。应用程序不同,这些元素通常也不同。但是,所有应用程序往往都具有一组共同的功能,便于进行应用程序实现和管理。在 WPF 中,这组共同的应用程序范围功能由 Application 类封装,该类提供下列服务:

  • 创建和管理公共应用程序基础结构。

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

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

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

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

  • 返回退出代码。

  • 管理独立应用程序中的窗口(请参见 WPF Windows 概述)。

  • 跟踪和管理导航(请参见导航概述)。

若要在应用程序中使用这些服务,您需要使用 Application 类来实现应用程序定义。

应用程序定义

WPF 应用程序定义是一个从 Application 派生的类,并且使用特殊的 Microsoft Build Engine (MSBuild) 设置进行配置。

实现应用程序定义

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

下面的示例演示如何结合使用标记与代码隐藏来实现应用程序定义:

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

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

若要使标记文件和代码隐藏文件能够配合工作,需要满足下列条件:

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

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

说明:

在使用 Microsoft Visual Studio 创建新的 WPF 应用程序项目或 WPF 浏览器应用程序项目时,默认情况下会将应用程序定义包括在内并结合使用标记和代码隐藏来对其进行定义。

此类代码是实现应用程序定义的最低要求。但是,若要生成并运行应用程序,需要对应用程序定义进行额外的 MSBuild 配置。

针对 MSBuild 配置应用程序定义

独立应用程序和 XAML 浏览器应用程序 (XBAP) 要求实现某种级别的基础结构才能运行。该基础结构最重要的部分是入口点。当用户启动应用程序时,操作系统会调用入口点,入口点是一个用于启动应用程序的众所周知的函数。

传统上,根据所用技术的不同,开发人员一直需要自己编写部分或全部此类代码。但是,当应用程序定义的标记文件被配置为 MSBuild ApplicationDefinition 项时,WPF 将为您生成此类代码,如下面的 MSBuild 项目文件中所示:

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

由于代码隐藏文件包含代码,因此按照惯例将它标记为 MSBuild Compile 项。

对应用程序定义的标记和代码隐藏文件应用这些 MSBuild 配置会使 MSBuild 生成如下所示的代码:

using System; // STAThread
using System.Windows; // Application

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()
        {


...


        }
    }
}

生成的代码使用附加的基础结构代码扩充了应用程序定义,这些代码包括入口点方法 Main。STAThreadAttribute 属性被应用于 Main 方法以指示 WPF 应用程序的主 UI 线程是一个 STA 线程,此线程是 WPF 应用程序所必需的。当 Main 被调用时,它将创建 App 的一个新实例,然后调用 InitializeComponent 方法来注册事件并设置在标记中实现的属性。因为系统已为您生成 InitializeComponent,所以您无需像对 PageWindow 实现所做的那样从应用程序定义中显式调用 InitializeComponent。最后,调用 Run 方法以启动应用程序。

获取当前的应用程序

由于在整个应用程序中共享 Application 类的服务,因此对于每个 AppDomain,可以只有一个 Application 类实例。为了实施这一点,系统将 Application 类实现为单一实例类(请参见 Implementing Singleton in C#(在 C# 中实现单一实例),该类使用 static Current 属性创建自身的单一实例并提供对该实例的共享访问。

下面的代码显示对于当前的 AppDomain,如何获得对 Application 对象的引用。

// Get current application
Application current = App.Current;

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

// Get strongly-typed current application
App app = (App)App.Current;

可以在 Application 对象生存期中的任何时刻检查 Current 的值。但您应该十分谨慎。在实例化 Application 类之后, Application 对象的状态会在一段时间内不一致。在此时间段内,Application 执行代码运行所需的各种初始化任务,其中包括建立应用程序基础结构、设置属性以及注册事件。如果您在此期间尝试使用 Application 对象,代码可能会有意外的结果,特别是当代码依赖于设置各种 Application 属性时。

Application 完成其初始化任务后,其生存期才真正开始。

应用程序生存期

WPF 应用程序生存期由 Application 所引发的几个事件来标记,这些事件告诉您应用程序何时启动、何时激活和停用以及何时关闭。

初始屏幕

当在 .NET Framework 3.5 SP1 中启动时,您可以指定要在启动窗口中使用的图像或“初始屏幕”。通过使用 SplashScreen 类,可以在应用程序加载时轻松地显示启动窗口。调用 Run 之前将创建和显示 SplashScreen 窗口。有关更多信息,请参见应用程序启动时间如何:将初始屏幕添加到 WPF 应用程序

启动应用程序

在调用 Run 并且初始化应用程序之后,就可以运行应用程序了。此时刻表示 Startup 事件的引发时间:

using System.Windows; // Application, StartupEventArgs, WindowState

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


...


        }
    }
}

在应用程序生存期中的这一时刻,所要做的最常见的一件事情是显示 UI。

显示用户界面

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

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

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

默认情况下,在独立应用程序中实例化的第一个 Window 成为主应用程序窗口。此 Window 对象由 Application.MainWindow 属性引用。如果要使用与实例化的第一个 Window 不同的窗口作为主窗口,可以使用编程方式更改 MainWindow 属性的值。

当 XBAP 首次启动时,它很可能导航到 Page。下面的代码对此进行演示。

如果您处理 Startup 的目的只是为了打开 Window 或导航到 Page,则可以改为在标记中设置 StartupUri 属性。

下面的示例演示如何从独立应用程序中使用 StartupUri 以打开 Window

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

下面的示例演示如何从 XBAP 中使用 StartupUri 以导航到 Page

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

此标记与前面用于打开窗口的代码具有相同的效果。

说明:

有关导航的更多信息,请参见导航概述

如果需要使用非默认的构造函数来实例化 Window,或者需要在显示它之前设置它的属性或订阅它的事件,则需要处理 Startup 事件来打开它。

处理命令行参数

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

wpfapplication.exe /StartMinimized

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

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

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();
        }
    }
}

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

有关演示更可靠的使用正则表达式的命令行分析技术的示例,请参见处理命令行参数的示例

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

激活与停用应用程序

用户使用 Windows 可以在应用程序之间进行切换。最常用的方法是使用 Alt+Tab 组合键。仅当应用程序具有用户可选择的可见的 Window 时,才能切换到该应用程序。当前选定的 Window 是“活动窗口”(也称为前台窗口),并且是接收用户输入的 Window。具有活动窗口的应用程序为“活动应用程序”(即前台应用程序)。在下列情况下,应用程序将成为活动应用程序:

  • 应用程序启动并显示一个 Window

  • 用户通过在应用程序中选择一个 Window 从另一个应用程序切换过来。

可以通过处理 Application.Activated 事件来检测应用程序何时成为活动应用程序。

与此类似,在下列情况下,应用程序成为非活动应用程序:

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

  • 应用程序关闭。

可以通过处理 Application.Deactivated 事件来检测应用程序何时成为非活动应用程序。

下面的代码演示如何处理 ActivatedDeactivated 事件以确定应用程序是否为活动应用程序。

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

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;
        }
    }
}

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

说明:

Application.ActivatedApplication.Deactivated 都不会对 XBAP 引发。

应用程序关闭

应用程序关闭时,其生存期就结束了。应用程序可能由于下列原因而关闭:

  • 用户关闭了所有 Window

  • 用户关闭了主 Window

  • 用户通过注销或关闭来终止 Windows 会话。

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

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

说明:

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

关闭模式

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

ShutdownMode 的默认值为 OnLastWindowClose,该值表示应用程序将在用户关闭应用程序中的最后一个窗口时自动关闭。但是,如果应用程序应当在主窗口关闭时关闭,并且您将 ShutdownMode 设置为 OnMainWindowClose,那么 WPF 会自动关闭。下面的示例对此进行演示。

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

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

说明:

可以从 XBAP 中设置 ShutdownMode,但此设置将被忽略;当在浏览器中通过导航而离开 XBAP 时,或者当承载 XBAP 的浏览器关闭时,将总是关闭该应用程序。有关更多信息,请参见导航概述

会话终止

ShutdownMode 属性所描述的关闭条件是特定于应用程序的。但在某些情况下,应用程序可能会由于外部条件而关闭。最常见的外部条件出现在用户通过以下操作终止 Windows 会话时:

  • 注销

  • 关机

  • 重新启动

  • 休眠

若要检测 Windows 会话的终止时间,可以处理 SessionEnding 事件,如下面的示例所示。

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

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;
            }
        }
    }
}

在此示例中,代码检查 ReasonSessionEnding 属性以确定 Windows 会话的结束方式。代码使用该值向用户显示一个确认消息。如果用户不希望终止会话,则代码会将 Cancel 设置为 true 以阻止 Windows 会话终止。

说明:

SessionEnding 不会对 XBAP 引发。

退出

当应用程序关闭时,它可能需要执行一些最终处理,例如保存应用程序状态。对于这些情况,您可以处理 Exit 事件。

<Application
    xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
    x:Class="SDKSample.App"
    StartupUri="MainWindow.xaml" 
    Startup="App_Startup" 
    Exit="App_Exit">


...


</Application>
using System.Windows; // Application, StartupEventArgs
using System.IO; // StreamReader, FileMode
using System.IO.IsolatedStorage; // IsolatedStorageFile, IsolatedStorageFileStream

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

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

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

  • 离开一个 XBAP。

  • 在 Internet Explorer 7 中,关闭了承载 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);

可以通过处理 Exit 事件来检测和更改退出代码的值。Exit 事件处理程序接受一个传递给它的 ExitEventArgs,该参数通过 ApplicationExitCode 属性提供对退出代码的访问。有关更多信息,请参见 Exit

说明:

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

未处理的异常

有时,应用程序可能在非正常条件下关闭,例如当引发意外的异常时。在此情况下,应用程序可能没有代码来检测和处理异常。这种类型的异常是未处理的异常;在应用程序关闭之前,将显示一个如下图所示的通知。

未处理的异常通知

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

  • 显示用户友好的信息。

  • 尝试使应用程序继续运行。

  • 在 Windows 事件日志中以开发人员易于理解的方式详细记录异常信息。

实现此支持的前提是能够检测到未处理的异常(对于该异常将引发 DispatcherUnhandledException 事件)。

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

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;
        }
    }
}
说明:

有关如何处理 DispatcherUnhandledException 的更详细的示例,请参见未处理的应用程序异常示例

DispatcherUnhandledException 事件处理程序接受一个传递给它的 DispatcherUnhandledExceptionEventArgs 参数,该参数包含关于未处理异常的上下文信息,其中包括异常本身 (DispatcherUnhandledExceptionEventArgs.Exception)。使用该信息可以确定如何处理异常。

在处理 DispatcherUnhandledException 时,应当将 DispatcherUnhandledExceptionEventArgs.Handled 属性设置为 true;否则,WPF 仍会将异常视为未处理的异常,并恢复前面描述的默认行为。如果引发未处理的异常,并且或者 DispatcherUnhandledException 事件未被处理,或者该事件被处理但 Handled 被设置为 false,则应用程序会立即关闭。而且,不会再引发其他 Application 事件。因此,如果应用程序具有必须在应用程序关闭之前运行的代码,则需要处理 DispatcherUnhandledException

尽管应用程序可能会由于未处理的异常而关闭,但应用程序通常是为响应用户请求而关闭的,如下一节所述。

应用程序生存期事件

独立应用程序和 XBAP 的生存期不完全相同。下图说明了独立应用程序的生存期中的关键事件,并显示这些事件的引发顺序。

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

同样,下图说明了 XBAP 的生存期中的关键事件,并显示这些事件的引发顺序。

XBAP – 应用程序对象事件

其他应用程序服务

除了管理应用程序的生存期以外,Application 还提供其他一些服务,包括:

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

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

  • 应用程序资源、内容和源站点数据文件。

  • 窗口管理。

  • 导航管理。

共享的应用程序范围属性

应用程序提供 Properties 属性来公开可以在应用程序范围内共享的状态。下面提供了一个使用 Properties 的示例:

// Set an application-scope property with a custom type
CustomType customType = new CustomType();
Application.Current.Properties["CustomType"] = customType;


...


// Get an application-scope property
// NOTE: Need to convert since Application.Properties is a dictionary of System.Object
CustomType customType = (CustomType)Application.Current.Properties["CustomType"];

有关更多信息,请参见以下内容:

共享的应用程序范围资源

应用程序提供了 Resources 属性,以使开发人员可以在应用程序内共享 UI 资源。下面提供了一个使用 Properties 的示例:

// Set an application-scope resource
Application.Current.Resources["ApplicationScopeResource"] = Brushes.White;


...


// Get an application-scope resource
Brush whiteBrush = (Brush)Application.Current.Resources["ApplicationScopeResource"];

有关更多信息,请参见以下内容:

应用程序资源、内容和源站点数据文件

WPF 应用程序可以管理多种类型的非代码数据文件,包括资源文件、内容文件和源站点文件。下面的帮助器方法可用于加载这些类型的数据文件:

窗口管理

ApplicationWindow 之间具有密切关系。正如您所看到的,应用程序的生存期可能取决于其窗口的生存期,如 ShutdownMode 属性中所指定的那样。Application 记录哪个窗口被指定为主应用程序窗口 (Application.MainWindow),并维护当前已实例化的窗口的列表 (Application.Windows)。

有关更多信息,请参见 WPF Windows 概述

导航管理

对于具有导航功能(使用 NavigationWindowFrame)的独立应用程序或 XBAP,Application 检测应用程序内的任何导航操作,并在适当的时候引发下列事件:

此外,Application 使任何类型的应用程序都能够使用 GetCookieSetCookie 来创建、保存和检索 Cookie。

有关更多信息,请参见导航概述

请参见

概念

WPF Windows 概述

导航概述

Windows Presentation Foundation 应用程序资源、内容和数据文件

参考

Application

修订记录

日期

修订记录

原因

2008 年 7 月

添加了关于使用初始屏幕的一节。

SP1 功能更改。