MVP被动视图 - 保持视图数据和模型数据分开
-
10-10-2019 - |
题
我使用被动视图模式实现了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的正确用法。