Я бы не сказал, что это ошибка проектирования, у основной формы есть веские причины для того, чтобы слушать, что делает объект. Один из сценариев, с которыми я столкнулся, - это отображение пользователю сообщений о состоянии, чтобы указать, что делают фоновые процессы или что делают несколько элементов управления в многопоточном приложении, которое позволяет вам открывать сразу несколько экранов / «страниц».
В прикладном блоке составного пользовательского интерфейса базовый эквивалент контейнера внедрения зависимостей связывает события, когда его экземпляры объектов в одном и том же рабочем элементе (рабочий элемент является просто контейнером объекта для группы связанных пользовательских элементов управления). Это делается путем сканирования специальных атрибутов, таких как [EventPublication("StatusChanged")]
для событий и [EventSubscription("StatusChanged")]
для открытых методов. Одно из моих приложений использует эту функциональность, так что пользовательский элемент управления, находящийся во внутренней части приложения, может передавать информацию о состоянии (например, «Загрузка данных клиента ... 45%»), не зная, что эти данные окажутся в строка состояния главной формы.
Таким образом, UserControl может сделать что-то вроде этого:
public void DoSomethingInTheBackground()
{
using (StatusNotification sn = new StatusNotification(this.WorkItem))
{
sn.Message("Loading customer data...", 33);
// Block while loading the customer data....
sn.Message("Loading order history...", 66);
// Block while loading the order history...
sn.Message("Done!", 100);
}
}
... где класс StatusNotification
имеет event
с подписью типа
[EventPublication("StatusChanged")]
public event EventHandler<StatusEventArgs> StatusChanged;
... и вышеупомянутые методы Message()
и Dispose()
этого класса вызывают это событие соответствующим образом. Но этот класс явно не связывал это событие ни с чем. Объектный экземпляр автоматически подключит события к любому с атрибутом подписки с тем же именем.
Итак, у MainForm есть обработчик событий, который выглядит примерно так:
[EventSubscription("StatusChanged", ThreadOption=ThreadOption.UserInterface)]
public void OnStatusChanged(object sender, StatusEventArgs e)
{
this.statusLabel.Text = e.Text;
if (e.ProgressPercentage != -1)
{
this.progressBar.Visible = true;
this.progressBar.Value = e.ProgressPercentage;
}
}
... или что-то подобное. Это сложнее, так как он будет вращаться через несколько уведомлений о состоянии в течение заданного количества секунд, поскольку несколько пользовательских элементов управления могут передавать сообщения о состоянии в одно и то же время.
Таким образом, чтобы воссоздать это поведение без фактического переключения на CAB (что, честно говоря, намного сложнее, чем я думаю, на самом деле это нужно), вы могли бы либо иметь объект MessageNotificationService
, который вы передаете в приложении или что вы превращаетесь в статический / одноэлементный объект (я обычно избегаю такого подхода, так как его сложнее тестировать), ИЛИ вы можете сделать так, чтобы ваши подчиненные пользовательские элементы управления создавались фабричным классом, который выполняет для вас событие. Объекты могут регистрироваться на фабрике по атрибутам вашего собственного создания или путем явного вызова методов, которые говорят: «Эй, всякий раз, когда вы создаете объект с событием этой подписи, я хочу знать об этом».
Только будьте осторожны, чтобы любой класс, который вы реализуете, отцепил события, когда объект был удален, потому что в этом сценарии глупо легко получить что-то, что не будет собирать мусор.
Надеюсь, это поможет!