Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why isn't WPF Window automatically disposed and collected after MainWindow closed?

I have been wondering this for long and am finding the best solution. To reproduce this issue, simply:

  1. Add a new Window in a new WPF application.
  2. Add the following code:

    public MainWindow()
    {
        InitializeComponent();
        //add code
        Window1 w1 = new Window1();
        //following is in my actual project
        DataContext = new AllUserQueryiesViewModel(new CommonQueryTestSubWindow());
    }
    
  3. Run and close MainWindow.

By the time, I expect the application to be closed but my VS2010 seems to be still running instead. So why and how to solve it?

---------UPDATE-----------------

I think I'd better give my scenery in my real project, for you to better understand why I do this. Think of a Queries List and open query function, like that for TFS workitem queries. I am creating some usercontrol to do this, for others in our team to use in the same application. The Queries List and opened query are in seperate usercontrols, so the user(other programers) can place the opened query in wherever(ContentControl) they want. The ViewModels:

public class AllVM

    {
        public AllVM(IOneVMContainer container)
        {
            OneVMs = new ObservableCollection<OneVM>()
           {
               new OneVM(container),
               new OneVM(container)
           };
        }

        public ObservableCollection<OneVM> OneVMs { get; set; }
        public OneVM SelectedOneVM { get; set; }

        public void OpenOne()
        {
            SelectedOneVM.Open();
        }
    }

public class OneVM
{
    IOneVMContainer container;
    public OneVM(IOneVMContainer container)
    {
        this.container = container.NewInstance();
    }
    //I want to open and close the view from viewmodel
    public void Open()
    {
        container.GetContentCtl().Content = this;
        container.Show();
    }
    public void Close()
    {
        container.Close();
    }
}

To open and close the view in ViewModel, I created such interface

//Is it good to use interface?
//Maybe a better interface?
public interface IOneVMContainer
{
    IOneVMContainer NewInstance();
    void Show();
    void Close();
    ContentControl GetContentCtl();
}

Use of above ViewModels:

//my usercontrol to hold AllVM
public AllUC()
{
    InitializeComponent();
    DataContext = new AllVM(new  OneContainerWindow());
}

//implement by other team members
public partial class OneContainerWindow : Window,IOneVMContainer
{
    public OneContainerWindow()
    {
        InitializeComponent();
    }
    public IOneVMContainer NewInstance()
    {
       return new OneContainerWindow();
    }
    public ContentControl GetContentCtl()
    {
        return contentCtl1;
    }
}

So I want to know if it is inevitable to store a window in the ViewModel, which results to the main problem. I know you must have some better solution, please help.

like image 551
Lei Yang Avatar asked Mar 21 '23 23:03

Lei Yang


2 Answers

First of all do not instantiate a new window in the constructor.

Now, for your question, you need to set the Owner of the window:

public MainWindow()
{
    InitializeComponent();
    Loaded += MainWindow_Loaded;
}

void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
    Window w = new Window() { Owner = this };
}

in this way the child window should be correctly disposed.

like image 165
Alberto Avatar answered Apr 22 '23 15:04

Alberto


The WPF dispatcher loop keeps running as long as your application still has a live Window instance. Your Window1 object. The fact that it is not visible because you never called its Show() method and that you can never close the window yourself because you lost the reference to the window doesn't change that outcome.

Pretty unclear why you'd contemplate do this, but one workaround is to force the dispatcher loop to exit when the main window is closed:

    protected override void OnClosed(EventArgs e) {
        base.OnClosed(e);
        Application.Current.Dispatcher.InvokeShutdown();
    }

There are many others, like making "w1" a field of your class so you can call its Close() method. Or making it an owned window so it dies at the same time as your main window. Or showing it so the user stays in control over the windows in your app. Take your pick.

like image 43
Hans Passant Avatar answered Apr 22 '23 14:04

Hans Passant