MVP被动视图 - 将视图数据和模型数据分开

时间:2010-12-08 10:27:57

标签: c# winforms design-patterns mvp passive-view

我使用被动视图模式实现了MVP三元组 - 即视图仅包含简单的getter和setter。但是,我无法分离视图数据和模型数据。特别是在处理视图状态的更改时。

三元组用于使用户能够从列表中选择一个部件。零件清单由模型提供,每个零件由唯一ID唯一标识。

让我们说部件看起来像这样:

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

视图向用户显示列表,并允许他们选择部件

列表显示在DataGridView中,通过单击dataGridView中的行来选择部件。

ID不会显示给用户,也不会显示电压,因此模型会创建一个仅包含partCode和description的DataTable。此DataTable由演示者分配给视图上的属性,该属性映射到DataGridView的DataSource属性。

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中显示已过滤的数据。

我遇到的问题是返回用户选择的部分。

视图不知道唯一ID,因为它没有显示,而且其他信息不能保证是唯一的 - 因此无法唯一识别所选部分。

基本上我正在尝试将视图数据(所选行)转换为模型数据(所选部分),而没有一个组件使用其他数据。

到目前为止,我有以下解决方案:

1)视图传递包含ID的DataTable,然后过滤显示,以便不向用户显示。然后返回所选行的ID是微不足道的。这里的问题是我现在已经用未经测试的逻辑(过滤显示)污染了视图。

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学习,因为它是可测试的,将逻辑排除在视图之外,并从模型和演示者中查看数据。

你会如何解决这个问题?有更好的解决方案吗?

3 个答案:

答案 0 :(得分:2)

我之前发布了一个简单的解决方案;这是对问题的更详细的回复

您是否有理由不想将List<Part>传递给视图?

您可以配置网格以隐藏ID和电压列。您只需从视图中的绑定源获取所选对象即可。演示者可以查询该选择的视图,或者视图可以在演示者上调用SelectionChanged(Part selected)

这意味着您不再严格遵循passive-view模式,而是supervising controller,因为现在您的视图了解模型。

如果您不喜欢这样,可以引入视图模型,您已经使用DataTable隐式执行了该操作。 (这不一定是坏事,顺便说一句。)

在您的示例中,模型类知道视图模型,因为您在模型上有生成它们的方法。我建议您反转这种关系:在视图模型上创建依赖于模型对象的方法。 这样,您将保持模型类的美观和干净,并且独立于表示层中所需的所有UI数据。

使用视图模型/监督控制器方式时,请考虑删除DataTable概念以支持简单类。

编辑:使视图完全不知道模型的替代方法:

在演示者中构造此类的实例,您可以在其中了解模型和视图模型:

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是gridview连接的bindingsource,你可以像这样处理partBindingSource的CurrentChanged事件:

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
}

希望这有帮助。

答案 1 :(得分:1)

  

不会向ID显示ID   用户,也不是电压,   因此模型创建了一个   DataTable只包含   partCode和description。

简单解决方案:执行在数据表和hide it in in the datagrid view中创建ID列。

答案 2 :(得分:0)

我认为你在这里误解了整个概念!

Presenter应该处理这个,而不是模型。模型应该只专注于自己的责任,如果没有,你保持视图和模型太近了!

我的建议是在表格中保留一个隐藏的列,将所选行的事件传递给Presenter,然后让Presenter处理工作!

这将是MVP的正确用法。