I have
public static int WindowCounter = 0;
[STAThread]
public static void Main()
{
ShowBeforeApplicationCreation();
//ShowAfterApplicationCreation();
}
public static void ShowBeforeApplicationCreation()
{
ShowWindow();
ShowWindow();
ShowWindow();
var app = new Application();
app.Run();
}
public static void ShowAfterApplicationCreation()
{
var app = new Application();
ShowWindow();
ShowWindow();
ShowWindow();
app.Run();
}
public static void ShowWindow()
{
var window = new Window { Title = string.format("Title{0}", WindowCounter++) };
window.Show();
}
If I run the code as shown and I close any of the windows it will close all windows. If I switch to what is commented out, it seems random as to which window will cause all windows to close (most of the time it is the last one).
How is the Application deciding which window will cause the process to close?
Is that window the "main" window?
Let's define the window that closes the process as the OwningWindow (hopefully that name isn't overloaded in this context). How can I ensure that the first window shown is the OwningWindow?
This last question is my main one. I don't want to open up other secondary windows, have the user close the main one, and have the process keep running. Or do I have to make subsequent windows children of the window I want to be the OwningWindow?
How is the Application deciding which window will cause the process to close?
By default, the Application
class will exit the Run()
method (and hence the whole process here) when all of the open windows have been closed. Based on your code example though, there seems to be a wrinkle: the Application
class does not consider any windows that were opened before it was itself created. If you don't tell it about any window (i.e. by passing a reference to the Run()
method), then any window closing will cause the Run()
method to exit, because the Application
class does see the closure of the window, but thinks there are no windows in the process.
Is that window the "main" window?
The "main" window is by default the first window to be instantiated in the AppDomain
. You can override this at any time by setting the MainWindow
property of the Application
object.
Naturally, as discussed above, the Application
object has no way to see windows created before it itself is created. An exception: if you pass a window to the Run()
method, that can become the MainWindow
even if the window was created before the Application
object. If all your windows are created before the Application
is and you don't pass the reference to any of those windows to the Run()
method, then there is no main window at all.
If you want to deterministically assign a single window to cause the process to exit when the window is closed, one option would be to pass that to the Application.Run()
method. For example:
class Program
{
public static int WindowCounter = 0;
private static Window _firstWindow;
[STAThread]
public static void Main()
{
ShowBeforeApplicationCreation();
}
public static void ShowBeforeApplicationCreation()
{
ShowWindow();
ShowWindow();
ShowWindow();
var app = new Application();
app.Run(_firstWindow);
}
public static void ShowWindow()
{
var window = new Window { Title = string.Format("Title{0}", WindowCounter++) };
window.Show();
_firstWindow = _firstWindow ?? window;
}
}
That said, the default Application.ShutdownMode
value is ShutdownMode.OnLastWindowClose
, so without doing anything else, the program should not close until the last window is closed. It's just because the class doesn't seem to notice the opening of windows that occurred before it was instantiated (it obviously reacts to the actual creation of the window, rather than searching the process for open windows when it starts).
Clearly, creating the windows before Application.Run()
has been called confuses the method; it seems to think no windows are still open if any are closed. If you pass it the reference to one of the windows, it won't return until that window is closed, that window being the only window it "knows" about; the closure of any other window is ignored, because the one window it knows about is still open.
(For what it's worth, in my tests the program does not exit until all windows are closed, in the scenario where they are all created after the Application
object. This is consistent with the above idea that the Application
class correctly notes any windows created and/or shown after it itself is created).
IMHO, the best approach is to not confuse the method. It cannot correctly handle windows that are shown before the Application
object is created. So, don't do that. Make sure you create all of your windows only after Application
is created. If you have specific behavior for closing that is different from the default ShutdownMode.OnLastWindowClose
behavior, then that should be simple enough to implement.
And that could be as simple as just setting the ShutdownMode
property to something else. For example, ShutdownMode.OnMainWindowClose
. In this case, you of course need to make sure that some window is the main window, otherwise the process won't exit at all. You can do this by using the default behavior (i.e. create your main window as the first window, but after the Application
object has been created), by setting the MainWindow
property explicitly, or even by passing a window reference to the Run()
method having created all of the windows before the Application
object.
Let's define the window that closes the process as the OwningWindow (hopefully that name isn't overloaded in this context). How can I ensure that the first window shown is the OwningWindow?
From the above discussion, you can probably answer this question yourself. :)
First, the "owning window" is actually the MainWindow
. But, by default the Application.Run()
method only returns when all windows (it knows about) have been closed. It is only by hiding some windows from the Application
method that it seems like some "owning window" is in charge of closing the program (in the default ShutdownMode
scenario).
In reality, if what you want is to only close the program when some specific window is itself closed, the right way to do this is to ensure that the MainWindow
is set correctly, and that you've set Application.ShutdownMode
to the ShutdownMode.OnMainWindowClose
value. In that way, you are being completely explicit with WPF about the exact behavior you want, and it won't have the chance to mess it up, even if you do something weird like creating a Window
object before the Application
object is created. :)