这是一个使用 MVP 设计模式演示被动视图概念的简单示例。因为我们使用的是被动视图,所以视图不知道演示者。演示者将简单地订阅视图发布的事件并采取相应的行动。
首先,我们需要为我们的视图定义一个契约。这通常是使用接口来实现的,本质上,我们希望与我们的视图有一个非常松散的耦合。我们希望能够切换到不同的视图或事件创建模拟视图以进行单元测试。
这是一个合同,描述了一个简单的视图,将用于显示客户信息
public interface ICustomerManagementView
{
void InitializeCustomers(ICustomer[] customers);
void DisplayCustomer(ICustomer customer);
event EventHandler<EventArgs<ICustomer>> SelectedCustomerChanged;
}
它公开了一个方法InitializeCustomers,该方法将用于使用模型中的对象初始化我们的视图。
我们还有一个事件SelectedCustomerChanged,我们的演示者将使用它来接收视图中发生操作的通知。
一旦我们有了合约,我们就可以开始在 Presenter 中处理这些交互。
public class CustomerManagementPresenter
{
private ICustomer _selectedCustomer;
private readonly ICustomerManagementView _managementView;
private readonly ICustomerRepository _customerRepository;
public CustomerManagementPresenter(ICustomerManagementView managementView, ICustomerRepository customerRepository)
{
_managementView = managementView;
_managementView.SelectedCustomerChanged += this.SelectedCustomerChanged;
_customerRepository = customerRepository;
_managementView.InitializeCustomers(_customerRepository.FetchCustomers());
}
private void SelectedCustomerChanged(object sender, EventArgs<ICustomer> args)
{
// Perform some logic here to update the view
if(_selectedCustomer != args.Value)
{
_selectedCustomer = args.Value;
_managementView.DisplayCustomer(_selectedCustomer);
}
}
}
在演示器中,我们可以使用另一种称为依赖注入的设计模式来提供对我们的视图和我们可能需要的任何模型类的访问。在此示例中,我有一个负责获取客户详细信息的 CustomerRepository。
在构造函数中我们有两行重要的代码,首先我们在视图中订阅了 SelectedCustomerChanged 事件,在这里我们可以执行相关的操作。其次,我们使用存储库中的数据调用了 InitilaizeCustomers。
在这一点上,我们实际上还没有为我们的视图定义一个具体的实现,我们需要做的就是创建一个实现ICustomerManagementView的对象。例如,在 Windows 窗体应用程序中,我们可以执行以下操作
public partial class CustomerManagementView : Form, ICustomerManagementView
{
public CustomerManagementView()
{
this.InitializeComponents();
}
public void InitializeCustomers(ICustomer[] customers)
{
// Populate the tree view with customer details
}
public void DisplayCustomer(ICustomer customer)
{
// Display the customer...
}
// Event handler that responds to node selection
private void CustomerTreeViewAfterSelect(object sender, TreeViewEventArgs e)
{
var customer = e.Node.Tag as ICustomer;
if(customer != null)
{
this.OnSelectedCustomerChanged(new EventArgs<ICustomer>(customer));
}
}
// Protected method so that we can raise our event
protected virtual void OnSelectedCustomerChanged(EventArgs<ICustomer> args)
{
var eventHandler = this.SelectedCustomerChanged;
if(eventHandler != null)
{
eventHandler.Invoke(this, args);
}
}
// Our view will raise an event each time the selected customer changes
public event EventHandler<EventArgs<ICustomer>> SelectedCustomerChanged;
}
如果我们想测试我们的表示逻辑,我们可以模拟我们的视图并执行一些断言。
编辑:包括自定义事件参数
public class EventArgs<T> : EventArgs
{
private readonly T _value;
public EventArgs(T value)
{
_value = value;
}
public T Value
{
get { return _value; }
}
}