Подтвердить что ты не робот

Как закрыть диалоговое окно из viewmodel (Caliburn + WPF)?

У меня есть ViewModel1 и View1, связанные с ним. Я запускаю диалоговое окно из ViewModel2 (некоторая другая модель) с помощью объекта IWindowManager. Код из ViewModel2 class:

windowManager.ShowDialog(new ViewModel());

Итак, у меня есть диалоговое окно с пользовательским элементом управления View1.

Мой ответ следующий - я могу закрыть это диалоговое окно, используя красную кнопку закрытия, но как закрыть его с помощью моей конкретной кнопки (содержится в пользовательском элементе View1), что-то вроде кнопки "Отмена" с закрытой командой (Command={Binding CancelCommand}), CancelCommand, конечно, содержится в классе ViewModel1.

4b9b3361

Ответ 1

Это еще проще, если ваша модель просмотра расширяет Caliburn.Micro.Screen:

TryClose();

Ответ 2

Вы можете получить текущее представление (в вашем случае диалоговое окно) с реализацией интерфейса IViewAware на вашем ViewModel. Затем вы можете вызвать Close в представлении (Window, созданный как диалог), когда ваша команда выполнена.

Самый простой способ - извлечь из ViewAware:

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

Если вам не разрешено выводить, вы можете реализовать его самостоятельно:

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

Примечание. Я использовал Caliburn.Micro 1.3.1 для своего образца.

Ответ 3

Более чистый способ (тема личного вкуса), который я использую много, - использовать шаблон IResult, таким образом, вы абстрагиваете реализацию окна

ViewModel

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

Код результата

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 (требуется только для IoC). Если вы хотите сделать это еще дальше, вы делаете базовый класс для всех экранов

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

    public IResultFactory Result { get; private set; }
}

Таким образом, вы можете легче вложить зависимости с IoC, тогда ваш метод закрытия VIewmodel будет выглядеть следующим образом:

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

Пример в IResult, который использует зависимость, может быть

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 Только что заметил, что я забыл добавить пример вышеприведенного IoC exmaple, здесь идет С детским шаблоном контейнера IoC это будет выглядеть как

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

Без шаблона дочернего контейнера вам нужно будет вручную ввести parent dependeync в дочерний элемент

    yield return Result.ShowDialog<MyViewModel>().Configure(m => m.SomeData = this.SomeData);