这是我对MVP和你的具体问题的谦虚看法。
首先,用户可以交互或仅显示的任何内容都是视图。这种视图的规律、行为和特性由接口描述。该接口可使用WinForms UI、控制台UI、Web UI甚至没有UI(通常在测试Presenter时)来实现 - 只要它遵守其视图接口的规律即可,具体实现并不重要。
其次,视图始终由Presenter控制。这种Presenter的规律、行为和特性也由接口描述。只要它遵守其视图接口的规律,该接口就不会对具体视图实现产生兴趣。
第三,由于Presenter控制其视图,为了最小化依赖关系,视图根本不需要知道有关其Presenter的任何信息。Presenter与视图之间有一个协议,并由视图接口声明。
第三点的含义是:
- Presenter没有任何视图可以调用的方法,但视图具有Presenter可以订阅的事件。
- Presenter知道它的视图。我喜欢通过具体Presenter的构造函数注入来实现这一点。
- 视图不知道哪个Presenter正在控制它;它永远不会提供任何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()
{
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
{
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. 当窗体加载时,它必须获取一个树形视图。我是否正确地认为视图应该调用像
presenter.gettree()
这样的方法,这将委托给模型,模型将获取树形数据,创建并配置它,将其返回给 presenter,然后再传递给视图,最后将其分配给面板?
我将在
IConfigurationPresenter.ShowView()
中调用
IConfigurationView.SetTreeData(...)
,就在调用
IConfigurationView.Show()
之前。
2. 对于窗体上的任何数据控件,如datagridview,是否也是这样?
是的,对于这个我会调用
IConfigurationView.SetTableData(...)
。由视图来格式化给定的数据。Presenter只是遵循视图的契约,即它想要表格数据。
3. 我的应用程序有许多具有相同程序集的模型类。它还支持插件架构,需要在启动时加载插件。视图是否只需调用一个 presenter 方法,然后调用一个加载插件并在视图中显示信息的方法?哪一层将控制插件引用?视图会保存对它们的引用还是 presenter?
如果插件与视图相关,则视图应该知道它们,但不应该由 presenter 知道。如果它们都是关于数据和模型的,则视图不应该涉及它们。
4. 我是否正确地认为视图应该处理有关呈现的每一个细节,从树形节点颜色到datagrid大小等等?
是的。可以将其看作 presenter 提供描述数据的 XML,而视图则采用 CSS 样式表对其进行渲染。具体来说,presenter 可能会调用
IRoadMapView.SetRoadCondition(RoadCondition.Slippery)
,然后视图以红色渲染道路。
5. 如果我单击树节点时,我是否应该将特定节点传递给 presenter,然后 presenter 将计算出它需要的数据,并向模型请求该数据,最后再将其呈现回视图?
是的,当单击树节点时,我会将特定节点传递给 presenter,然后 presenter 将计算出它需要的数据,并向模型请求该数据,最后再将其呈现回视图。
如果可能的话,我会在一次性地传递所有呈现树所需的数据。但如果某些数据太大而无法从一开始传递,或者如果其动态性质需要从模型(通过Presenter)获取“最新快照”,那么我会在视图接口中添加类似于
event LoadNodeDetailsEventHandler LoadNodeDetails
的内容,以便Presenter可以订阅它,在模型中获取节点的详细信息
LoadNodeDetailsEventArgs.Node
(可能通过其某种ID), 以便当事件处理程序委托返回时,视图可以更新其显示的节点详细信息。请注意,如果获取数据可能过慢导致用户体验不佳,则可能需要使用异步模式。