Tuesday, March 20, 2007

Avalon (WPF) Application Not Closing

I've had a problem for the last few days that has been driving me nuts. I'm writing an Avalon (WPF) application, and the damn thing wouldn't shut down. Even though I was calling this.Close() in my File->Exit handler, the process would stick around. It was particularly insidious because the main window would close, making it look like the application had exited. Even though I knew it had this problem, I would still forget until the next time I would compile, when I'd get the error, "The compiler can't overwrite the file (dumbass)."

 

Mike Woodring and I spent what must have been close to an hour reviewing all my threading code to make sure it was correct. It wasn't, of course (it never is), but after we'd tried everything I ripped all that code out and it still wouldn't close.

 

It bugged me enough that it was the last thing I was thinking of as I fell asleep last night. And then - and I'm sure this has happened to most people reading this - I solved the problem in my sleep.

 

Here's the deal. I had a class that looks more or less like this:

 

public partial class MyWindow : Window {

  private readonly MyDialog _myDialog = new MyDialog();

 

  protected void OnFileExit(...) {

    this.Close();

  }

}

 

Where MyDialog is another Window. I like to use this pattern for dialogs that I'm going to pop up and down a lot: create it once during the constructor. That way they keep all their settings and whatnot when they're not visible. And in WinForms, this works great. However, in Avalon I needed to do this:

 

public partial class MyWindow : Window {

  private readonly MyDialog _myDialog = new MyDialog();

 

  protected void OnFileExit(...) {

    this.Close();

    _myDialog.Close();

  }

}

 

even if you never displayed the dialog! Because otherwise, you apparently still have a top-level window hanging out, and Avalon won't let you exit the process.

 

I haven't looked to see if there's some magic property that says "OK to nuke the process if this window is still around". You may want to check the comments on this post - given my level of knowledge of Avalon at this point, it's likely that someone out there knows better how to address this. Certainly, having to remember to Close all your subordinate Windows can't be the best way. But it beats having to run Process Explorer to kill the process every time.

6 comments:

  1. Pretty easy, once you know what to do (Chapter 1 of Petzold's book has this info):

    public class Program : Window

    {

    [STAThread]

    static void Main(string[] args)

    {

    Application app = new Application();

    Program prog = new Program();

    app.MainWindow = prog;

    app.ShutdownMode = ShutdownMode.OnMainWindowClose

    new Application().Run(prog);



    }





    Scott Seely

    http://blogs.catalystss.com/blogs/Scott_Seely/

    ReplyDelete
  2. Is this particular dialog shown modally or modelessly? (Do you call ShowDialog or just Show?)



    ReplyDelete
  3. Modeless: I call Show.

    ReplyDelete
  4. I had a similar problem. In my case, I was trying to load a Window dynamically and getting exceptions because it couldn't find a resource dictionary. After the exception occurred, the application would hang around in memory. I'm not sure this is the best approach, but this worked for me:



    protected override void OnStartup(StartupEventArgs e) {

    AVAirWindow w = null;

    bool success = false;

    try {

    base.OnStartup(e);



    string path = (create the path)



    using (FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read)) {

    w = (AVAirWindow)XamlReader.Load(fs);

    }



    w.Show();



    success = true;



    } catch (Exception ex) {

    MessageBox.Show("Unexpected exception.\n\n" + ex.ToString());

    } finally {

    if (!success)

    Shutdown();

    }

    }

    ReplyDelete
  5. Hi people! I found out better solution for closing all loaded windows in WPF:



    for(int i = 0; i < Application.Current.Windows.Count; i++)

    Application.Current.Windows[i].Close();



    This will close all windows and so whole application. If you do not wish to say to all windows "bye-bye" ;), you can use this:

    System.Environment.Exit(System.Environment.ExitCode);

    ReplyDelete
  6. I just new there would be an Exit(), if I could only find where it lives. Many thanks.

    ReplyDelete