Я сделал именно это. Решение основано на Autofac, но может быть реализовано поверх любого контейнера.
Сначала определите интерфейс, представляющий полномочия для представления представлений в запросе к системе MVP:
public interface IMvpRequest
{
void Present(object view);
}
Затем создайте базовую страницу со свойством этого типа:
public abstract class PageView : Page
{
public IMvpRequest MvpRequest { get; set; }
}
На этом этапе настройте внедрение зависимостей для страниц. Большинство контейнеров имеют интеграцию ASP.NET, обычно в форме модулей HTTP. Поскольку мы не создаем экземпляр страницы, мы не можем использовать инъекцию конструктора и должны использовать только инъекцию свойства здесь.
После этого создайте аргументы события, представляющие представление, готовое к представлению:
public class PresentableEventArgs : EventArgs
{}
Теперь перехватите события в PageView
и передайте их запросу (также представьте страницу):
protected override bool OnBubbleEvent(object source, EventArgs args)
{
var cancel = false;
if(args is PresentableEventArgs)
{
cancel = true;
Present(source);
}
else
{
cancel = base.OnBubbleEvent(source, args);
}
return cancel;
}
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
Present(this);
}
private void Present(object view)
{
if(MvpRequest != null && view != null)
{
MvpRequest.Present(view);
}
}
Наконец, создайте базовые классы для каждого типа элемента управления, который вы хотите использовать в качестве представления (главные страницы, составные элементы управления и т. Д.):
public abstract class UserControlView : UserControl
{
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
EnsureChildControls();
RaiseBubbleEvent(this, new PresentableEventArgs());
}
}
Это соединяет дерево управления с системой MVP через IMvpRequest
, которое вы теперь должны будете реализовать и зарегистрировать в контейнере уровня приложения. Интеграция ASP.NET должна позаботиться о внедрении реализации на страницу. Это полностью отделяет страницу от создания докладчика, полагаясь на IMvpRequest
для сопоставления.
Реализация IMvpRequest
будет зависеть от контейнера. Представители будут зарегистрированы в контейнере, как и другие типы, то есть их конструкторы будут автоматически разрешены.
У вас будет какая-то карта от типов просмотра к типам презентаторов:
public interface IPresenterMap
{
Type GetPresenterType(Type viewType);
}
Это типы, которые вы разрешите из контейнера.
(Единственное, что здесь понятно, это то, что представление уже существует, то есть контейнер не создает экземпляр или вообще не знает о нем. Вам нужно будет передать его как параметр разрешения, еще одну концепцию, поддерживаемую большинством контейнеров.)
Приличное отображение по умолчанию может выглядеть так:
[Presenter(typeof(LogOnPresenter))]
public class LogOnPage : PageView, ILogOnView
{
// ...
}