3

我已经使用被动视图模式实现了一个 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 和描述的 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;
          }
      }
}

到现在为止还挺好。View 在 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 学习,因为它是可测试的,将逻辑排除在视图之外,并将数据视图排除在模型和演示者之外。

您将如何解决这个问题 - 有没有更好的解决方案?

4

3 回答 3

2

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

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

您可以配置网格以隐藏 id 和电压列。您可以简单地从视图中的绑定源获取所选对象。演示者可以查询视图以获取此选择,或者视图可以调用SelectionChanged(Part selected)演示者的 a。

这意味着您不再严格遵循被动视图模式,而是一个监督控制器,因为现在您的视图知道模型。

如果你不喜欢这个,你可以引入一个视图模型,你已经用你的 DataTable 隐式地做了。(这不一定是坏事,顺便说一句。)

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

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

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

在presenter中构造这个类的一个实例,你知道模型和视图模型:

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

将 aList<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
}

希望这可以帮助。

于 2010-12-08T11:55:36.463 回答
1

ID 不会显示给用户,电压也不会显示给用户,因此模型会创建一个仅包含 partCode 和描述的 DataTable。

简单的解决方案:数据表中创建一个 ID 列并将其隐藏在数据网格视图中

于 2010-12-08T11:50:54.550 回答
0

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

应该由 Presenter 处理,而不是 Model。模型应该只专注于它唯一的责任,否则,你让视图和模型太接近了!

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

这将是 MVP 的正确用法。

于 2010-12-08T11:52:35.277 回答