Mvvm Light message recipient action being garbage

2019-07-19 00:52发布

问题:

My wpf application has child windows that popup to show views to the user. The child windows are closed by executing CloseCommand on the window's viewmodel, which sends a CloseWindowMessage using itself as the token.

This is the viewmodel used:

public class ChildWindowViewModel : ViewModel
{
    ICommand _closeCommand;
    public ICommand CloseCommand
    {
        get
        {
            if (_closeCommand == null)
                _closeCommand = new RelayCommand(DoClose);
            return _closeCommand;
        }
    }

    public void DoClose()
    {
        System.GC.Collect();
        System.GC.WaitForPendingFinalizers();
        Messenger.Default.Send(new CloseWindowMessage(),this);
    }
}

This is the method that shows windows:

protected void ShowWpfWindow(Window wpfWindow)
{
    var dc = wpfWindow.DataContext as ChildWindowViewModel;
    if (dc == null)
        throw new ArgumentOutOfRangeException();

    Action<CloseWindowMessage> action = (x) => 
    {
        wpfWindow.Close();
    };
    Messenger.Default.Register(wpfWindow, dc, action);

    wpfWindow.Owner = MainWindow.Single;
    wpfWindow.ShowInTaskbar = false;
    wpfWindow.Show();
}

I have added the calls to GC.Collect() and GC.WaitForPendingFinalizers() for debugging purposes. These seem to cause the variable Action<CloseWindowMessage> action to be garbage collected.

If I make Action<CloseWindowMessage> action a field on the containing class:

Action<CloseWindowMessage> action;
protected void ShowWpfWindow(Window wpfWindow)
{
    var dc = wpfWindow.DataContext as ChildWindowViewModel;
    if (dc == null)
        throw new ArgumentOutOfRangeException();

    action = (x) => 
    {
        wpfWindow.Close();
    };
    Messenger.Default.Register(wpfWindow, dc, action);

    wpfWindow.Owner = MainWindow.Single;
    wpfWindow.ShowInTaskbar = false;
    wpfWindow.Show();
}

The code works. I assume that the messenger maintains only a weak reference to the action, so that once execution passes out of ShowWpfWindow the action may be garbage collected. By setting the action as a field on the class, I am tying its lifetime to that of its containing object. However, I don't want to do this as the lifetime of the containing object may be less than wpfWindow.

What sort of pattern should I adopt to ensure that the close action is not disposed?