yes referencing view in viewmodel isn't best practice. WHY? because when you unit test your viewmodel it is require you to instantiate view, for small view will not difficult to do that, but for a complex view with complex tree of dependency? that wont be good.
for me, the easiest way to do communication with view is by passing IInputElement
on viewmodel constructor. the bennefit of IInputElement
is Routed Event backbone, it has RaiseEvent
and AddHandler
method required for routed event. thus you can bubble/tunnel/direct event to any view or viewmodel on your application freely without any additional library.
here is my the simplified code on viewmodel but remember this technique only work for view first approach
public class MyViewModel : INotifyPropertyChanged
{
public static readonly RoutedEvent RequestCloseEvent = EventManager.RegisterRoutedEvent("RequestClose",
RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(MyViewModel));
private IInputElement dispatcher;
public MyViewModel(IInputElement dispatcher)
{
this.dispatcher = dispatcher;
}
public void CloseApplication()
{
dispatcher.RaiseEvent(new RoutedEventArgs(RequestCloseEvent));
}
}
on your View simply
DataContext = new MyViewModel(this)
//notice "this" on the constructor
and the root view (Window) of your application simply
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
AddHandler(MyViewModel.RequestCloseEvent, new RoutedEventHandler(onRequestClose));
}
private void onRequestClose(object sender, RoutedEventArgs e)
{
if (MessageBox.Show("Are you sure you want to quit?", "Confirmation", MessageBoxButton.YesNo) == MessageBoxResult.Yes)
{
Close();
}
}
}
and because IInputElement
is interface rather than class, you easily create a mock class for your unit test
var target = new MyViewModel(new DispatcherMock)
or you can use mock library like RhinoMocks
for further reading, you can learn more about how to use Routed Event
Close()
method fromViewModel
is not a good practice and is contrary to the MVVM idea itself. Use triggers for such purposes. – Anatolii Gabuza