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

StackOverflow https://stackoverflow.com/questions/424927

  •  05-07-2019
  •  | 
  •  

Вопрос

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

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

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

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

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

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

Мэтью Райт нижеприведенные государства:

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

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

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

Это было полезно?

Решение

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

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

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

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

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

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

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

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

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

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

Как навигатор создает представление для 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 и переносом представления на Страницу)

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

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

Другие советы

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

Конструктор

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

Затем, в методе delete method в ViewModel ... что-то вроде

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

Это сделает его тестируемым, вы можете подключить другие реализации iMessage, которые на самом деле не отображают messagebox.Они могут просто выводиться на консоль в целях тестирования.Очевидно, что ваше приложение 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);
  ...

}

Что касается вашего другого вопроса о том, когда использовать команду, я создаю экземпляры представлений Add New или Details из рассматриваемого представления.Точно так же, как у вас в вашем примере. Представления создаются только другими представлениями в нашем приложении.Я не использую команду в таких случаях.Однако я использую свойства ViewModel родительского представления для дочернего представления.

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

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top