Understanding WPF Application Lifecycle


1. What is the Application Class:

When we run any WPF application, it is represented by an instance of the System.Windows.Application class. The job of this class is to track all open windows in our application, decide when our application will shut down and fires application events that we handle to perform initialization and other work. So every WPF application uses the Application class which is why it's important to understand it.

The System.Windows.Application class plays the same role in a WPF application as the System.Windows.Forms.Application class plays in a Windows Forms application.

2. Application life cycle:

A WPF application undergoes a straight forward life cycle. When we start our application, the application objects are created, and as our application runs, lots of events are fired. Finally, when the application object is released, our application ends.

So let us understand each element involved in this life cycle.

A. Creation of Application objects: To understand this, it's good to write the Application class by ourselves. Below I have written an application entry point (a Main () method) that creates a window named Window1 and fires up a new application. For this create a WPF application and add a class called Startup.cs to write the below code.

public class Startup
    {
        [STAThread]
        static void Main()
        {
            //Create application
            Application app = new Application();
            //Create a main window
            Window1 win = new Window1();
            //Lauch the application and show the window
            app.Run(win);
        }
    }


Explanation of code:

  1. When we pass a window to the Application.Run() method, that window is set as the main window and exposed to our entire application through the Application.MainWindow property.
  2. The Run () method then fires the Application.Startup event and shows the main window.

Well we can accomplish the same result with the other way of coding like below;

// Create the application.
            Application app = new Application();
            // Create, assign, and show the main window.
            Window1 win = new Window1();
            app.MainWindow = win;
            win.Show();
            // Keep the application alive.
            app.Run();

Differences between these two approaches are that in the later approach our application continues running until the main window and every other window is closed. At that point, the Run () method returns, and any additional code in our Main () method is executed before the application winds down.

B: Deriving a Custom Application Class: The above approach is not the pattern which  Visual Studio follows when we create a new WPF application, well instead, Visual Studio derives a custom class from the Application class.

The starting point is a XAML template, which is named App.xaml by default.

<Application x:Class="TestApplication.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="Window1.xaml"

</Application>

The Class attribute is used in XAML to create a class derived from the element. Thus, this class creates a class that derives from Application, with the name TestApplication.App in the above example. The application tag not only creates a custom application class, but it also sets the StartupUri property to identify the XAML document that represents the main window.

As with windows, the application class is defined in two separate portions that are fused together at compile time. The automatically generated portion isn't visible in our project, but it contains the Main () entry point and the code for starting the application. It looks something like this:

public partial class App : Application
    {
        [STAThread()]
        public static void Main()
        {
            TestApplication.App app = new TestApplication.App();
            app.InitializeComponent();
            app.Run();
        }
        public void InitializeComponent()
        {
            this.StartupUri = new Uri("Window1.xaml", System.UriKind.Relative);
        }
    }

If we're really interested in seeing the custom application class that the XAML template creates, look for the App.g.cs file in the obj\Debug folder inside our project directory.

Point to remember: The only difference between the automatically generated code shown here and a custom application class is that we might create on our own is that the automatically generated class uses the StartupUri property instead of setting the MainWindow property or passing the main window as a parameter to the Run() method.

C. Application Shutdown: Ordinarily, the Application class keeps our application alive as long as at least one window is still open. If this isn't the behavior we want, we can adjust the Application.ShutdownMode. If we're instantiating our Application object by hand, we need to set the ShutdownMode property before we call Run ().

Let us understand ShutdownMode enums

  1. OnLastWindowClose: This is the default behavior—our application keeps running as long as there is at least one window in existence. If we close the main window, the Application.MainWindow property still refers to the object that represents the closed window.
     
  2. OnMainWindowClose: our application stays alive only as long as the main window is open. When ShutdownMode is OnMainWindowClose and we close the main window, the Application object will automatically close all the other windows before the Run() method returns.
     
  3. OnExplicitShutdown: The application never ends (even if all the windows are closed) unless we call Application.Shutdown(). This approach might make sense if our application is a front end for a long-running background task.

So when we want to use the OnMainWindowClose approach and we're using the App.xaml file, we need to make this addition in the XAML.

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

    </Application.Resources>
</Application>

No matter what shutdown method we choose, we can always use the Application. Shutdown() method to end our application immediately.

D. Application events: Initially, the App.xaml.cs file doesn't contain any code. Although no code is required, we can add code that handles application events. The Application class provides a small set of useful events which I have discussed below;

  1. Startup: Occurs after the Application.Run() method is called and just before the main window is shown (if we passed the main window to the Run() method).
     

  2. Exit: Occurs when the application is being shut down for any reason, just before the Run() method returns. You can't cancel the shutdown at this point.
     

  3. SessionEnding: Occurs when the Windows session is ending—for example, when the user is logging off or shutting down the computer. You can also cancel the shutdown by setting SessionEndingCancelEventArgs.Cancel to true. If we don't, WPF will call the Application.Shutdown() method when our event handler ends.

    Lets have a look on how to use this in code;

    private bool unsavedData = false;
            public bool UnsavedData
            {
                get { return unsavedData; }
                set { unsavedData = value; }
            }
            protected override void OnStartup(StartupEventArgs e)
            {
                base.OnStartup(e);
                UnsavedData = true;
            }
    private void Application_SessionEnding(object sender, SessionEndingCancelEventArgs e)
            {
                base.OnSessionEnding(e);
                if (UnsavedData)
                {
                    e.Cancel = true;
                    MessageBox.Show(

                    "The application attempted to be closed as a result of " +                e.ReasonSessionEnding.ToString() +
                    ". This is not allowed, as we have unsaved data.");
                }
    }
     

  4. Activated: Occurs when one of the windows in the application gets activated. This occurs when we switch from another Windows program to this application. It also occurs the firsttime we show a window.
     

  5. Deactivated: Occurs when a window in the application gets deactivated. This occurs when we switch to another Windows program.
     

  6. DispatcherUnhandledException: Occurs when an unhandled exception occurs anywhere in Your application on the main application thread. The application dispatcher catches these exceptions. By responding to this event, we can log critical errors.

    private void Application_DispatcherUnhandledException(object sender, System.Windows.Threading.DispatcherUnhandledExceptionEventArgs e)
            {
                MessageBox.Show("An unhandled " + e.Exception.GetType().ToString() +
    " exception was caught and ignored.");
                e.Handled = true;

            }
    <Application x:Class="ApplicationLifeCycle.App"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        StartupUri="Window1.xaml"
        DispatcherUnhandledException="Application_DispatcherUnhandledException"            
        ShutdownMode="OnMainWindowClose"
                 >   
    </Application>

Hope It Helped, Cheers.