Вот простой пример, демонстрирующий концепцию пассивных представлений с использованием шаблона проектирования MVP. Поскольку мы используем пассивные представления, представление не знает докладчика. Докладчик просто подпишется на события, опубликованные представлением, и будет действовать соответственно.
Для начала нам нужно определить контракт на наш взгляд. Обычно это достигается с помощью интерфейса, по сути, мы хотим иметь очень слабую связь с нашим представлением. Нам нужна возможность переключаться на разные виды или создавать события для имитации тестов.
Вот контракт, описывающий простое представление, которое будет использоваться для отображения информации о клиенте
public interface ICustomerManagementView
{
void InitializeCustomers(ICustomer[] customers);
void DisplayCustomer(ICustomer customer);
event EventHandler<EventArgs<ICustomer>> SelectedCustomerChanged;
}
Он предоставляет один метод InitializeCustomers , который будет использоваться для инициализации нашего представления с объектами из нашей модели.
У нас также есть событие SelectedCustomerChanged , которое будет использоваться нашим докладчиком для получения уведомления о том, что в представлении произошло действие.
Как только у нас будет контракт, мы можем начать обрабатывать эти взаимодействия в нашем докладчике.
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 Forms мы можем сделать следующее
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; }
}
}