Cómo traer la ventana al frente con wpf y usando mvvm

Tengo una ventana que esencialmente ejecuta un temporizador. Cuando el temporizador llegue a 0, quiero traer la ventana al frente para que esté visible y no oculta detrás de otra aplicación.

Por lo que puedo reunir, simplemente llamaría a window.activate () para lograr esto, pero con mvvm mi modelo de vista no tiene una referencia a la ventana.

Podría hacerlo en un par de formas: agregar una referencia a la ventana podría funcionar ya que el modelo de vista no está acoplado con la vista, sino que está relacionado con ella, pero no me gusta ese enfoque ya que prácticamente se ajusta a su vista a su modelo de vista, que no es realmente el punto de MVVM

Un mejor enfoque puede ser que su modelo de vista genere un evento o un comando que la vista pueda manejar. De esta manera, la vista decide qué acción de UI está asociada con el comando / evento

por ejemplo, simplemente

class SomeView { void HandleSomeCommandOrEvent() { this.Activate(); } } 

Por supuesto, la forma en que conectas esto depende de ti, pero probablemente intentaría que los comandos enrutados sucedieran

Edición: Realmente no se puede “vincular” un evento simple, ya que se invoca desde el modelo de vista.

Un ejemplo simple basado en un evento es simplemente agregar el evento al modelo de visualización y manejarlo directamente … por ejemplo, imagine la siguiente ventana principal con una propiedad ViewModel

 public partial class MainWindow : Window { MainWindowViewModel ViewModel { get; set; } public MainWindow() { InitializeComponent(); ViewModel = new MainWindowViewModel(); ViewModel.ShowMessage += ViewModel_ShowMessage; this.DataContext = ViewModel; } void ViewModel_ShowMessage(object sender, ShowMessageEventArgs e) { MessageBox.Show(e.Message, "Some caption", MessageBoxButton.OK); } } 

Entonces el ViewModel puede simplemente disparar el evento:

 // The view model public class MainWindowViewModel { // The button click command public RelayCommand ButtonClickCommand { get; set; } // The event to fire public event EventHandler ShowMessage; public MainWindowViewModel() { ButtonClickCommand = new RelayCommand(ButtonClicked); } void ButtonClicked(object param) { // This button is wired up in the view as normal and fires the event OnShowMessage("You clicked the button"); } // Fire the event - it's up to the view to decide how to implement this event and show a message void OnShowMessage(string message) { if (ShowMessage != null) ShowMessage(this, new ShowMessageEventArgs(message)); } } public class ShowMessageEventArgs : EventArgs { public string Message { get; private set; } public ShowMessageEventArgs(string message) { Message = message; } } 

El XAML sería:

  

Así que el botón invoca el comando, que a su vez dispara el evento que la vista (MainWindow) maneja y muestra un cuadro de mensaje. De esta manera, la vista / UI decide el curso de acción en función del tipo de evento generado. Por supuesto, podría ser su temporizador el que encendió el evento.

Siempre puede ir por la ruta más complicada, como algunas de las respuestas a esta pregunta …

¿Cómo debe el ViewModel cerrar el formulario?

pero para ser honesto, depende de si realmente lo necesita; un evento simple funciona bien. Algunas personas complican las cosas por el simple hecho de ser elegantes, ¡pero en detrimento de la simplicidad y la productividad!

Una solución MVVM “purista” es usar un comportamiento. A continuación se muestra un comportamiento para una Window con una propiedad Activated . Establecer la propiedad en true activará la ventana (y la restaurará si está minimizada):

 public class ActivateBehavior : Behavior { Boolean isActivated; public static readonly DependencyProperty ActivatedProperty = DependencyProperty.Register( "Activated", typeof(Boolean), typeof(ActivateBehavior), new PropertyMetadata(OnActivatedChanged) ); public Boolean Activated { get { return (Boolean) GetValue(ActivatedProperty); } set { SetValue(ActivatedProperty, value); } } static void OnActivatedChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e) { var behavior = (ActivateBehavior) dependencyObject; if (!behavior.Activated || behavior.isActivated) return; // The Activated property is set to true but the Activated event (tracked by the // isActivated field) hasn't been fired. Go ahead and activate the window. if (behavior.AssociatedObject.WindowState == WindowState.Minimized) behavior.AssociatedObject.WindowState = WindowState.Normal; behavior.AssociatedObject.Activate(); } protected override void OnAttached() { AssociatedObject.Activated += OnActivated; AssociatedObject.Deactivated += OnDeactivated; } protected override void OnDetaching() { AssociatedObject.Activated -= OnActivated; AssociatedObject.Deactivated -= OnDeactivated; } void OnActivated(Object sender, EventArgs eventArgs) { this.isActivated = true; Activated = true; } void OnDeactivated(Object sender, EventArgs eventArgs) { this.isActivated = false; Activated = false; } } 

El comportamiento requiere una referencia a System.Windows.Interactivity.dll . Afortunadamente, ahora está disponible en NuGet en el paquete Blend.Interactivity.Wpf .

El comportamiento se adjunta a una ventana en XAML como esta:

     

El modelo de vista debería exponer una propiedad Activated booleana. Establecer esta propiedad en verdadero activará la ventana (a menos que ya esté activada). Como bono adicional, también restaurará una ventana minimizada.

Yo iría por aquí

 using GalaSoft.MvvmLight; using GalaSoft.MvvmLight.Command; using GalaSoft.MvvmLight.Messaging; // View public partial class TestActivateWindow : Window { public TestActivateWindow() { InitializeComponent(); Messenger.Default.Register(this, (msg) => Activate()); } } // View Model public class MainViewModel: ViewModelBase { ICommand _activateChildWindowCommand; public ICommand ActivateChildWindowCommand { get { return _activateChildWindowCommand?? (_activateChildWindowCommand = new RelayCommand(() => { Messenger.Default.Send(new ActivateWindowMsg()); })); } } } public class ActivateWindowMsg { }