The approach another MVVM framework uses (Caliburn Micro) is essentially just using events from the VM.
However, to extend the idea into a reusable 'module' Caliburn Micro uses a Conductor
class which manages the relationship between the lifecycle of the View and the lifecycle of the ViewModel. An interface on the ViewModel which marks it as 'closable' is required, and you do need to write a conductor specific to the window/dialog implementation you are using (assuming it doesn't subclass from standard Window
).
Somewhere in your code you have to create a window and bind it to the viewmodel. This is the place where the conductor should be created to manage the relationship (Caliburn has this in its IWindowManager
implementation which provides and binds Window
instances to a given VM when the ShowPopup
/ShowDialog
methods are called)
The conductor may look like (a contrived example):
public class WindowConductor
{
private ISupportClose _closeable;
private Window _window;
private bool _closingFromViewModel;
private bool _closingFromView;
public WindowConductor(Window view, ISupportClose closeable)
{
_closeable = closeable;
_window = view;
_window.Closed += WindowClosed;
_closeable.Closed += ViewModelClosed;
}
public void WindowClosed(object sender, EventArgs e)
{
if(_closingFromViewModel) return;
_closingFromView = true;
closeable.Close();
}
public void ViewModelClosed(object sender, EventArgs e)
{
if(_closingFromView) return;
_closingFromViewModel = true;
window.Close();
}
}
Your ISupportClose
interface can simply be:
public interface ISupportClose
{
event EventHandler<CloseEventArgs> Closed;
void Close();
}
Then when you create your windows to display a view for a VM:
public void CreateWindow(viewModel)
{
Window window = new Window();
window.DataContext .. // etc etc bind up the view/model
// Wrap the window/vm with the conductor if the view supports the interface
var closeable = viewModel as ISupportClose;
if(closeable != null)
new WindowConductor(window, closeable);
}
I always find this very useful as it splits the concerns into smaller chunks. You don't often use more than 1 maybe 2 window implementations in an app anyway.
It may be worth noting that there is a bit of plumbing code behind all this (in fact a base class Screen
provides a standard implementation of lifecycle management etc)
If you aren't using an MVVM framework, I'd highly recommend you do so - writing boilerplate 'glue' has been done already by multiple frameworks