The WPF Application Class: Overview and Gotcha

I’ve written often lately about WPF, including BabySmash posts and Missing .NET articles. I’ve decided to do a complete app using WPF. I’ve dabbled here and there with the concepts, but I’ve decided I don’t really know the framework that well. I still have to look up how to do something basic like master-detail data binding. So I set out to do something fairly basic and (barely) doable by one person – kind of what Scott Hanselman is doing with BabySmash, but a little more traditional, so I can take advantage of more of WPF. I shan’t disclose the function of the app for now, because it’s no where near ready. I will, however, document some things I run into, either because they were so useful, but not really advertised, or because I’ve found sharp rocks in the shallows and must warn others. This post is a bit of both.

If you use Visual Studio, this is obscured from you, but every WPF application must contain the equivalent of the following Main

[STAThread]
public static void Main(string[] args)
{
    Application app = new Application();
    Window win = new Window();
    win.Show();
    app.Run();    
}

Without the Application class, the above program will end right after you run it. The magic in Application.Run() is that it sets up a Win32 message pump so that your whiz bang WPF objet d’art can interact with the rest of Windows, and not exit immediately. There is a surprising amount going on in those four lines of code. Let me explain the big things, before I tell you about the subtlety I found.

There can only be one Application object per process. After the first line of code in the snippet above is run, you can access the static Application.Current property. Doing a test in the Immediate window in Visual Studio to check equality of app and Application.Current will yield true. You can access this anywhere in your app, which makes it really handy.

Let’s continue with executing the second line.

Once the win instance is created, if we were to inspect the app object again, we’d see a few things have changed: the MainWindow property is now non-null — it’s pointing to the win instance; also, the Windows collection will have a Count of 1. For every other window created during the lifetime of the application, the Windows collection will be added to.

Quite a lot has happened in just two lines of code!

The other two lines are obvious; a lot happens when they execute too, but they’re peripheral to the discussion, so let’s move on.

This is a common pattern. Microsoft recognized this and codified it into a pattern. The Application class is designed to be inherited, and a WPF app that doesn’t show a window is a very boring thing. If you create a new WPF application project in VS, you don’t have to write that Main method above; it’s all done for you with code gen in the inherited Application object. You don’t even need to explicitly instantiate a Window instance: VS points the Application.StartupUri property to the XAML file defining your main window.

Now that’s great for one-off demos or simple applications that do the equivalent of turn a light on and off. But, in my experience as a Windows developer, if you want to do anything complicated, your application will have all sorts of configuration to do; or you have to log in to a service to do anything useful; or whatever. Suppose a developer had to code a scenario to show a dialog on the first run of the app to do some absolutely necessary, can’t-run-the-app-without-it configuration. To develop that scenario, one would have to eschew the StartupUri property for something slightly more complex.

The Application class also has a number of important events concerned with application lifetime. One of them is the Startup event. A naive developer would handle the previously mentioned scenario of a config dialog in that event handler, like so:

public class MyApp : Application
{
    protected override void OnStartup(object sender, StartUpEventArgs e)
    {
        if (firstRun)
        {
            MyConfigDialog dialog = new MyConfigDialog();
            if (!dialog.ShowDialog().Value)
            {
                this.Shutdown();
            }
        }
        MyMainWindow window = new MyMainWindow();
        window.Show();
    }
}

If firstRun is true, and you click OK on the dialog, so that ShowDialog() returns true, then the app will shutdown. I know what you’re thinking:

Huh?

I’m not sure, myself: this may be a bug. But here’s what’s going on: if this is the first run of the app, then the dialog is shown. It’s the first Window class created. Therefore, the Application.MainWindow property is set to the dialog instance. When ShowDialog() returns, the dialog is closed. Therefore, Application.MainWindow is null. It’s like that MyMainWindow instance is invisible or something. He could be screaming at the top of his lungs at Application: “HEY, LOOK AT ME! I’VE GOT SOMETHING TO SHOW YOU!”

Then Application just turns around and exits the building.

“ASSH– wait a minute! I’m invisible! I don’t need that stupid App instance. I’m going to rob a bank! I’ll be rich and the ladies will love me.” Then he falls on the floor lifeless, ’cause the whole process died. Poor MyMainWindow.What, you don’t anthropomorphize your objects?

You could also set the MainWindow property yourself, apparently, at any time you want to. But this fails too:

public class MyApp : Application
{
    protected override void OnStartup(object sender, StartUpEventArgs e)
    {
        if (firstRun)
        {
            MyConfigDialog dialog = new MyConfigDialog();
            if (!dialog.ShowDialog().Value)
            {
                this.Shutdown();
            }
        }
        MyMainWindow window = new MyMainWindow();
        //guess what? this doesn't work!
        this.MainWindow = window;
        window.Show();
    }
}

I’m seriously baffled by this. Looking around in Reflector, the code I’m looking at says it shouldn’t act like this. I also don’t touch Application.ShutdownMode (I’ve tested this with OnLastWindowClose and with OnMainWindowClose). Anyway, to workaround the issue, simply ensure that MainWindow is instantiated first:

public class MyApp : Application
{
    protected override void OnStartUp(object sender, StartUpEventArgs e)
    {
        //this works! 
        MainWindow window = new MainWindow();
        if (firstRun)
        {
            MyConfigDialog dialog = new MyConfigDialog();
            if (!dialog.ShowDialog().Value)
            {
                this.Shutdown();
            }
        }
        window.Show();
    }
}

The Application class is a much needed addition to Windows programming. A cross between the ASP.NET Application and Session classes and the WinForms Application class, it provides a property bag, application lifetime events, application-wide resources, an entry point, a Win32 message pump and access to the application’s windows including the Main Window. Architecturally, I find this class very appealing, but that’s a topic for another post.

Just watch out for trying to be fancy with extra windows.