Как я могу захватить события из суб-элементов управления пользователя в приложении WinForms?

Есть ли способ, чтобы основная форма могла перехватывать события, запускающие субконтроль в пользовательском элементе управления?

У меня есть пользовательский элемент управления, встроенный в основную форму моего приложения. Элемент управления содержит различные субконтроллеры, которые управляют данными, которые сами отображаются другими элементами управления в основной форме. Я бы хотел, чтобы основная форма могла быть каким-то образом проинформирована, когда пользователь меняет субконтроллеры, поэтому я мог обновлять данные и соответствующий экран в другом месте.

Сейчас я обманываю. У меня есть делегат, подключенный к фокусу-событию субконтроля. Этот делегат изменяет свойство пользовательского элемента управления, которое я не использую в другом месте (в этой причине CausesValidation). Затем у меня есть делегат, определенный в основной форме, когда изменяется свойство CausesValidation элемента управления пользователя, которое затем направляет приложение для обновления данных и отображения.

Проблема возникает из-за того, что у меня также есть набор делегатов, когда фокус покидает пользовательский контроль, потому что мне нужно проверить поля в пользовательском элементе управления, прежде чем я смогу позволить пользователю делать что-либо еще. Однако, если пользователь просто переключается между субконтролями, я не хочу проверять, потому что они могут не выполняться.

В принципе, я хочу, чтобы данные обновлялись, когда пользователь переключает субконтроллеры или выходит из пользовательского элемента управления, но не проверяет. Когда пользователь покидает элемент управления, я хочу обновить и проверить. Прямо сейчас, оставляя контроль пользователя, вызывает проверку дважды.

Ответ 1

Лучшей практикой было бы вывести события на UserControl, которые пузырят события до родительской формы. Я пошел вперед и собрал для вас пример. Вот описание того, что предоставляет этот пример.

  • UserControl1
    • Создайте UserControl с помощью TextBox1
    • Зарегистрируйте публичное событие на UserControl, называемом ControlChanged
    • Внутри регистра UserControl обработчик события для TextBox1 TextChangedEvent
    • В функции обработчика TextChangeEvent я вызываю событие ControlChanged для создания пузырьков в родительскую форму
  • Form1
    • Отбросить экземпляр UserControl1 в конструкторе
    • Зарегистрируйте обработчик событий на UserControl1 для MouseLeave и для ControlChanged

Вот скриншот, иллюстрирующий, что событие ControlChanged, которое я определил в UserControl, доступно через UX в Visual Studio в родительской форме Windows.

Обработчики событий для пользовательского контроля http://friendfeed.s3.amazonaws.com/0d5a3968cb785625c8afb3974a8d84894c476291

Ответ 2

Лучшая модель для такого рода вещей будет создавать пользовательские события в вашем пользовательском элементе управления и поднять их в соответствующее время.

Ваш сценарий довольно сложный, но не неслыханный. (Я нахожусь в очень похожем режиме в одном из моих текущих проектов.) Способ, которым я к нему подхожу, заключается в том, что пользовательский контроль отвечает за собственную проверку. Я не использую CausesValidation; вместо этого в соответствующей контрольной точке пользователя я выполняю проверку с помощью переопределения ValidateChildren(). (Обычно это происходит, когда пользователь нажимает "Сохранить" или "Далее" в пользовательском элементе управления для меня.)

Не знакомы с пользовательским интерфейсом пользовательского контроля, который не может быть на 100% правильным для вас. Однако, если вы поднимаете пользовательские события (возможно, с помощью специального EventArgs, который указывает, выполнять ли валидацию или нет), вы должны иметь возможность получить, где вы хотите быть.

Ответ 3

Вам нужно будет связать события, которые вам интересны при захвате внутри пользовательского элемента управления, и опубликовать их через некоторые настраиваемые свойства событий в самом пользовательском элементе управления. Простым примером будет обертка события нажатия кнопки:

// CustomControl.cs
// Assumes a Button 'myButton' has been added through the designer

// we need a delegate definition to type our event
public delegate void ButtonClickHandler(object sender, EventArgs e);

// declare the public event that other classes can subscribe to
public event ButtonClickHandler ButtonClickEvent;

// wire up the internal button click event to trigger our custom event
this.myButton.Click += new System.EventHandler(this.myButton_Click);
public void myButton_Click(object sender, EventArgs e)
{
  if (ButtonClickEvent != null)
  {
    ButtonClickEvent(sender, e);
  }
}

Затем в форме, которая использует этот элемент управления, вы подключаете событие так же, как и любое другое:

// CustomForm.cs
// Assumes a CustomControl 'myCustomControl' has been added through the desinger
this.myCustomControl.ButtonClickEvent += new System.EventHandler(this.myCustomControl_ButtonClickEvent);
myCustomControl_ButtonClickEvent(object sender, EventArgs e)
{
  // do something with the newly bubbled event
}

Ответ 4

В случае, если кому-то все еще интересно, как имитировать пузырьки событий в WinForm, метод Application.AddMessageFilter - это хорошее место для просмотра.

С помощью этого метода вы можете установить свой собственный фильтр, который отслеживает все сообщения в текущей очереди сообщений потока.

Вы должны знать, что сообщения, отправленные (не отправленные), не могут обрабатываться этим фильтром. В целом, наиболее интересные события (например, события кликов) публикуются и не отправляются и, следовательно, могут контролироваться этим фильтром

Ответ 5

Я хотел бы перезвонить, поскольку, как описано, на самом деле это звучит так, будто вы преследуете красную селедку. Хотя кажется, что у вас есть ситуация, когда отсутствие пузырьков событий в WinForms вызывает у вас проблемы, реальность такова, что плохая архитектура вынуждает вас взывать пузырьки событий, когда вы этого не сделаете.

Если вы можете реорганизовать/реорганизовать свой дизайн таким образом, чтобы элементы управления работали с общей моделью данных (MVC/MVP - очевидные варианты), вы можете просто применить обычные шаблоны WinForms, такие как события PropertyChanged на модели, чтобы сообщить основную форму и любые другие элементы управления, которые используют эти данные для обновления.

Короче говоря, другие ответы разумны, поскольку они отвечают на заданный вопрос. Но с точки зрения качества кода, я думаю, лучший ответ - отделить ваши данные от пользовательского интерфейса.