2010-12-08 3 views
2

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

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

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

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

Вид отображает список пользователей и позволяет им выбрать часть

Список отображается в DataGridView и часть выбрана нажав на строку в файле dataGridView.

Идентификатор пользователя не должен отображаться, и ни одно из них не является напряжением, поэтому модель создает DataTable, который содержит только код детали и описание. Этот 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) Представление передается DataTable, который содержит идентификатор, а затем фильтрует дисплей так, чтобы он не отображается пользователю. Затем тривиально вернуть идентификатор для выбранной строки. Проблема здесь в том, что я теперь загрязнил представление логикой, которая не тестировалась (фильтрация дисплея).

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

public int RowIndexSelected { get; private set; } 

    //...// 

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

      SelectedPartChangedEvent();    
     } 
    } 

3) Вариант на (2). Создайте объект адаптера, чтобы сидеть между презентатором и представлением. Переместите код преобразования строки в ID из модели в адаптер. Затем ведущий обрабатывает измененное событие детали 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, так как это проверяемым, хранит логику из данных, и вид из модели и ведущий.

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

+0

Я должен добавить, что я следовал методологии Presenter First, описанной здесь http://www.atomicobject.com/pages/Presenter+First. – Kildareflare 2010-12-08 13:31:51

ответ

1

Идентификатор не не будет отображаться на пользователя, и ни это напряжение, поэтому модель создает DataTable, который содержит только partCode и описание.

Простое решение: сделать создать столбец ID в DataTable и hide it in in the datagrid view.

0

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

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

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

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

+0

Предполагая, что ведущий сообщает, какой столбец скрывает это, все равно потребуется код в представлении, чтобы скрыть столбец идентификатора, не так ли? Или есть способ скрыть столбец в самом DataTable. Я хочу сохранить представление пассивным и ограничить интерфейс только получателями и сеттерами. – Kildareflare 2010-12-08 13:53:11

+1

Я не вижу большой проблемы, позволяя виду скрыть столбец, поскольку это строго относится к представлению-состоянию/поведению. – 2010-12-09 06:53:17

2

Я отправил простое решение раньше; это более подробный ответ на вопрос

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

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

Это означает, что вы больше не будете строго следовать шаблону passive-view, но supervising controller, потому что теперь ваш взгляд знает о модели.

Если вам не нравится эта информация, вы можете ввести модель:, которую вы уже используете неявным образом с помощью DataTable. (Это не обязательно плохо, кстати.)

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

При использовании модели представления/контроля над контроллером рассмотрите возможность отказаться от концепции DataTable в пользу простых классов.

EDIT: альтернатива, чтобы сделать вид совершенно невежественный модели:

Construct экземпляр этого класса в ведущий, где вы знаете, в обе модели и модели представления:

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

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

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

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

Предполагая partBindingSource является BindingSource 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 
} 

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