我使用被动视图模式实现了MVP三合会 - IE视图仅包含简单的Getters和Setter。但是,我在分离视图数据和模型数据时遇到了麻烦。特别是在处理视图状态的更改时。

该三合会用于使用户从列表中选择零件。零件列表由模型提供,每个零件都以唯一的ID识别。

假设这些部分看起来像这样:

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

该视图将列表显示给用户,并允许他们选择零件

该列表显示在DataGridView中,并通过单击DataGridView中的一行来选择零件。

ID不应显示给用户,电压也不是,因此该模型创建一个仅包含零件代码和描述的数据表。演示者分配给该数据词到映射到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然后过滤显示的数据表,以免向用户显示。然后,返回所选行的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的学习是可以测试的,因此将逻辑远离视图,并将数据视为模型和演示者。

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

有帮助吗?

解决方案

ID不应显示给用户,电压也不是,因此该模型创建一个仅包含零件代码和描述的数据表。

简单解决方案: 在DataTable中创建一个ID列,并 将其隐藏在DataGrid视图中.

其他提示

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

有什么原因你不想通过 List<Part> 看?

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

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

如果您不喜欢这样,可以介绍 查看模型, ,您已经对数据进行了隐式。 (这不一定是不好的,顺便说一句。)

在您的示例中,模型类了解视图模型,因为您在生成它们的模型上具有方法。我建议您逆转这种关系:在视图模型上创建取决于模型对象的方法。这样,您将使模型类保持良好,干净,独立于演示层中所需的所有UI数据。

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

编辑:使视图完全对模型一无所知的替代方法:

在主持人中构建此类的实例,您知道模型和查看模型:

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

通过 List<PartViewModel> 作为DataGridView的数据源。您可以将所选的PartViewModel对象返回到主持人(使用事件或使用方法)。主持人知道它可以将partModel属性投入到部分实例。正如您所说的那样,该视图不需要了解该模型。但是您仍然可以在主持人中使用简单的对象标识,避免使用ID的“复杂”查找。

带有主持人回调:

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

假设PartBindingSource是GridView连接到的bindingsource,您可以处理像这样的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