3

Создание и показ дочернего окна из MainViewModel:

    public ICommand OpenWindow_AddNewBid_Command => new RelayCommand<object>(p => OpenWindow_AddNewBid());
    private void OpenWindow_AddNewBid()
    {            
        var wnd = new AddNewBidWindow();           
        if (wnd.ShowDialog() == true)
        {
            AddNewBidViewModel vm = (AddNewBidViewModel)wnd.DataContext;
            AddNewBidToCollection(vm.CreatedBid);
        }
    }

В ViewModel дочернего окна есть команда, которая привязана к кнопке OK.

    public ICommand AddNewBidCommand => new RelayCommand<object>(p => AddNewBid(),
                                                                 p => IsValid);

XAML:

<Button Command="{Binding AddNewBidCommand}" IsDefault="True">ОК</Button>

Не могу понять, как сделать, что бы выполнилась команда привязанная к кнопке и окно закрылось?

MaximK
  • 2,691
  • Чем не устраивает простой вариант - вызвать метод Close у окна? – Monk Apr 05 '17 at 12:07
  • 1
    из VM? Она же ничего не знает об окне? – MaximK Apr 05 '17 at 12:10
  • 4
    Как то вы сложно выражаетесь. Ваш вопрос должен звучать проще. Что-то вроде "как в рамках MVVM принято закрывать окно?". – vitidev Apr 05 '17 at 12:12
  • Можно передать окно параметром в команду. Можно - найти в вм связанные окна и закрыть их все. Идеальных вариантов я тут не видел. – Monk Apr 05 '17 at 12:14
  • Где-то было на моей памяти несколько похожих вопросов. – VladD Apr 05 '17 at 14:51
  • 1
    Вот: http://ru.stackoverflow.com/q/525998/10105 и http://ru.stackoverflow.com/q/449911/10105 – VladD Apr 05 '17 at 14:56
  • Ну и если мы говорим о MVVM-закрытии окна, то имеет смысл говорить и о MVVM-создании окна. В вопросе окно создаётся в VM-коде. – VladD Apr 05 '17 at 14:59

2 Answers2

3

Самый простой вариант, не нарушающий принципы MVVM.

View:

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();

        var vm = new MainViewModel();
        this.DataContext = vm;
        vm.CloseHandler += (sender, args) => this.Close();
    }
}

ViewModel:

public class MainViewModel : BindableBase
{
    public EventHandler CloseHandler;

    public ICommand AddNewBidCommand => new RelayCommand(action =>
    {
        // ...

        var handler = CloseHandler;
        if (handler != null) handler.Invoke(this, EventArgs.Empty);

    }, canExecute => true);

}

Вариант с передачей экземпляра окна в команду через CommandParameter возможен, но будет нарушать принципы MVVM.

Nikita
  • 1,980
  • Какой принцип будет нарушать передача окна через параметр? – trydex Apr 05 '17 at 14:15
  • 1
    @maxwell появляется зависимость VM от конкретного View. Таким образом теряется весь смысл использования этого паттерна. Вью-модель должна "общаться" с представлением только посредством механизма привязки данных (bindings) – Nikita Apr 05 '17 at 14:23
  • @Nikita, если окно будет реализовывать интерфейс IClosable, то при передаче окна через параметр ничего не нарушится) – Gardes Apr 06 '17 at 05:35
3

Изящный вариант закрытия окна из VM в духе MVVM (декларативный и через биндинги)

Вспомогательный класс

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)
    {
        window.DialogResult = e.NewValue as bool?;
        if (window.DialogResult != null)
            window.Close();
    }
}

public static void SetDialogResult(Window target, bool? value)
{
    target.SetValue(DialogResultProperty, value);
}

}

VM

class MyViewModel
{

private bool? _dialogResult;

public bool? DialogResult { get { return _dialogResult; } protected set { _dialogResult = value; OnPropertyChanged("DialogResult"); } }

public ICommand OkCommand {get;}=new RelayCommand(Save);

private void Save()
{
    ....
    DialogResult = true;
}

}

XAML

<Window ...DialogCloser.DialogResult="{Binding DialogResult}" .../>

При выставлении у VM DialogResult=true/false форма закроется и у нее будет установлен DialogResult, так что проверка результата будет работать

if(view.ShowDialog()==true)..

upd: Если нужно при нажатии кнопки закрытия окна нужно не закрывать сразу, а в OnClosing вызвать вьюмодель с просьбой "останови все, а я подожду" и эта вьюмодель в итоге установит DialogResult=true, то будет ошибка вида "во время закрытия окна нельзя изменить Visibility". Решается await Task.Yield(); в OnClosing

vitidev
  • 3,887