2015-04-28 12 views
2

В WPF у меня есть класс ViewModel под названием Malfunctions, и он имеет ObservableCollection of PartMalfunctions. Как правило, в ObservableCollection существует от 10 до 15 объектов PartMalfunction; сколько из них зависит от других параметров, выходящих за рамки этого вопроса.Какой слой должен содержать ICommand?

У меня есть xaml, у которого есть DataGrid, который привязывается к этому ObservableCollection. В DataGrid я отображаю различные свойства PartMalfuction (т.е. - описание, имя и т. Д.), И у меня есть кнопка Start timer, которую пользователь может щелкнуть. Кнопка таймера запуска привязана к ICommand StopwatchCmd в классе модели PartMalfunction (вы можете увидеть все это ниже в коде).

У меня есть вопрос: У меня есть StopwatchCmd в неправильном слое (т.е. - принадлежит ли он в Mommunctions ViewModel)? Я действительно боролся с этим и пытался разобраться в этом сам, но я продолжаю ударять по стене, так сказать, потому что StopwatchCmd в классе Model отлично работает! Я имею в виду, что он способен выполнять там и выполнять любые бизнес-правила, в которых он нуждается, и взаимодействовать только с этим экземпляром объекта, для которого он запускал. Если я вставлю его в ViewModel, то мне кажется, что мне нужно пройти больше работы, чтобы заставить его делать то, что уже сделано.

Обратите внимание, что я не указал код из Mommunctions ViewModel, поскольку он не имеет отношения к этому вопросу. Вот код для Mommunctions ViewModel.

public class Malfunctions : ViewModelBase { 
     public ObservableCollection<Model.PartMalfunction> AllPartMalfunctions { 
      get; 
      private set; 
     } 
} 

Класс Модель PartMalfunction выглядит примерно так:

public class PartMalfunction : INotifyPropertyChanged { 
    public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged; 

    private void OnPropertyChanged(string propertyName) { 
     if (PropertyChanged != null) { 
      PropertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName)); 
     } 
    } 

    private int _seconds; 
    private string _stopwatchText = string.Empty; 
    private bool _isStopwatchInProgress = false; 
    System.Windows.Threading.DispatcherTimer _timer = new System.Windows.Threading.DispatcherTimer(); 
    RelayCommand _stopwatchCmd; 

    public ICommand StopwatchCmd { 
     get { 
      if (_stopwatchCmd == null) 
       _stopwatchCmd = new RelayCommand(param => this.StopwatchClick()); 
      return _stopwatchCmd; 
     } 
    } 
    public bool IsStopwatchInProgress { 
     get { 
      return _isStopwatchInProgress; 
     } 
     set { 
      _isStopwatchInProgress = value; 
      OnPropertyChanged("IsStopwatchInProgress"); 
     } 
    } 
    public string StopwatchText { 
     get { 
      return _stopwatchText; 
     } 
     set { 
      _stopwatchText = value; 
      OnPropertyChanged("StopwatchText"); 
     } 
    } 
    private void StopwatchClick() { 

     if (!this.IsStopwatchInProgress) { 
      // Start the timer 
      _seconds = 0; 

      // Will immediately update the timer text to "00:00:00" 
      this.StopwatchText = GetElapsed(); 

      _timer.Tick += DispatcherTimer_Tick; 
      _timer.Interval = new TimeSpan(0, 0, 1); // Ticks every second 
      _timer.Start(); 

      this.IsStopwatchInProgress = true; 
     } 
     else { 
      // Stop the timer 
      _timer.Stop(); 
      _timer.Tick -= DispatcherTimer_Tick; 
      _seconds = 0; 

      this.IsStopwatchInProgress = false; 
     } 
    } 
    private void DispatcherTimer_Tick(object sender, System.EventArgs e) { 
     _seconds += 1; 

     this.StopwatchText = GetElapsed(); 
    } 
    private string GetElapsed() { 
     int hour = 0, min = 0, sec = 0; 

     if (_seconds > 59) { 
      min = (int)_seconds/60; 
      sec = _seconds % 60; 

      if (min > 59) { 
       hour = (int)min/60; 
       min = min % 60; 
      } 
     } 
     else 
      sec = _seconds; 

     string elapsed = hour < 10 ? "0" + hour.ToString() : hour.ToString(); 
     elapsed += ":" + (min < 10 ? "0" + min.ToString() : min.ToString()); 
     elapsed += ":" + (sec < 10 ? "0" + sec.ToString() : sec.ToString()); 

     return elapsed; 
    } 
} 
+0

Для меня то, что вы думаете о модели, на самом деле является ViewModel, более конкретно родительский ViewModel (Malfunctions) имеет коллекцию дочерних ViewModels (PartMalfunction), выставленных как коллекция (ObservableCollection ), что означает, что нет проблема с свойствами ICommand в классе PartMalfunction. – AwkwardCoder

+0

@AwkwardCoder - Я понимаю, о чем вы говорите. Так что это действительно может быть проблемой с моим восприятием модели или ViewModel. Не возражаете ли вы поместить свой комментарий в качестве ответа? – Jagd

+0

в порядке, я отвечу на это как ответ, но вопрос может быть воспринят как в первую очередь на основе мнения :) – AwkwardCoder

ответ

4

Вот как я это организовать в MVVM:

  • Модели: только сущности, партиалы классов, которые потребляются.
  • Вид: Xaml и код, за которым могут отображаться/манипулировать Модели, предоставленные моделью просмотра.
  • ViewModel: Этот слой находится там, где хранится логика и сохраняются данные (модели), извлеченные из БД. Это проводник между изображением и моделями. Он может обращаться к базам данных, создавать таймеры, удерживать ICommands, .... до тех пор, пока относится к бизнес-логике только. Нет прямой обработки.

Помните, когда три яруса были в ярости? Если вы думаете о MVVM как о трех уровнях, которые могут помочь. ИМХО


Какой слой должен содержать ICommand?

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

+2

Для меня я бы не согласился с идеей «этот слой, где находится бизнес-логика». ViewModels больше похожи на контроллеры в классическом смысле MVC, и они могут организовать процесс. Фактическая бизнес-логика содержится в модели и \ или наборе классов обслуживания, которые заполняют \ обновляют модель. Но это всего лишь мнение :) – AwkwardCoder

+0

@AwkwardCoder Я думаю, что ваши мысли верны, и я прагматичен в этом, нельзя полностью требовать правила * одного размера для всех * для всех ситуаций. Я чувствовал, что мой * opionion * был немного отличается, поэтому я бросил его на ринг. Хороший ответ с вашей стороны. :-) – OmegaMan

+0

Все это очень сложно и вдвойне из-за небольших различий в понимании того, что включает в себя модель и ViewModel. Я следил за видео в Интернете, в котором показано, как настроить MVVM в приложении WPF, и создатель видео использовал свои классы Model как объекты в ObservableCollections. Вот почему я так запутался! – Jagd

3

Этот вопрос может восприниматься как прежде всего, как на основе мнения, но я считаю, что это помогает меньше разработчикам опыт понимания границ Model-View-ViewModel.

Для меня то, что вы думаете о модели, на самом деле является ViewModel, более конкретно, родительский ViewModel (Malfunctions) имеет коллекцию дочерних ViewModels (PartMalfunction), представленных как коллекция (ObservableCollection), что означает, что нет проблема с свойствами ICommand в классе PartMalfunction.

Если я нахожу класс модели, делающий много форматов данных для отображения (текст, даты и т. Д.), То, скорее всего, это ViewModel, такая вещь является репутацией ViewModel. Также для меня класс Model не затрагивает интерфейс INotifyPropertyChanged, уведомления выполняются с использованием событий (или потоков Rx), и абонент (ViewModel) может затем выбрать, как и когда обновлять пользовательский интерфейс.

2

ICommand имеет два интересных члена. Execute, который определяет действие, которое другой объект может запросить у владельца команды, и CanExecute, который определяет, должен ли другой запрашивать действие. Это очень заметно в названиях ICommand, но если вы внимательно посмотрите, они приземлится ICommand - идеальный дом в слое ViewModel. Модели просмотра могут выставлять некоторые действия с контролем, когда их разрешено выполнять, до неизвестного вида. Поскольку команды обычно являются общедоступными свойствами модели представления, вы можете легко привязать их к элементам управления WPF без какой-либо жесткой связи. Например, Button может привязываться к команде, чтобы определить, что она делает, не зная, какой тип ее DataContext (модель обзора) есть.

Если вы попытались разместить свой ICommand на свой взгляд, возможно, они будут громоздкими по сравнению с методом, поскольку представления имеют доступ к их собственным методам. Из-за этого ICommand не очень хорошо подходит для слоя «Вид».

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

Смежные вопросы