M-V-VM - Есть ли примеры использования команд во ViewModel? - PullRequest
16 голосов
/ 08 января 2009

Я разрабатывал очень большое LOB-приложение, используя мой вид M-V-VM, который я называю M-V-MC (Model-View-ModelController), который является своего рода комбинацией между M-V-C и M-V-VM. Я опубликовал этот ответ о том, как в MV-VM создаются экземпляры представлений, на вопрос " какие самые распространенные ошибки сделаны в wpf-разработке " .

Сэм сделал следующий комментарий относительно моего ответа:

Это создает дополнительный вопрос: как ты создаешь представления? я использую RelayCommands для привязки действий от вид на модель представления, поэтому вид даже не знает, что действие имеет уволен, не знает, что он должен открыть новый взгляд. Решение: создать событие в виртуальная машина для View для подписки?

Когда я первоначально начал разработку MV-VM, у меня было представление, что ВСЕ должно жить во ViewModel, и я изучил множество примеров от таких парней, как Джош Смит и Карл Шиффлетт, Однако мне еще предстоит привести хороший пример того, когда команда должна жить во ViewModel.

Например, допустим, у меня есть ListView, в котором отображаются клиенты, и кнопка, которую я нажимаю, чтобы позволить мне редактировать текущего выбранного клиента. ListView (View) привязан к CustomerVM (ViewModel). Нажатие на кнопку запускает команду EditCustomerCommand, которая открывает всплывающее окно, которое позволяет мне редактировать все свойства CustomerVM. Где живет этот EditCustomerCommand? Если это включает в себя открытие окна (функциональность пользовательского интерфейса), не должно ли оно быть определено в коде позади представления? alt text

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

Мэтью Райт указано ниже:

Новое и удалить из списка будет хорошие примеры. В этих случаях пустой запись добавлена ​​или текущая запись удаляется ViewModel. любой действия, предпринятые по мнению должны быть в ответ на происходящие события.

Так что, если я нажму на новую кнопку, что произойдет? Новый экземпляр CustomerVM создается родительской ViewModel и добавляется в его коллекцию, верно? Так как же тогда откроется мой экран редактирования? Представление должно создать новый экземпляр Customer ViewModel и передать его методу ParentVM.Add (newCreatedVM), верно?

Допустим, я удаляю запись клиента через DeleteCommand, живущую на виртуальной машине. виртуальная машина вызывает бизнес-уровень и пытается удалить запись. Не может, поэтому возвращает сообщение в виртуальную машину. Я хочу отобразить это сообщение в диалоговом окне. Как представление получает сообщение из командного действия?

Ответы [ 4 ]

3 голосов
/ 16 января 2009

Никогда не думал, что увижу, что меня цитируют в вопросе.

Я некоторое время обдумывал этот вопрос и принял довольно прагматичное решение для своей кодовой базы:

В моей кодовой базе ViewModel вызывается, когда происходят какие-то действия, и я хотел, чтобы так и оставалось. Кроме того, я не хочу, чтобы ViewModel управлял представлениями.

Что я сделал?
Я добавил контроллер для навигации:

public interface INavigation
{
  void NewContent(ViewModel viewmodel);
  void NewWindow(ViewModel viewmodel);
}

Этот контроллер содержит два действия: NewContent () отображает новое содержимое в текущем окне, NewWindow () создает новое окно, заполняет его содержимым и показывает его.
Конечно, мои viewmodels не имеют ни малейшего представления, какой вид показать. Но они знают, какую модель представления они хотят показать, поэтому в соответствии с вашим примером при выполнении DeleteCommand он вызвал бы функцию службы навигации NewWindow (new ValidateCustomerDeletedViewModel ()) , чтобы показать окно, в котором указано, что клиент имеет был удален »(избыточно для этого простого ящика сообщений, но было бы легко иметь специальную функцию навигатора для простых ящиков сообщений).

Как viewmodel получает навигационный сервис?

Мой класс viewmodel имеет свойство для контроллера навигации:

public class ViewModel
{
  public INavigation Navigator { get; set; }
  [...]
}

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

Как навигатор создает представление для модели вида?

У вас может быть простой список, представление которого создавать, для какой модели представления, в моем случае я могу использовать простое отражение, поскольку имена совпадают:

public static FrameworkElement CreateView(ViewModel viewmodel)
{
  Type vmt = viewmodel.GetType();
  // big bad dirty hack to get the name of the view, but it works *cough*
  Type vt = Type.GetType(vmt.AssemblyQualifiedName.Replace("ViewModel, ", "View, ")); 
  return (FrameworkElement)Activator.CreateInstance(vt, viewmodel);
}

Конечно, представлению нужен конструктор, принимающий модель представления в качестве параметра:

public partial class ValidateCustomerDeletedView : UserControl
{
  public ValidateCustomerDeletedView(ValidateCustomerDeletedViewModel dac)
  {
    InitializeComponent();
    this.DataContext = dac;
  }
}

Как выглядит мое окно?

Простой: мое главное окно реализует интерфейс INavigation и показывает стартовую страницу при создании. Убедитесь сами:

public partial class MainWindow : Window, INavigation
{
  public MainWindow()
  {
    InitializeComponent();
    NewContent(new StartPageViewModel());
  }

  public MainWindow(ViewModel newcontrol)
  {
    InitializeComponent();
    NewContent(newcontrol);
  }

  #region INavigation Member
  public void NewContent(ViewModel newviewmodel)
  {
    newviewmodel.Navigator = this;
    FrameworkElement ui = App.CreateView(newviewmodel);
    this.Content = ui;
    this.DataContext = ui.DataContext;
  }

  public void NewWindow(ViewModel viewModel)
  {
    MainWindow newwindow = new MainWindow(viewModel);
    newwindow.Show();
  }
  #endregion
}

(Это одинаково хорошо работает с окном NavigationWindow и переносом представления на страницу)

Конечно, это можно проверить, так как навигационный контроллер может быть легко смоделирован.

Я не совсем уверен, что это идеальное решение, но сейчас оно прекрасно работает для меня. Любые идеи и комментарии приветствуются!

1 голос
/ 09 января 2009

Для вашего случая удаления сообщения я абстрагирую почтовые ящики через интерфейс. Похоже на это. Я также внедряю эти интерфейсы для своего приложения WPF.

Конструктор

    public MyViewModel(IMessage msg)
    {
      _msg = msg;
    }

Затем в методе метода удаления в ViewModel ... что-то вроде

    public void Delete()
    {
      if(CanDelete)
      {
        //do the delete 
      }
      else
      {
        _msg.Show("You can't delete this record");
      }
    }

Это сделает его тестируемым, вы можете подключить различные реализации IMessage, которые фактически не отображают окно сообщения. Они могут просто напечатать на консоль, в целях тестирования. Очевидно, что ваше приложение WPF может иметь реализацию типа

public class MessageBoxQuestion : IMessage
{
   public void Show(string message)
   {
     MessageBox.Show(message);
   }
}

Выполнение этого делает тестирование различных маршрутов (например, диалогов Да / Нет) очень простым и понятным. Вы можете представить подтверждение удаления. Вы можете использовать конкретный экземпляр IMessage для возврата true / false для подтверждения или макетировать контейнер во время теста.

[Test]
public void Can_Cancel_Delete()
{
  var vm = new ProductViewModel(_cancel);
  ...

}
[Test]
public void Can_Confirm_Delete()
{
  var vm = new ProductViewModel(_yes);
  ...

}

В связи с вашим другим вопросом о том, когда использовать Команду, я создаю экземпляры представлений «Добавить новый» или «Детали» из рассматриваемого представления. Так же, как в вашем примере. Представления создаются только другими представлениями в нашем приложении. Я не использую команду в этих случаях. Однако я использую свойства ViewModel родительского представления для дочернего представления.

public void Object_DoubleClick(object sender, EventArgs e)
{
  var detailView = new DetailView(ViewModel.Product);
  detailView.Show();
}

Надеюсь, это поможет!

0 голосов
/ 09 января 2009

Один из способов заключается в использовании объекта параметра команды, который может изменить бизнес-уровень, и ваша ВМ может обрабатывать после выполнения команды.

0 голосов
/ 08 января 2009

Новое и удаление из списка будет хорошим примером. В этих случаях добавляется пустая запись или текущая запись удаляется с помощью ViewModel. Любое действие, предпринимаемое представлением, должно быть ответом на происходящие события.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...