다음을 통해 공유


Application Management Overview

This topic provides an overview of these services, starting with an introduction to the Application class, how to define an application, application lifetime, and the additional services provided by Application.

This topic contains the following sections.

  • The Application Class
  • Implementing an Application
  • Configuring the Application Definition for MSBuild
  • Application Lifetime
  • Other Application Services
  • Related Topics

The Application Class

In WPF, an application is encapsulated by the Application class. The Application provides the following fundamental services:

  • Creating and managing common application infrastructure.

  • Tracking and interacting with application lifetime.

  • Retrieving and processing command-line parameters.

  • Sharing application-scope properties and resources.

  • Detecting and responding to unhandled exceptions.

  • Returning exit codes.

  • Managing windows in standalone applications (see Windows Presentation Foundation Windows Overview).

  • Tracking navigation in XAML browser applications (XBAPs), and standalone applications with navigation windows and frames (see Navigation Overview).

Implementing an Application

A typical WPF application is defined using both markup and code-behind. This allows you to use markup to declaratively set application properties, resources, and register events, while handling the events and implementing application-specific behavior in code-behind. The following example shows how to define an application using both markup and code-behind:

<Application
    xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
    x:Class="App" />
using System.Windows;
public partial class App : Application
{
    public App()
    {
        InitializeComponent();
    }
}

To allow a markup file and code-behind file to work together, the following needs to happen:

  • In markup, the Application element must include the x:Class attribute, which instructs MSBuild to create a partial class for the markup file when the project is built with the name specified by the x:Class attribute. This requires the addition of an XML namespace declaration for the XAML schema (xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"). The partial generated partial class implements InitializeComponent, which is called to register the events and set the properties that are implemented in markup.

  • In code-behind, the class must be a partial class with the same name that is specified by the x:Class attribute in markup, and needs to derive from Application. This allows the code-behind file to be associated with the partial class that is generated for the markup file when it is built (see Building a Windows Presentation Foundation Application).

  • In code-behind, the class must be declared as a partial class with the same name as the x:Class attributed in markup. This allows the code-behind file to be associated with the partial class that is generated for the markup file when the project is built (see Building a Windows Presentation Foundation Application).

NoteNote:

When you create a new Windows Application (WPF) project or XAML Browser Application (WPF) project using Microsoft Visual Studio, an application definition is included by default, and is defined using both markup and code-behind.

This minimal application definition is all the code that you need to write to create a working WPF application. However, the application definition needs to be configured for MSBuild in a specific way.

Configuring the Application Definition for MSBuild

All WPF executable applications (standalone and XBAPs) require the implementation of a certain level of infrastructure to be able to run. Primarily, this includes an entry point function, which is a well known function that the operating system calls to start an application. Traditionally, developers have needed to write some degree of this code for themselves, if not all of it. However, WPF will generate this code for by when you configure the application definition markup file as an MSBuild ApplicationDefinition item. The necessary configuration is shown in the following example:

<Project

DefaultTargets="Build"

xmlns="https://schemas.microsoft.com/developer/msbuild/2003">

...

<ApplicationDefinition Include="App.xaml" />

<Compile Include="App.xaml.cs" />

...

</Project>

Here, the code-behind file is configured as an MSBuild Compile item. Furthermore, the code-behind file has the same file name as the markup file, with an additional language-specific suffix (.cs or .vb). The suffix is not required although Microsoft Visual Studio uses this convention by default.

NoteNote:

For in-depth coverage of building WPF applications, see Building a Windows Presentation Foundation Application.

The effect of the combination of application definition and MSBuild configuration causes MSBuild to generate code like the following:

public class App : Application
{
    public App() { }
    [STAThread]
    public static void Main()
    {
        App app = new App();
        app.InitializeComponent();
        app.Run();
    }

    public void InitializeComponent()
    {
        // Register XAML-declared events
        ...
        // Set XAML-declared properties
        ...
    }
}

When this application is launched, Windows will call the entry point function (Main in both C# and Microsoft Visual Basic). In turn, the entry point function creates a new instance of the custom application class before calling InitializeComponent to register the events and set the properties that are implemented in markup.

At most, there can only be one instance of the Application class or subclass per AppDomain. For this reason, and to support shared access to a single set of application-scope properties and resources (discussed later), Application is implemented using the singleton pattern. The singleton pattern is a model for creating classes that can only have one instance, which is known as a singleton ( see Implementing Singleton in C#). A singleton class creates an instance of itself, to which it provides shared access via a static property. For the Application class, this property is Current:

// Get reference to application
App currentApp = (App)Application.Current;

In the generated code, you'll notice that the Run method is called after App is instantiated,; this is the point at which the life of a WPF application begins.

NoteNote:

There are situations where you need to write this code yourself, including when you need to detect that only a single instance of an executable application will run. See Single Instance Detection Sample for more information.

Application Lifetime

After System.Windows.Application.Run is called, an application starts, and may be deactivated and activated many times during its lifetime before it finally stops running in response to either user or programmatic conditions.

Starting an Application

When System.Windows.Application.Run is called, Application establishes a set of infrastructure that is commonly required by all applications. When the infrastructure is in place, Application raises the Startup event, which signifies the moment when a WPF application has started:

<Application
    xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
    x:Class="App"
    Startup="App_Startup" />
public partial class App : Application
{
    void App_Startup(object sender, StartupEventArgs e)
    {
        // Application is running
    }
}

When you implement an application definition as an MSBuild ApplicationDefinition item, the work of launching, running, and establishing application infrastructure is performed on your behalf by WPF. This allows you to focus on building the functionality that is specific to your application's startup needs, which almost always includes showing a UI, and sometimes includes processing command-line arguments.

Showing a User Interface

Because most Windows applications open a user interface when they begin running the Startup event handler is an ideal location to do so. For standalone applications, this involves showing a window:

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

        // Show the main window
        MainWindow mainWindow = new MainWindow();
        mainWindow.Show();
    }
}

If the only reason you handle Startup is to show a window, you can set the StartupUri attribute in markup instead:

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

If the UI of a standalone application is composed entirely of pages (Page), instead of windows, you can also use StartupUri to specify the start page:

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

In this case, Application automatically creates an instance of NavigationWindow and navigates it to the specified page. You could also set StartupUri with a custom NavigationWindow.

In XBAP applications, you specify the first page to be shown in Windows Internet Explorer by using StartupUri too.

With respect to showing a UI, the main reason that you need to handle Startup, instead of setting StartupUri, is to instantiate the class that implements the UI by using a non-default constructor, or to set properties on it before showing it.

You will also need to handle Startup to retrieve and process the command-line arguments for your application.

Processing Command-Line Arguments

In Windows, standalone applications can be launched from a command prompt or the desktop. In both cases, command-line arguments can be passed to the application by using something like the following syntax:

wpfapplication.exe /winstate:maximized

During application initialization, WPF retrieves the command-line arguments from the operating system and passes them to the Startup event handler via the Args property of the StartupEventArgs parameter. You can retrieve and store the command-line arguments using code like the following:

<Application
    xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
    x:Class="App"
    StartupUri="MainWindow.xaml"
    Startup="App_Startup" />
using System;
using System.Collections;
using System.Text.RegularExpressions;
using System.Windows;
public partial class App : Application
{
    // Indexed command line args using hash table
    public static Hashtable CommandLineArgs = new Hashtable();

    void App_Startup(object sender, StartupEventArgs e)
    {
        // Don't bother if no command line args were passed
        // NOTE: e.Args is never null - if no command line args were passed, 
        //       the length of e.Args is 0.
        if (e.Args.Length == 0) return;

        // Parse command line args for args in the following format:
        //   /argname:argvalue /argname:argvalue /argname:argvalue ...
        //
        // Note: This sample uses regular expressions to parse the command line arguments.
        // For regular expressions, see:
        // https://msdn.microsoft.com/library/en-us/cpgenref/html/cpconRegularExpressionsLanguageElements.asp
        string pattern = @"(?<argname>/\w+):(?<argvalue>\w+)";
        foreach (string arg in e.Args)
        {
        Match match = Regex.Match(arg, pattern);

        // If match not found, command line args are improperly formed.
        if (!match.Success) throw new ArgumentException("The command line arguments are improperly formed. Use /argname:argvalue.");

        // Store command line arg and value
        CommandLineArgs[match.Groups["argname"].Value] = match.Groups["argvalue"].Value;
    }
}
NoteNote:

See the Processing Command Line Arguments Sample for more information.

Application Activation and Deactivation

During the lifetime of an application, users may switch between them and other applications that they may currently have running. A user switches to another application by activating one of its windows, at which point the current application becomes deactivated. This situation can be detected by handling Deactivated. Likewise, when a user switches back to the application by selecting one of its windows, the application activates and, consequently, Activated is raised. Both Activated and Deactivated can be handled like os:

<Application 
  xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
  x:Class="App"
  StartupUri="MainWindow.xaml"
  Activated="App_Activated" 
  Deactivated="App_Deactivated" />
using System;
using System.Windows;
public partial class App : Application
{
    bool isApplicationActive;

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

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

If you need to handle per-window activation and deactivation, rather than for the entire application, you can handle System.Windows.Window.Activated and System.Windows.Window.Deactivated instead.

Unhandled Exceptions

While an application is running, application code may thrown an exception that is unanticipated and, potentially, unhandled. By default, .NET Framework 3.0 responds to unhandled exceptions by displaying a notification to the user, shown in the following figure, before shutting the application down.

From the user experience perspective, it is better for an application to avoid the default behavior by doing some or all of the following:

  • Providing a common mechanism for detecting unhandled exceptions.

  • Providing more descriptive information to users.

  • Attempting to keep an application running.

  • Logging descriptive information, typically in the Windows event log.

Implementing this support depends on being able to detect unhandled exceptions, which is what DispatcherUnhandledException is raised for:

<Application 
    xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
    x:Class="App"
    StartupUri="MainWindow.xaml" 
    DispatcherUnhandledException="App_DispatcherUnhandledException" />
public partial class App : Application
{
    void App_DispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e)
    {
        // Process unhandled exception
        ...

        // Prevent default unhandled exception processing
        e.Handled = true;
    }
}

The DispatcherUnhandledException event handler is passed a DispatcherUnhandledExceptionEventArgs parameter that contains contextual information regarding the unhandled exception, including the exception itself (System.Windows.Threading.DispatcherUnhandledExceptionEventArgs.Exception). You can use this information to determine whether an exception is recoverable or not. A recoverable exception might be a FileNotFoundException, for example, while an unrecoverable exception might be a StackOverflowException, for example.

Whenever you handle DispatcherUnhandledException, you should set the System.Windows.Threading.DispatcherUnhandledExceptionEventArgs.Handled property to false; otherwise, WPF still considers the exception to be unhandled shuts down the application, as per the default behavior.

NoteNote:

If an unhandled exception is raised and the DispatcherUnhandledException event is not handled, or the event is handled and Handled is set to false, the application will shut down straight away. Furthermore, no other events that are implemented by Application are raised. Consequently, you will need to handle DispatcherUnhandledException if your application has code that is required to run prior to the application shutting down.

For a more involved sample that demonstrates handling DispatcherUnhandledException, see Unhandled Application Exceptions Sample.

Application Shutdown

When an unhandled exception occurs, the application will shut down if the exception remains unhandled. An application is far more likely to be shut down, though, for the following reasons:

  • All windows have been closed by the user.

  • The main window has been closed by a user.

  • A user ends their Windows session by logging off or shutting idown.

  • An identified end condition has been met.

In all these cases, the Shutdown method is called. Whether Shutdown is called by you or WPF depends on how you configure Application.

Shutdown Mode

In general, the lifetime of a standalone application encapsulates the lifetime of the windows that it shows. Depending on the type of application, the application will shutdown when either all the windows are closed, or when the main window is closed. Because these two scenarios are the most common, you can configure Application to automatically shut down when they happen by setting ShutdownMode to one of the following ShutdownMode enumeration values:

For example, if you wanted to configure your application to shut down when the main window has closed, you would do the following:

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

The main window is the window that is referenced by the System.Windows.Application.MainWindow property. By default, the value of this property is a reference to the first window that is instantiated within an application, although the value can be changed programmatically afterwards.

Setting ShutdownMode specifies when Shutdown is called, and by who. When ShutdownMode is set to either OnLastWindowClose or OnMainWindowClose, WPF will automatically call Shutdown when either of the conditions specified by these enumeration values are set.

If ShutdownMode is set to OnExplicitShutdown, it is your responsibility to call Shutdown, otherwise your application will continue running even if all the windows are closed.

Session Ending

Whereas setting ShutdownMode is an internal mechanism for shutting an application down, applications can also shutdown due to external conditions:

  • When a user ends their session by logging off from Windows.

  • When a user ends their session by shutting down, restarting, or hibernating Windows.

When a user ends their session, Windows gives the opportunity for each currently running application to detect when they do and, if required, prevent the session from ending. Applications that allow users to edit data (like word processors and spread sheets, for example) are most likely to take advantage of this capability; just as a user may accidentally attempt to close a document that they haven't saved, they may accidentally shut down Windows while applications have unsaved documents. Consequently, applications can use this opportunity to check that their application data is saved and, if not, give the user the opportunity to prevent Windows from shutting down.

Application detects when Windows raises the session ending notification and raises the SessionEnding event when it happens. You can handle SessionEnding to detect, respond to, and cancel session ending, as shown in the following example:

<Application
    xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
    x:Class="App"
    SessionEnding="App_SessionEnding" />
public partial class App : Application
{
    void App_SessionEnding(object sender, SessionEndingCancelEventArgs e)
    {
        using (StreamWriter writer = File.AppendText("output.txt"))
        //using (FileStream stream = File.AppendText("output.txt"))
        //using (StreamWriter writer = new StreamWriter(stream))
        {
            writer.WriteLine("OnSessionEnding");
        }

        // Ask the user if they want the session to end
        string msg = "The application is shutting down for the following reason: " + e.ReasonSessionEnding + "\n\nShutdown?";
        string title = "An Application";
        MessageBoxButton buttons = MessageBoxButton.YesNo;
        MessageBoxImage icon = MessageBoxImage.Stop;
        MessageBoxResult result = MessageBox.Show(msg, title, buttons, icon);

        // If they don't, prevent both the session from ending and the 
        // application from shutting down
        e.Cancel = (result == MessageBoxResult.No);
    }
}

The SessionEndingCancelEventArgs parameter that is passed to the SessionEnding event handler exposes the reason for the session to end from the ReasonSessionEnding property, and implements the Cancel property to allow you to cancel the session ending.

Exit

If shutdown is initiated by an application - that is, does not shut down due to unhandled exceptions or when the user session ends - the lifetime of an application will end. Before ending, applications may need to perform final processing such as persisting application state. For these situations, you can handle the Exit event.

<Application
    xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
    x:Class="App"
    StartupUri="MainWindow.xaml" 
    Startup="App_Startup" 
    Exit="App_Exit" />
using System;
using System.Windows;
using System.IO;
using System.IO.IsolatedStorage;
public partial class App : Application
{
    string filename = "App.txt";
    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]);
            }
        }
    }
}

Exit Code

Applications are mostly launched by the operating system in response to a user request. However, an application can be launched by another application to perform some specific task. In this case, both calling and called applications operate in separate processes. One issue with this situation occurs when the calling application's execution depends on how the called application terminated. In these situations, the called application can return a value that indicates how it terminated by using a special integer code known as the exit code. By default, Application will return a default exit code value of 0. To change this value, you can call an overload of Shutdown that accepts an integer argument to be the exit code:

// Shutdown and return a custom exit code
Application.Current.Shutdown(-1);

You can detect the value of the exit code, and change it, by handling the Exit event. The Exit event handler is passed an ExitEventArgs which provides access to the exit code with the ApplicationExitCode property. See Exit for more information.

Application Lifetime Events

The following figure illustrates the key events in the lifetime of an application, and the sequence in which they are raised.

Application diagram

Other Application Services

This topic covers the core lifetime of an application, as exposed by Application. However, Application provides further services that are discussed in more detail elsewhere, including:

  • Application and window management.

  • Application-Scope properties.

  • Application-Scope resources.

  • Application and navigation management.

Application and Windows

Application and Window have a close relationship. As you've seen, the lifetime of an application can depend on the lifetime of its windows, as specified by the ShutdownMode property. Application records which window is designated the main application window (System.Windows.Application.MainWindow), and maintains a list of currently instantiated windows (System.Windows.Application.Windows).

See Windows Presentation Foundation Windows Overview for further information.

Application-Scope Properties

Application implements the Properties property to expose state that can be shared across the breadth of an application. The following provides an example of using 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"];

See the following for more information:

Application-Scope Resources

Application implements the Resources property to allow developers to share user interface (UI) resources across an application. The following provides an example of using Properties:

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

...

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

See the following for more information:

Application and Navigation

For standalone applications with navigation, using NavigationWindow and Frame, or , Application detects any navigation within an application and raises the following events as appropriate:

See Navigation Overview for more information.

Application and Application Data Files

WPF applications can manage several types of data files, including resource data files, content data files, and remote data files. The following helper methods can be used to load these types of data files:

See Also

Reference

Application

Concepts

Windows Presentation Foundation Windows Overview
Navigation Overview
Windows Presentation Foundation Application Data Files