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.
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.
Ran into this issue using a login dialog. Thanks for saving me hours of headache
set shutdown mode to explicit on application
Where does the variable ‘firstRun’ get set? Everything else works great. Thanks!
Great!
Anyway it’s not the solution of mine because I have to reopen the first and second window as well several times.
So my solution is exactly what Torna wrote!
Thank you for shaing the info. You saved me maybe hours 🙂
Something is changed, or was not working all along:
public class MyApp : Application
{
protected override void OnStartUp(object sender, StartUpEventArgs e)
{
MainWindow window = new MainWindow();
MyConfigDialog dialog = new MyConfigDialog();
dialog.ShowDialog();
// Problem: The application doesn’t close when mainwindow is closed.
// window.Show(); // If you uncomment this it shows *two* main windowes. But same problem, application never closes.
}
}
I’m sorry above example works just fine.
Just remember to *remove* the StartupUri in Application element at app.xaml.
I was in a hurry and missed that. (Looking only the source code)
Great ! This article was useful for me, too.
thanks a lot, I was looking for that for some time 🙂
Spent most of the day trying to find a solution to the same problem. Thanks for posting this!
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).
I think the problem with this one has to do with the window count as well as the MainWindow property. As you noticed with the first example posted, showing a dialog and closing it causes the app to shut down as the MainWindow property is nulled. The same is happening here, and if you do happen to change the ShutdownMode to OnLastWindowClose, you get essentially the same behavior, because the first window (the dialog) is opened, and then the last window (the dialog) is closed (shutting down the app), before the next window is instantiated. I haven’t tested, but would be willing to bet that if you set the ShutdownMode to Explicit, you would not get this (not neccessarily recommended, just saying…)
All that said, many thanks for the post. As a relative newbie to any programming other than VBA, this got me really digging around in WPF application lifetime management. If you like, I can post the MSDN links where I found this info (along with many other related and quite helpful topics).
Cheers and thanks again,
j.
I’m pretty sure that the “correct” solution in this case is to set the ShutdownMode to Explicit before creating your login window, and then set it back to either OnLastWindowClose or OnMainWindowClose *after* creating (and possibly also showing) your main window.