example commandmanager c# wpf mvvm relaycommand

c# - commandmanager - Implementando el comando "Cerrar ventana" con MVVM



commandmanager wpf example (13)

Así que mi primer intento hizo todo fuera del código, y ahora estoy tratando de refactorizar mi código para usar el patrón MVVM, siguiendo las instrucciones del MVVM en la información del cuadro .

Creé una clase viewmodel para que coincida con mi clase de vista, y estoy sacando el código del código en el modelo de vista que comienza con los comandos.

Mi primer inconveniente es tratar de implementar un botón ''Cerrar'' que cierra la ventana si los datos no se han modificado. Creé un CloseCommand para reemplazar el método ''onClick'' y todo está bien, excepto donde el código intenta ejecutar esto. this.Close() . Obviamente, dado que el código se ha movido de una ventana a una clase normal, "esto" no es una ventana y, por lo tanto, no se puede cerrar. Sin embargo, de acuerdo con MVVM, el modelo de vista no conoce la vista, por lo que no puedo llamar a view.Close() .

¿Puede alguien sugerir cómo puedo cerrar la ventana desde el comando viewmodel?


Aquí está la solución más simple y la solución MVVM pura

Código de ViewModel

public class ViewModel { public Action CloseAction { get; set; } private void CloseCommandFunction() { CloseAction(); } }

Aquí está el código de vista XAML

public partial class DialogWindow : Window { public DialogWindow() { ViewModel vm = new ViewModel(); this.DataContext = vm; vm.CloseAction = new Action(() => this.Close()); } }


Dado un camino, por favor verifique

https://.com/a/30546407/3659387

Breve descripción

  1. Derive su ViewModel de INotifyPropertyChanged
  2. Cree una propiedad observable CloseDialog en ViewModel, cambie la propiedad CloseDialog cada vez que desee cerrar el diálogo.
  3. Adjunte un controlador a la vista para cambiar esta propiedad
  4. Ahora ya casi terminaste. En el controlador de eventos, haga DialogResult = true

Esta solución es rápida y fácil. Lo malo es que hay un acoplamiento entre las capas.

En su modelo de vista:

public class MyWindowViewModel: ViewModelBase { public Command.StandardCommand CloseCommand { get { return new Command.StandardCommand(Close); } } public void Close() { foreach (System.Windows.Window window in System.Windows.Application.Current.Windows) { if (window.DataContext == this) { window.Close(); } } } }


Esto es muy similar a la respuesta de eoldre. Es funcionalmente el mismo en el sentido de que mira a través de la misma colección de Windows para una ventana que tiene el modelo de vista como su contexto de datos; pero he usado un RelayCommand y algunos LINQ para lograr el mismo resultado.

public RelayCommand CloseCommand { get { return new RelayCommand(() => Application.Current.Windows .Cast<Window>() .Single(w => w.DataContext == this) .Close()); } }


Esto se toma de la respuesta ken2k (¡gracias!), Solo agrega el CloseCommand también a la base CloseableViewModel .

public class CloseableViewModel { public CloseableViewModel() { CloseCommand = new RelayCommand(this.OnClosingRequest); } public event EventHandler ClosingRequest; protected void OnClosingRequest() { if (this.ClosingRequest != null) { this.ClosingRequest(this, EventArgs.Empty); } } public RelayCommand CloseCommand { get; private set; } }

Su modelo de vista, lo hereda

public class MyViewModel : CloseableViewModel

Entonces en tu vista

public MyView() { var viewModel = new StudyDataStructureViewModel(studyId); this.DataContext = viewModel; //InitializeComponent(); ... viewModel.ClosingRequest += (sender, e) => this.Close(); }


Lo hago creando una propiedad adjunta llamada DialogResult:

public static class DialogCloser { public static readonly DependencyProperty DialogResultProperty = DependencyProperty.RegisterAttached( "DialogResult", typeof(bool?), typeof(DialogCloser), new PropertyMetadata(DialogResultChanged)); private static void DialogResultChanged( DependencyObject d, DependencyPropertyChangedEventArgs e) { var window = d as Window; if (window != null && (bool?)e.NewValue == true) window.Close(); } public static void SetDialogResult(Window target, bool? value) { target.SetValue(DialogResultProperty, value); } }

luego, escribe esto a ti XAML, en la etiqueta de la ventana

WindowActions:DialogCloser.DialogResult="{Binding Close}"

finalmente en el ViewModel

private bool _close; public bool Close { get { return _close; } set { if (_close == value) return; _close = value; NotifyPropertyChanged("Close"); } }

si cambia el Cerrar a verdadero, la ventana se cerrará

Close = True;


MVVM-light con una notificación de mensaje personalizado para evitar que la ventana procese cada notificación.

En el modelo de vista:

public class CloseDialogMessage : NotificationMessage { public CloseDialogMessage(object sender) : base(sender, "") { } } private void OnClose() { Messenger.Default.Send(new CloseDialogMessage(this)); }

Registre el mensaje en el constructor de la ventana:

Messenger.Default.Register<CloseDialogMessage>(this, nm => { Close(); });


Mi solución para cerrar una ventana del modelo de vista mientras hago clic en un botón es la siguiente:

En el modelo de vista

public RelayCommand CloseWindow; Constructor() { CloseWindow = new RelayCommand(CloseWin); } public void CloseWin(object obj) { Window win = obj as Window; win.Close(); }

En Ver, configure de la siguiente manera

<Button Command="{Binding CloseWindowCommand}" CommandParameter="{Binding ElementName=WindowNameTobeClose}" Content="Cancel" />


No es necesario que pase la instancia de View a su capa de ViewModel. Puede acceder a la ventana principal de esta manera:

Application.Current.MainWindow.Close()

No veo ningún problema para acceder a la ventana principal en la clase ViewModel como se indicó anteriormente. Según el principio de MVVM, no debe haber un acoplamiento estricto entre su View y ViewModel, es decir, deberían funcionar sin tener en cuenta el funcionamiento de los demás. Aquí, no estamos pasando nada a ViewModel desde View. Si desea buscar otras opciones, esto podría ayudarlo: cierre la ventana con MVVM.


Personalmente, utilizo un enfoque muy simple: para cada ViewModel que está relacionado con una vista que se puede cerrar, creé un ViewModelo base como este ejemplo siguiente:

public abstract class CloseableViewModel { public event EventHandler ClosingRequest; protected void OnClosingRequest() { if (this.ClosingRequest != null) { this.ClosingRequest(this, EventArgs.Empty); } } }

Luego, en su ViewModel heredado de CloseableViewModel , simplemente llame a this.OnClosingRequest(); para el comando Close .

En la vista:

public class YourView { ... var vm = new ClosableViewModel(); this.Datacontext = vm; vm.ClosingRequest += (sender, e) => this.Close(); }


Tenga cuidado con los paradigmas de moda. MVVM puede ser útil, pero no debería tratarlo como un conjunto rígido de reglas. Use su propio criterio, y cuando no tenga sentido, no lo use.

Las soluciones proporcionadas aquí (con la excepción de la solución de @ RV1987) son un muy buen ejemplo de que las cosas se están yendo de las manos. Está reemplazando una sola llamada Close() con una cantidad tan grande de código, ¿con qué propósito? No se gana absolutamente nada moviendo el código de cierre de la vista al modelo de vista. Lo único que ganas es espacio para más errores.

Ahora, no estoy diciendo que MVVM deba ser ignorado. Por el contrario, puede ser muy útil. Simplemente no más de hacerlo.


en primer lugar dale a tu ventana un nombre como

x:Name="AboutViewWindow"

en mi botón de cerrar he definido Comando y Parámetro de Comando como

CommandParameter="{Binding ElementName=AboutViewWindow}" Command="{Binding CancelCommand}"

entonces, en mi opinión, el modelo

private ICommand _cancelCommand; public ICommand CancelCommand { get { if (_cancelCommand == null) { _cancelCommand = new DelegateCommand<Window>( x => { x?.Close(); }); } return _cancelCommand; } }


usando el juego de herramientas MVVM-light:

En ViewModel:

public void notifyWindowToClose() { Messenger.Default.Send<NotificationMessage>( new NotificationMessage(this, "CloseWindowsBoundToMe") ); }

Y en la vista:

Messenger.Default.Register<NotificationMessage>(this, (nm) => { if (nm.Notification == "CloseWindowsBoundToMe") { if (nm.Sender == this.DataContext) this.Close(); } });