C# MVP被动视图-保持视图数据和模型数据分开
我使用被动视图模式实现了一个MVP三元组,即视图只包含简单的getter和setter。但是,我在分离视图数据和模型数据时遇到问题。尤其是在处理视图状态中的更改时 空间坐标轴用于使用户能够从列表中选择零件。零件列表由模型提供,每个零件由唯一id唯一标识 假设零件看起来像这样:C# MVP被动视图-保持视图数据和模型数据分开,c#,winforms,design-patterns,mvp,passive-view,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; St
class Part
{
int ID; // this code uniquely identifies the part within the model
String partCode;
String description;
double voltage;
}
private void partBindingSource_CurrentChanged(object sender, EventArgs e)
{
_presenter.SelectedPartChanged(partBindingSource.Current as PartViewModel);
}
视图向用户显示列表,并允许用户选择零件
该列表显示在DataGridView中,并通过单击DataGridView中的行来选择零件
ID不会向用户显示,电压也不会显示,因此模型会创建一个仅包含零件代码和说明的数据表。演示者将此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中显示过滤后的数据
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;
}
}
}
我遇到的问题是返回用户选择的零件
视图不知道唯一ID,因为它不显示,并且无法保证其他信息是唯一的-因此无法唯一标识所选零件
本质上,我正在尝试将视图数据(选定的行)转换为模型数据(选定的零件),而没有一个组件使用其他数据
到目前为止,我有以下解决方案:
1) 视图被传递一个包含ID的DataTable,然后过滤显示,以便不向用户显示。然后,返回所选行的ID就很简单了。这里的问题是,我现在用未经测试的逻辑(显示器的过滤)污染了视图
2) 视图返回行索引,模型将该索引与原始数据中的行相匹配。这意味着确保视图中的顺序永远不会更改,这可能会限制视图显示(和操作)数据的方式。这还会用视图数据(行索引)污染模型
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列,我想您在这里误解了整个概念 应该由演示者来处理,而不是模型。模型应该只专注于其唯一的责任,否则,您将视图和模型保持得太近 我的建议是在表中保留一个隐藏列,并将所选行的事件传递给演示者,然后让演示者处理工作
这将是MVP的正确用法。我之前发布了一个简单的解决方案;这是对问题更详细的答复 是否有不想将
列表传递给视图的原因
您可以将网格配置为隐藏id和电压列。您只需从视图中的绑定源获取所选对象。演示者可以在视图中查询此选择,或者视图可以在演示者上调用SelectionChanged(Part selected)
这将意味着您不再严格遵循模式,而是一种模式,因为现在您的视图了解了模型
如果您不喜欢这样,可以引入一个视图模型,您已经在DataTable中隐式地这样做了。(顺便说一句,这不一定不好。)
在您的示例中,模型类知道视图模型,因为您在模型上有生成它们的方法。我建议您颠倒这种关系:在视图模型上创建依赖于模型对象的方法。
通过这种方式,您将保持模型类整洁,并且独立于表示层中所需的所有UI数据
使用视图模型/监视控制器的方式,考虑删除DATABATE概念,支持简单的类。
编辑:使视图完全忽略模型的替代方法:
在presenter中构造此类的实例,在该实例中,您可以同时了解模型和视图模型:
public class PartViewModel
{
object PartModel { get; set; }
string Name { get; set; }
string Description { get; set; }
}
将列表作为数据源传递给DataGridView。
可以将选定的PartViewModel对象返回给演示者(使用事件或方法)。演示者知道它可以将PartModel特性强制转换回零件实例。视图不需要知道关于模型的任何信息,正如您所说的您更喜欢的那样。但是您仍然可以在presenter中使用简单的对象标识,避免使用id进行“复杂”的查找
使用演示者回调:
interface IPartListPresenter
{
// other methods
void SelectedPartChanged(PartViewModel nowSelected);
}
假设partBindingSource是gridview连接到的bindingsource,您可以如下处理partBindingSource的CurrentChanged事件:
class Part
{
int ID; // this code uniquely identifies the part within the model
String partCode;
String description;
double voltage;
}
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
}
希望这能有所帮助。我应该补充一点,我一直在遵循这里介绍的Presenter First方法。我不想将列表传递给视图,因为我希望视图对域对象一无所知。还因为我需要在视图中添加(未测试的)代码来格式化要显示的零件。我想尽量让视野“薄”,我明白你的意思。在这种情况下,我认为您的视图模型方法是正确的。看看我在这个答案中的建议,看看它们是否对你有用。你可能喜欢假设演示者告诉视图