这是我对 MVP 和您的具体问题的谦虚看法。
首先,用户可以与之交互或仅显示的任何内容都是视图。这种视图的规律、行为和特征由界面描述。该界面可以使用 WinForms UI、控制台 UI、Web UI 甚至根本不使用 UI(通常在测试演示者时)来实现——具体实现并不重要,只要它遵守其视图界面的法则.
第二,视图总是由presenter控制。 界面也描述了这种演示者的规律、行为和特征。该接口只要遵守其视图接口的规律,就不会对具体的视图实现感兴趣。
第三,由于演示者控制它的视图,为了最小化依赖关系,让视图完全了解它的演示者并没有任何好处。演示者和视图之间有一个约定的合同,并且由视图界面声明。
第三个的含义是:
- 演示者没有任何视图可以调用的方法,但视图有演示者可以订阅的事件。
- 演示者知道它的观点。我更喜欢通过在具体演示者上注入构造函数来实现这一点。
- 视图不知道是什么演示者在控制它;它永远不会被提供任何演示者。
对于您的问题,上面的代码可能看起来像这样简化:
interface IConfigurationView
{
event EventHandler SelectConfigurationFile;
void SetConfigurationFile(string fullPath);
void Show();
}
class ConfigurationView : IConfigurationView
{
Form form;
Button selectConfigurationFileButton;
Label fullPathLabel;
public event EventHandler SelectConfigurationFile;
public ConfigurationView()
{
// UI initialization.
this.selectConfigurationFileButton.Click += delegate
{
var Handler = this.SelectConfigurationFile;
if (Handler != null)
{
Handler(this, EventArgs.Empty);
}
};
}
public void SetConfigurationFile(string fullPath)
{
this.fullPathLabel.Text = fullPath;
}
public void Show()
{
this.form.ShowDialog();
}
}
interface IConfigurationPresenter
{
void ShowView();
}
class ConfigurationPresenter : IConfigurationPresenter
{
Configuration configuration = new Configuration();
IConfigurationView view;
public ConfigurationPresenter(IConfigurationView view)
{
this.view = view;
this.view.SelectConfigurationFile += delegate
{
// The ISelectFilePresenter and ISelectFileView behaviors
// are implicit here, but in a WinForms case, a call to
// OpenFileDialog wouldn't be too far fetched...
var selectFilePresenter = Gimme.The<ISelectFilePresenter>();
selectFilePresenter.ShowView();
this.configuration.FullPath = selectFilePresenter.FullPath;
this.view.SetConfigurationFile(this.configuration.FullPath);
};
}
public void ShowView()
{
this.view.SetConfigurationFile(this.configuration.FullPath);
this.view.Show();
}
}
除了上述之外,我通常还有一个基本的IView 接口,我在其中存储Show() 和我的视图通常受益的任何所有者视图或视图标题。
您的问题:
1. 当winform加载时,它必须获得一个treeview。我认为视图应该因此调用诸如presenter.gettree()之类的方法是否正确,这又将委托给模型,模型将获取树视图的数据,创建并配置它,将其返回给演示者,然后将传递给视图,然后将其简单地分配给一个面板?
我会在拨打IConfigurationView.Show()之前从IConfigurationPresenter.ShowView()拨打IConfigurationView.SetTreeData(...)
2. 这对于 Winform 上的任何数据控件是否都一样,因为我也有一个 datagridview?
是的,我会为此打电话给IConfigurationView.SetTableData(...)。由视图来格式化给它的数据。演示者只是遵守视图的约定,即它需要表格数据。
3. 我的应用程序有许多具有相同程序集的模型类。它还支持带有需要在启动时加载的插件的插件架构。视图是否会简单地调用一个演示者方法,该方法又会调用一个加载插件并在视图中显示信息的方法?然后哪个层将控制插件引用。视图会包含对他们或演示者的引用吗?
如果插件与视图相关,那么视图应该知道它们,而不是演示者。如果它们都是关于数据和模型的,那么视图不应该与它们有任何关系。
4. 我认为视图应该处理有关表示的每一件事,从树视图节点颜色到数据网格大小等,我是否正确?
是的。将其视为提供描述数据的 XML 和获取数据并对其应用 CSS 样式表的视图的演示者。具体来说,presenter 可能会调用IRoadMapView.SetRoadCondition(RoadCondition.Slippery),然后视图会将道路渲染为红色。
点击节点的数据呢?
5. 如果当我点击树节点时,我是否应该将特定节点传递给演示者,然后演示者会从中计算出它需要什么数据,然后询问为该数据建立模型,然后再将其呈现回视图?
如果可能,我会一次性传递在视图中呈现树所需的所有数据。但是,如果某些数据太大而无法从一开始就传递,或者它本质上是动态的并且需要模型中的“最新快照”(通过演示者),那么我会在视图界面中添加类似 event LoadNodeDetailsEventHandler LoadNodeDetails 的内容,以便演示者可以订阅它,从模型中获取LoadNodeDetailsEventArgs.Node 中节点的详细信息(可能通过其某种ID),以便视图可以在事件处理程序委托返回时更新其显示的节点详细信息。请注意,如果获取数据可能太慢而无法获得良好的用户体验,则可能需要这种异步模式。