Обработка событий в .NET с помощью C#
В этой статье я расскажу о модели обработки событий в .NET с помощью C#.
🕛 20.04.2010, 22:34
Обсуждение начнется с введения в концепцию делегатов, а затем расширится концепциями событий и их обработкой в .NET. В конце концов, мы применим механизм к событиям графического пользовательского интерфейса (Graphical User Interface - GUI), используя формы Windows. Дискуссия сопровождается законченными примерами.Введение
Обработка событий хорошо известна тем разработчикам, которые программировали графический пользовательский интерфейс. Когда пользователь взаимодействует с ним (например, нажав кнопку), один или более методов, ожидающих этого отклика, запускаются. Событие может быть сгенерировано и без взаимодействия с пользователем. Обработчики событий это методы в объекте, привязанные к событию, возникающему в приложении. Чтобы разобраться в механизме обработки событий необходимо понимать принципы делегатов.
Делегаты в C#
Делегаты в C# позволяют применять методы одного класса к объектам другого класса, который может эти методы вызвать. Вы можете применить метод m класса A, определенного в делегате, к классу B, который будет способен вызвать метод m класса A. Вы можете вызвать как статические методы, так и нестатические. Эта концепция хорошо известна разработчикам C++, которые использовали указатель на функцию в качестве параметра, передаваемого другой функции. Концепция делегатов была введена в Visual J++, а затем перенесена в C#. Делегаты C# являются в .NET Framework реализацией в качестве производного класса от System.Delegate. Использование делегатов можно описать четырьмя шагами.
1. Определение объекта делегата с сигнатурой, точно соответствующей сигнатуре метода, который пытаемся связать. 2. Определение метода с сигнатурой делегата, определенного на первом шаге 3. Создание объекта делегата и связывание их с методами 4. Вызов связанного метода с помощью объекта делегата
Следующий C# код показывает вышеупомянутые шаги в реализации одного делегата и четырех классов.
using System; // Шаг 1. Определение делегата с сигнатурой связываемого метода public delegate void MyDelegate(string input); // Шаг 2. Определение метода с сигнатурой определенного делегата class MyClass1{ public void delegateMethod1(string input){ Console.WriteLine( "This is delegateMethod1 and the input to the method is {0}", input); } public void delegateMethod2(string input){ Console.WriteLine( "This is delegateMethod2 and the input to the method is {0}", input); } } // Шаг 3. Создание объектов делегата и связывание с методами class MyClass2{ public MyDelegate createDelegate(){ MyClass1 c2=new MyClass1(); MyDelegate d1 = new MyDelegate(c2.delegateMethod1); MyDelegate d2 = new MyDelegate(c2.delegateMethod2); MyDelegate d3 = d1 + d2; return d3; } } // Шаг 4. Вызов метода с помощью делегата class MyClass3{ public void callDelegate(MyDelegate d,string input){ d(input); } } class Driver{ static void Main(string[] args){ MyClass2 c2 = new MyClass2(); MyDelegate d = c2.createDelegate(); MyClass3 c3 = new MyClass3(); c3.callDelegate(d,"Calling the delegate"); } }
Обработчики событий в C#
Обработчик события это делегат со специальной сигнатурой.
public delegate void MyEventHandler(object sender, MyEventArgs e);
Первый параметр (sender) в вышеупомянутом определении определяет объект, который издает событие. Второй параметр (e) содержит данные, которые должны быть использованы обработчиком события. Класс MyEventArgs должен быть производным от класса EventArgs. EventArgs является базовым классом для более специализированных классов, таких как MouseEventArgs, ListChangedEventArgs и т.д. Для GUI события вы можете применять объекты этих специализированных классов без создания своего собственного. Однако, для остальных событий вы должны свой класс и держать в нем данные, которые вы хотите передать делегату. Пример создания собственного класса.
public class MyEventArgs EventArgs{ public string m_myEventArgumentdata; }
В случае обработчика событий делегат, на который объект ссылается, помечается ключевым словом event.
public event MyEventHandler MyEvent;
Сейчас мы на примере двух классов рассмотрим, как данный механизм работает. На втором шаге нашей дискуссии делегат требовал определения метода с такой же сигнатурой. В нашем примере класс A определяет обработчики событий (методы с сигнатурой аналогичной делегатам). Далее свяжем их с соответствующими делегатами. Метод класса A примет с помощью делегата объект класса B. Затем при возникновении события в классе B будет вызван обработчик события, т.е. метод класса A.
using System; // Шаг 1 public delegate void MyHandler1(object sender,MyEventArgs e); public delegate void MyHandler2(object sender,MyEventArgs e); // Шаг 2 class A{ public const string m_id="Class A"; public void OnHandler1(object sender,MyEventArgs e){ Console.WriteLine("I am in OnHandler1 and MyEventArgs is {0}", e.m_id); } public void OnHandler2(object sender,MyEventArgs e){ Console.WriteLine("I am in OnHandler2 and MyEventArgs is {0}", e.m_id); } // Шаг 3 public A(B b){ MyHandler1 d1=new MyHandler1(OnHandler1); MyHandler2 d2=new MyHandler2(OnHandler2); b.Event1 +=d1; b.Event2 +=d2; } } // Шаг 4 class B{ public event MyHandler1 Event1; public event MyHandler2 Event2; public void FireEvent1(MyEventArgs e){ if(Event1 != null){ Event1(this,e); } } public void FireEvent2(MyEventArgs e){ if(Event2 != null){ Event2(this,e); } } } public class MyEventArgs EventArgs{ public string m_id; } public class Driver{ public static void Main(){ B b= new B(); A a= new A(b); MyEventArgs e1=new MyEventArgs(); MyEventArgs e2=new MyEventArgs(); e1.m_id ="Event args for event 1"; e2.m_id ="Event args for event 2"; b.FireEvent1(e1); b.FireEvent2(e2); } }
Обработка событий GUI в C#
Для обработки событий Winfows Form (.NET Framework поддерживает GUI приложения) применяется модель, описанная ранее. Рассмотрим на примере простого приложения. Определим класс MyForm, унаследованный от класса System.Windows.Forms.Form. Если вы проанализируете код и комментарии, то вы заметите, что мы не определяем делегат, используя ключевое слово event, т.к. события (нажатие мышью и т.д.) для GUI элементов (форма, кнопка и т.д.) уже доступны вам с помощью делегата System.EvantHandler. Однако все еще необходимо создавать объект делегата (System.EvantHandler) и встраивать его в метод, который должен реагировать отклик на событие.
using System; using System.Drawing; using System.Collections; using System.ComponentModel; using System.Windows.Forms; using System.Data; public class MyForm Form{ private Button m_nameButton; private Button m_clearButton; private Label m_nameLabel; private Container m_components = null; public MyForm(){ initializeComponents(); } private void initializeComponents(){ m_nameLabel=new Label(); m_nameButton = new Button(); m_clearButton = new Button(); SuspendLayout(); m_nameLabel.Location=new Point(16,16); m_nameLabel.Text="Click NAME button, please"; m_nameLabel.Size=new Size(300,23); m_nameButton.Location=new Point(16,120); m_nameButton.Size=new Size(176, 23); m_nameButton.Text="NAME"; m_nameButton.Click += new System.EventHandler(NameButtonClicked); m_clearButton.Location=new Point(16,152); m_clearButton.Size=new Size(176,23); m_clearButton.Text="CLEAR"; m_clearButton.Click += new System.EventHandler(ClearButtonClicked); this.ClientSize = new Size(292, 271); this.Controls.AddRange(new Control[] {m_nameLabel, m_nameButton, m_clearButton}); this.ResumeLayout(false); } private void NameButtonClicked(object sender, EventArgs e){ m_nameLabel.Text= "My name is john, please click CLEAR button to clear it"; } private void ClearButtonClicked(object sender,EventArgs e){ m_nameLabel.Text="Click NAME button, please"; } public static void Main(){ Application.Run(new MyForm()); }
Заключение
Другие популярные объектно-ориентированные языки подобные Java и Smalltalk не содержат в своем арсенале концепции делегатов. Это новшество для C#, наследуемое от C++ и J++. Я надеюсь, что вышеупомянутая дискуссия прояснила концепцию тем программистам, для которых C# является первым объектно-ориентированным языком. Если вы используете Visual Studio IDE для своих C# GUI разработок, код связывания делегатов с методами будет генерироваться автоматически управлением GUI (подобно нажатию кнопки мыши). Анализировать эту работу это лучший вариант узнать, что скрывается на самом деле под колпаком.