¿Cómo cerrar la ventana de diálogo desde viewmodel(Caliburn+WPF)?
dialog window (3)
Tengo ViewModel1
y View1 asociados con él. Comienzo la ventana de diálogo desde ViewModel2
(algún otro modelo de vista) usando el objeto IWindowManager
. El código de la clase ViewModel2
:
windowManager.ShowDialog(new ViewModel());
Por lo tanto, tengo ventana de diálogo con control de usuario View1.
Mi respuesta es la siguiente: puedo cerrar esa ventana de diálogo con el botón de cierre rojo, pero cómo cerrarla con mi botón específico (contenido en el control de usuario de View1
), algo así como el botón "Cancelar" con el comando de cierre ( Command={Binding CancelCommand}
) , CancelCommand
, por supuesto, está contenido en la clase ViewModel1
.
Es aún más fácil si tu modelo de vista amplía Caliburn.Micro.Screen
:
TryClose();
Puede obtener la vista actual (en su caso, la ventana de diálogo) con la implementación de la interfaz IViewAware
en su ViewModel. Luego puede llamar a Close
en la vista (la Window
creada como el cuadro de diálogo) cuando se ejecuta su comando.
Lo más fácil por qué es derivar de ViewAware
:
public class DialogViewModel : ViewAware
{
public void ExecuteCancelCommand()
{
(GetView() as Window).Close();
}
}
Si no se le permite derivar, puede implementarlo usted mismo:
public class DialogViewModel : IViewAware
{
public void ExecuteCancelCommand()
{
dialogWindow.Close();
}
private Window dialogWindow;
public void AttachView(object view, object context = null)
{
dialogWindow = view as Window;
if (ViewAttached != null)
ViewAttached(this,
new ViewAttachedEventArgs(){Context = context, View = view});
}
public object GetView(object context = null)
{
return dialogWindow;
}
public event EventHandler<ViewAttachedEventArgs> ViewAttached;
}
Nota: He usado Caliburn.Micro 1.3.1 para mi muestra.
Una forma más limpia (tema de gusto personal) que uso mucho es usar el patrón IResult, de esta manera abstraes la implementación de la ventana.
Modelo de vista
public IEnumerable<IResult> CloseMe()
{
yield return new CloseResult();
}
Código de resultado
public class CloseResult : Result
{
public override void Execute(ActionExecutionContext context)
{
var window = Window.GetWindow(context.View);
window.Close();
base.Execute(context);
}
}
public abstract class Result : IResult
{
public virtual void Execute(ActionExecutionContext context)
{
OnCompleted(this, new ResultCompletionEventArgs());
}
protected virtual void OnCompleted(object sender, ResultCompletionEventArgs e)
{
if (Completed != null)
Completed(sender, e);
}
public event EventHandler<ResultCompletionEventArgs> Completed;
}
Editar (solo necesario para IoC) : si quieres ir un paso más allá, haz una clase base para todas las pantallas
public abstract class ShellPresentationModel : Screen
{
public ShellPresentationModel(IResultFactory resultFactory)
{
Result = resultFactory;
}
public IResultFactory Result { get; private set; }
}
De esta manera puede inyectar dependencias con un IoC mucho más fácil, entonces su método de cierre VIewmodel se verá así
public IEnumerable<IResult> CloseMe()
{
yield return Result.Close();
}
Un ejemplo en un IResult que usa dependencia puede ser
public class ShowDialogResult<TModel> : Result
{
private readonly IWindowManager windowManager;
private readonly TModel model;
private Action<TModel> configure;
public ShowDialogResult(IWindowManager windowManager, TModel model)
{
this.windowManager = windowManager;
this.model = model;
}
public IResult Configure(Action<TModel> configure)
{
this.configure = configure;
return this;
}
public override void Execute(ActionExecutionContext context)
{
if(configure != null)
configure(model);
windowManager.ShowDialog(model);
base.Execute(context);
}
}
edit Acabo de notar que me olvidé de agregar un ejemplo del ejemplo de IoC anterior, aquí va Con un patrón de contenedor IoC secundario se vería así
public IEnumerable<IResult> ShowDialog()
{
yield return Result.ShowDialog<MyViewModel>();
}
Sin un patrón de contenedor secundario, tendría que inyectar el elemento primario dependiente en el hijo manualmente
yield return Result.ShowDialog<MyViewModel>().Configure(m => m.SomeData = this.SomeData);