Как ViewModel должен закрыть форму? - PullRequest
235 голосов
/ 02 февраля 2009

Я пытаюсь изучить WPF и проблему MVVM, но столкнулся с проблемой. Этот вопрос похож, но не совсем такой , как этот (обработка-диалогов-в-wpf-с-mvvm) ...

У меня есть форма "Логин", написанная с использованием шаблона MVVM.

Эта форма имеет ViewModel, которая содержит имя пользователя и пароль, которые связаны с представлением в XAML с использованием обычных привязок данных. Он также имеет команду «Логин», которая связана с кнопкой «Логин» в форме, кроме того, используется обычная привязка данных.

Когда запускается команда «Войти», она вызывает функцию в ViewModel, которая отключается и отправляет данные по сети для входа в систему. Когда эта функция завершается, есть 2 действия:

  1. Логин был неверен - мы просто показываем MessageBox и все в порядке

  2. Логин был действителен, нам нужно закрыть форму входа в систему и сделать так, чтобы она возвращала значение true как DialogResult ...

Проблема в том, что ViewModel ничего не знает о реальном представлении, так как он может закрыть представление и сказать ему, чтобы он возвращал определенный DialogResult? Я мог бы вставить некоторый код в CodeBehind и / или передать View через ViewModel, но похоже, что он полностью победил бы весь смысл MVVM ...


Обновление

В конце я просто нарушил «чистоту» шаблона MVVM, и View опубликовал событие Closed и выставил метод Close. Тогда ViewModel просто вызовет view.Close. Представление известно только через интерфейс и подключено через контейнер IOC, поэтому тестирование и ремонтопригодность не теряются.

Кажется довольно глупым, что принятый ответ при -5 голосов! В то время как я хорошо осведомлен о хороших чувствах, которые можно получить, решая проблему, будучи «чистым», Конечно, я не единственный, кто думает, что 200 строк событий, команд и поведений просто для того, чтобы избежать однострочного метода в название «узоры» и «чистота» немного смешно ....

Ответы [ 26 ]

0 голосов
/ 15 июля 2012

Я реализовал решение Джо Уайта, но столкнулся с проблемами со случайным " DialogResult может быть установлен только после того, как Окно создано и показано как ошибки диалога ".

Я держал ViewModel после закрытия View и иногда позже открывал новый View, используя ту же виртуальную машину. Похоже, что закрытие нового View до того, как старый View был собран мусором, привело к DialogResultChanged попытке установить свойство DialogResult в закрытом окне, что спровоцировало ошибку.

Мое решение было изменить DialogResultChanged , чтобы проверить свойство IsLoaded окна:

private static void DialogResultChanged(
    DependencyObject d,
    DependencyPropertyChangedEventArgs e)
{
    var window = d as Window;
    if (window != null && window.IsLoaded)
        window.DialogResult = e.NewValue as bool?;
}

После внесения этого изменения любые вложения в закрытые диалоги игнорируются.

0 голосов
/ 21 марта 2012

Другое решение - создать свойство с INotifyPropertyChanged в View Model, например DialogResult, а затем в Code Behind написать следующее:

public class SomeWindow: ChildWindow
{
    private SomeViewModel _someViewModel;

    public SomeWindow()
    {
        InitializeComponent();

        this.Loaded += SomeWindow_Loaded;
        this.Closed += SomeWindow_Closed;
    }

    void SomeWindow_Loaded(object sender, RoutedEventArgs e)
    {
        _someViewModel = this.DataContext as SomeViewModel;
        _someViewModel.PropertyChanged += _someViewModel_PropertyChanged;
    }

    void SomeWindow_Closed(object sender, System.EventArgs e)
    {
        _someViewModel.PropertyChanged -= _someViewModel_PropertyChanged;
        this.Loaded -= SomeWindow_Loaded;
        this.Closed -= SomeWindow_Closed;
    }

    void _someViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        if (e.PropertyName == SomeViewModel.DialogResultPropertyName)
        {
            this.DialogResult = _someViewModel.DialogResult;
        }
    }
}

Самый важный фрагмент - _someViewModel_PropertyChanged. DialogResultPropertyName может быть некоторой общедоступной константной строкой в ​​SomeViewModel.

Я использую этот вид трюка, чтобы внести некоторые изменения в View Controls в случае, когда это трудно сделать во ViewModel. OnPropertyChanged во ViewModel вы можете делать все, что вы хотите в View. ViewModel по-прежнему «тестируется модулем», и некоторые небольшие строки кода в коде не имеют значения.

0 голосов
/ 21 июля 2011
Application.Current.MainWindow.Close() 

Этого достаточно!

0 голосов
/ 05 марта 2011

Просто чтобы добавить к огромному количеству ответов, я хочу добавить следующее. Предполагая, что у вас есть ICommand в вашей ViewModel, и вы хотите, чтобы эта команда закрыла свое окно (или любое другое действие в этом отношении), вы можете использовать что-то вроде следующего.

var windows = Application.Current.Windows;
for (var i=0;i< windows.Count;i++ )
    if (windows[i].DataContext == this)
        windows[i].Close();

Это не идеально, и может быть трудно проверить (так как трудно подделать / заглушить статическое электричество), но оно чище (ИМХО), чем другие решения.

Erick

0 голосов
/ 24 мая 2018

Поведение - самый удобный способ здесь.

  • С одной стороны, он может быть привязан к данной модели представления (которая может сигнал "закрой форму!")

  • С другой стороны, у него есть доступ к самой форме, поэтому он может подписаться на необходимые события, связанные с формой, или показать диалог подтверждения, или что-либо еще.

Написание необходимого поведения может показаться скучным в первый раз. Однако теперь вы можете использовать его в каждой нужной форме, используя точный однострочный фрагмент XAML. И при необходимости вы можете извлечь его как отдельную сборку, чтобы его можно было включить в любой следующий проект, который вы хотите.

0 голосов
/ 27 марта 2014

В итоге я смешал Ответ Джо Уайта и некоторый код из Ответ Адама Миллса , поскольку мне нужно было показать пользовательский элемент управления в программно созданном окне. Таким образом, DialogCloser не обязательно должен быть в окне, он может быть в самом пользовательском элементе управления

<UserControl ...
    xmlns:xw="clr-namespace:Wpf"
    xw:DialogCloser.DialogResult="{Binding DialogResult}">

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

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

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

  public static class Extensions
  {
    public static Window GetWindow(this DependencyObject sender_)
    {
      Window window = sender_ as Window;        
      return window ?? Window.GetWindow( sender_ );
    }
  }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...