Пассивный просмотр MVP - разделение данных представления и данных модели

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

Вопрос

Я реализовал триаду MVP, используя шаблон пассивного просмотра, т. е.представление содержит только простые методы получения и установки.Однако у меня возникают проблемы с разделением данных представления и данных модели.В частности, при обработке изменения состояния представления.

Триада используется для того, чтобы пользователь мог выбрать деталь из списка.Список деталей предоставляется моделью, причем каждая деталь однозначно идентифицируется уникальным идентификатором.

Допустим, детали выглядят следующим образом:

class Part
{
    int ID; // this code uniquely identifies the part within the model
    String partCode;
    String description;
    double voltage;
}

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

Список отображается в DataGridView, а часть выбирается щелчком по строке в DataGridView.

Идентификатор не должен отображаться пользователю, как и напряжение, поэтому модель создает таблицу данных, содержащую только код детали и описание.Этот DataTable присваивается ведущим свойству представления, которое сопоставляется свойству DataSource DataGridView.

class Presenter
{
    IView _view;
    IModel _model;

    //...///

    _view.Data = _model.GetFilteredData();
}

class Model
{
    public DataTable GetFilteredData()
    {
        // create a DataTable with the partCode and Description columns only
        // return DataTable
    } 
}

class View  //winform
{
      public DataTable Data
      {
          set 
          {
              this.dataGridView.Source = value;
          }
      }
}

Пока все идет хорошо.Представление отображает отфильтрованные данные в DataGridView.

Проблема, с которой я сталкиваюсь, заключается в возврате части, выбранной пользователем.

Представление не знает об уникальном идентификаторе, поскольку он не отображается, а уникальность другой информации не может быть гарантирована, поэтому невозможно однозначно идентифицировать выбранную деталь.

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

Пока что у меня есть следующие решения:

1) Представлению передается таблица данных, содержащая идентификатор, а затем отображение фильтруется таким образом, чтобы оно не отображалось пользователю.Затем тривиально вернуть идентификатор для выбранной строки.Проблема здесь в том, что теперь я загрязнил представление непроверенной логикой (фильтрация отображения).

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

    public int RowIndexSelected { get; private set; }

    //...//

    private void gridParts_CellEnter(object sender, DataGridViewCellEventArgs e)
    {
        if (SelectedPartChangedEvent != null)
        {
            RowIndexSelected = e.RowIndex;

            SelectedPartChangedEvent();            
        }
    }

3) Вариация на тему (2).Создайте объект-адаптер, который будет располагаться между презентером и представлением.Переместите строку с кодом преобразования идентификатора из модели в адаптер.Затем ведущий обрабатывает событие изменения части dataGridAdapters.

    public PartSelectDataGridAdapter(IPartSelectView view, PartCollection data)
    {
        _view = view;
        _data = data;

        _view.SelectedPanelChangedEvent += HandleSelectedPartChanged;
    }

    void HandleSelectedPartChanged()
    {
        int id = _data[_view.RowIndexSelected].ID;

        if (SelectedPartChanged != null)
        {
            SelectedPartChanged(id);
        }
    }

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

Как бы вы справились с этим - есть ли лучшее решение?

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

Решение

Идентификатор не должен отображаться для пользователя, равно как и напряжение, поэтому модель создает Таблицу данных, содержащую только Код детали и описание.

Простое решение: делай создайте столбец ID в datatable и скройте это в представлении datagrid.

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

Ранее я опубликовал простое решение;это более подробный ответ на поставленный вопрос

Есть ли причина, по которой вы не хотите сдавать экзамен List<Part> к виду?

Вы могли бы настроить сетку так, чтобы скрыть столбец id и voltage.Вы можете просто получить выбранный объект из источника привязки в представлении.Докладчик может запросить представление для этого выбора, или представление может вызвать SelectionChanged(Part selected) о ведущем.

Это означало бы, что вы больше не будете строго следовать пассивный просмотр закономерность, но контролирующий контроллер, потому что теперь ваше представление знает о модели.

Если вам это не нравится, вы можете представить просмотр модели, что вы уже делаете неявно с вашим DataTable.(Кстати, это не обязательно плохо.)

В вашем примере классы моделей знают о моделях представлений, потому что у вас есть методы в модели, которые их генерируют.Я бы посоветовал вам обратить это соотношение вспять:создайте методы в вашей модели представления, которые зависят от объектов вашей модели.Таким образом, вы сохраните свои классы моделей красивыми, чистыми и независимыми от всех данных пользовательского интерфейса, необходимых на уровне представления.

При использовании способа view model / supervising controller рассмотрите возможность отказа от концепции DataTable в пользу простых классов.

Редактировать:альтернатива - сделать представление полностью неосведомленным о модели:

Создайте экземпляр этого класса в presenter, где вы знаете как о модели, так и о модели представления:

public class PartViewModel
{
  object PartModel { get; set; }
  string Name { get; set; }
  string Description { get; set; }
}

Передать List<PartViewModel> в качестве источника данных для DataGridView.Вы можете вернуть выбранный объект PartViewModel презентатору (либо с помощью события, либо с помощью метода).Ведущий знает, что он может привести свойство PartModel обратно к экземпляру Part.Представлению не нужно ничего знать о модели, как вы говорите, вы предпочитаете.Но вы все равно можете использовать простой идентификатор объекта в презентаторе, избегая "сложного" поиска с использованием идентификаторов.

С обратным вызовом ведущего:

interface IPartListPresenter
{
  // other methods
  void SelectedPartChanged(PartViewModel nowSelected);
}

Предполагая, что partBindingSource является источником привязки, к которому подключен gridview, вы можете обработать событие currentChanged partBindingSource следующим образом:

private void partBindingSource_CurrentChanged(object sender, EventArgs e)
{
  _presenter.SelectedPartChanged(partBindingSource.Current as PartViewModel);
}

В ведущем:

public void SelectedPartChanged(PartViewModel nowSelected)
{
  if(nowSelected == null)
  {
    return;
  }
  part myPart = (Part) nowSelected.Part;
  // dos stuff
}

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

Я думаю, вы здесь немного неправильно поняли всю концепцию!

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

Мое предложение состоит в том, чтобы сохранить скрытый столбец в вашей таблице и передать событие выбранной строки вашему ведущему, а затем позволить ведущему приступить к работе!

Это будет правильное использование MVP.

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