How to close dialog window from viewmodel (Caliburn+WPF)?
Asked Answered
B

3

19

I haveViewModel1 and View1 associated with it. I start dialog window from ViewModel2 (some another viewmodel) using IWindowManager object. The code from ViewModel2 class:

windowManager.ShowDialog(new ViewModel());

So, I have Dialog Window with View1 user control.

My answer is next - I can close that dialog window using red close button, but how to close it using my specific button (contained in View1 user control), something like "Cancel" button with close command (Command={Binding CancelCommand}), CancelCommand of course is contained in ViewModel1 class.

Blastomere answered 10/4, 2012 at 14:23 Comment(0)
H
44

It's even easier if your view model extends Caliburn.Micro.Screen:

TryClose();
Hash answered 19/7, 2012 at 20:15 Comment(2)
Well it does close the whole app only if you're trying to close the main application window.Hash
Hi @MichalT, I am showing dialog like await _dialogManager.ShowDialogAsync(item, new List<DialogResult>() { DialogResult.Cancel }); and I want to close it using timer. Inside tick event when I am using this.DialogHost().TryClose(DialogResult.Cancel); then this.DialogHost() is giving null.Fluent
T
11

You can get the current view (in your case the dialog window) with implementing the IViewAware interface on your ViewModel. Then you can call Close on the the view (the Window created as the dialog) when your command is executed.

The easiest why is to derive from ViewAware:

public class DialogViewModel : ViewAware
{
    public void ExecuteCancelCommand()
    {
        (GetView() as Window).Close();
    }
}

If you are not allowed to derive you can implement it yourself:

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;
}

Note: I've used Caliburn.Micro 1.3.1 for my sample.

Trivia answered 10/4, 2012 at 16:30 Comment(0)
D
5

A cleaner way (Subject of personal taste) that I use alot is to use the IResult pattern, this way you abstract the Window implemenation

Viewmodel

public IEnumerable<IResult> CloseMe()
{
    yield return new CloseResult();
}

Result code

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;
}

edit (Only needed for IoC): If you wanna take it a step further you do a base class for all screens

public abstract class ShellPresentationModel : Screen
{
    public ShellPresentationModel(IResultFactory resultFactory)
    {
        Result = resultFactory;
    }

    public IResultFactory Result { get; private set; }
}

This way you can inject dependencies with a IoC much easier, then your VIewmodel close method will look like this

public IEnumerable<IResult> CloseMe()
{
    yield return Result.Close();
}

An example on a IResult that uses dependency can be

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 Just noticed that i forgot to add an example of the above IoC exmaple, here goes With a child IoC container pattern it would look like this

public IEnumerable<IResult> ShowDialog()
{
    yield return Result.ShowDialog<MyViewModel>();
}

Without a child container pattern you would need to inject parent dependeync into the child manually

    yield return Result.ShowDialog<MyViewModel>().Configure(m => m.SomeData = this.SomeData);
Dumdum answered 16/4, 2012 at 13:38 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.