Solución:
Estás intentando hacer el trabajo de View en la clase ViewModel. Deje que su clase de View maneje la solicitud de cierre y si debe cancelarse o no.
Para cancelar el cierre de una ventana, puede suscribirse al Closing
evento de vista y set CancelEventArgs.Cancel
a verdadero después de mostrar un MessageBox
.
Aquí hay un ejemplo:
<Window
...
x:Class="MyApp.MyView"
Closing="OnClosing"
...
/>
</Window>
Código detrás:
private void OnClosing(object sender, CancelEventArgs e)
{
var result = MessageBox.Show("Really close?", "Warning", MessageBoxButton.YesNo);
if (result != MessageBoxResult.Yes)
{
e.Cancel = true;
}
// OR, if triggering dialog via view-model:
bool shouldClose = ((MyViewModel) DataContext).TryClose();
if(!shouldClose)
{
e.Cancel = true;
}
}
No soy un experto en MVVM, pero en mi opinión, la respuesta de Yusufs no es del todo MVVM. Por otro lado la respuesta de Torpederos es un poco complicada por solo una cancelación cercana. Este es mi enfoque. En este ejemplo me suscribí al evento de cierre, pero siempre se cancela
private void OnClosing(object sender, CancelEventArgs e)
{
e.Cancel = true;
return;
}
En el XAML agregué esto
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
<i:Interaction.Triggers>
<i:EventTrigger EventName="Closing">
<i:InvokeCommandAction Command="{Binding Close}" />
</i:EventTrigger>
</i:Interaction.Triggers>
Y finalmente en el modelo de vista
public ICommand Close { get; set; }
Close = new RelayCommand(CommandClose);
private void CommandClose(object sender)
{
if (Dirty)
{
// Save your data here
}
Environment.Exit(0);
}
En este enfoque, el evento de cierre se activa primero. Eso cancela el cierre. Después de eso, se invoca el disparador de interacción y activa el código en el modelo de vista a través del RelayCommand. En el modelo de vista, puedo usar la bandera Sucia que no es accesible en la vista.