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

M-V-VM - любые примеры использования команд в ViewModel?

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

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

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

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

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

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

Мэтью Райт:

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

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

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

4b9b3361

Ответ 1

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

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

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

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

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

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

Как модель просмотра получает навигационную службу?

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

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

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

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

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

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

Конечно, для представления требуется конструктор, принимающий 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 и обертывает представление на страницу)

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

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

Ответ 2

В случае вашего случая с сообщением об ошибке я абстрагирую сообщения через интерфейс. Аналогично этому. Я также вставляю эти интерфейсы для своего 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();
}

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

Ответ 3

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

Ответ 4

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