Генерация событий

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

Пример будет включать форму, генерирующую событие, которое будет прослушиваться другим классом. Когда возникает событие, принимающий объект определяет, должен ли выполняться какой-то процесс и затем отменяет событие, если этот процесс продолжаться не может. Нашей целью в данном случае будет определение того, что число секунд текущего времени больше или меньше 30. Если количество секунд меньше 30, свойство устанавливается в значение строки, представляющей текущее время; если же количество секунд будет больше 30, событие отменяется, а строка времени очищается.

Форма, используемая для генерации, будет включать в себя кнопку с меткой над ней. В этом примере кнопка называется btnRaise, а метка — Ibllnf о, однако вы можете выбрать любые другие имена. После создания формы и добавления к ней этих двух элементов управления можно создавать событие и соответствующий ему делегат Добавим следующий код в раздел объявления класса формы:

public delegate void ActionEventHandler(object sender,

ActionCancelEventArgs ev);

public static event ActionEventHandler Action;

Что же на самом деле происходит в этих двух строках кода? Во-первых, мы объявляем новый тип делегата — ActionEventHandler. Причина того, что нужно создать новый, а не использовать какой-то из предопределенных .NET Framework делегатов состоит в том, что мы будем применять пользовательский класс, унаследованный от EventArgs. Вспомните, что сигнатура метода должна соответствовать делегату. Теперь мы имеем делегат для использования; следующая строка в действительности определяет событие. В этом случае создается событие Action, а синтаксис определения события требует, чтобы был специфицирован делегат, который будет с этим событием ассоциирован. Можно использовать делегат, уже определенный .NET Framework. Существует около 100 классов, унаследованных от EventArgs, поэтому всегда можно выбрать подходящий. Но поскольку в этом примере применяется пользовательскиё класс-наследник EventArgs, то для него должен быть создан соответствующий делегат.

Этот новый класс, ActionCancelEventArgs, является наследником CancelEventArgs, который, в свою очередь, унаследован от EventArgs. CancelEventArgs добавляет свойство Cancel. Cancel имеет булевский тип и информирует объект-отправитель о том, что принимающий объект желает отменить или остановить обработку события. В классе ActionCancelEventArgs добавлено свойство Message.

Это строковое свойство, предназначенное для передачи текстовой информации о состоянии обработки события. Ниже показан код класса ActionCancelEventArgs.

public class ActionCancelEventArgs: System.ComponentModel.CancelEventArgs

{

string _msg = "";

public ActionCancelEventArgs(): base() {}

public ActionCancelEventArgs(bool cancel): base(cancel) {}

public ActionCancelEventArgs(bool cancel, string message): base(cancel)

{

_msg = message;

}

public string Message

{

get {return _msg;}

set {_msg = value;}

}

}

Вы можете убедиться, что все классы-наследники EventArgs заботятся о передаче информации между отправителем и получателем. Чаще всего информация класса EventArgs используется объектом-получателем в обработчике события. Однако иногда обработчик события может добавлять информацию в класс EventArgs, и эта информация становится доступной отправителю. Именно так этот пример использует касс EventArgs. Обратите внимание, что у класса EventArgs имеется несколько конструкторов. Это обеспечивает гибкость и удобство его использования. Итак, событие объявлено, делегат определен, класс-наследник EventArgs создан.Теперь событие нужно как-то возбудить. Единственное, что следует сделать для этого — выполнить вызов события с правильными параметрами, как показано в примере:

ActionCancelEventArgs cancelEvent = new ActionCancelEventArgs();

OnAction(this, cancelEvent);

Довольно-таки просто. Создаем экземпляр класса ActionCancelEventArgs и передаем его событию в качестве одного из параметров. Однако есть одна небольшая проблема. Что если событие пока нигде не будет использовано? Что если для события не определен ни один обработчик? Событие Action может быть равно null. Если вы попытаетесь возбудить такое событие, то получите исключение с null -ссылкой. Если вы захотите создать класс-наследник формы и использовать в качестве базовой форму, имеющую определенное в ней событие Action, то должны будете сделать кое-что еще при возбуждении события Action. Пока вам пришлось бы определять в унаследованной форме другой обработчик событий, чтобы сделать его доступным. Чтобы облегчить этот процесс, а также перехватывать ошибку нулевой ссылки, потребуется создать метод с именем OnEventName, где EventName — имя события. В нашем примере для этого предусмотрен метод OnAction. Ниже представлен его код:

protected void OnAction(object sender, ActionCancelEventArgs ev)

{

if(Action!= null)

Action(sender, ev);

}

He так много, но обеспечивается все необходимое. Объявляя этот метод защищенным (protected), мы обеспечиваем доступ к нему только из классов-наследников. Здесь также присутствует проверка на null перед возбуждением события. Если придется наследовать новый класс, который будет содержать этот метод и событие, то нужно будет переопределить метод OnAction и в нем обращаться к событию. Чтобы сделать это, в перегрузке придется вызвать base. OnAction(). В противном случае событие не будет возбуждено. Это соглашение об именовании используется повсюду в. NET Framework и зафиксировано в документации по. NET SDK.

Обратите внимание на два параметра, которые передаются методу OnEvent(). Они должны показаться вам знакомыми, потому что это те же самые параметры, которые должны быть переданы событию. Если событие должно быть возбуждено из другого объекта, а не того, в котором определен этот метод, то придется создать средство доступа с модификатором internal или public, а не protected. Иногда имеет смысл объявить класс, который состоит только из объявлений событий и ничего больше, чтобы их можно было вызывать из других классов. Нам все еще понадобятся методы OnEventName. Однако в этом случае они могут быть статическими.

После того, как событие возбуждено, что-то должно его обработать. Создадим новый класс в проекте и назовем его BusEntity. Вспомните, что целью проекта является проверка секундной составляющей текущего времени, и если она меньше 30, нужно установить строку времени, а если больше 30 — установить строку в значение:: и отменить событие. Вот как выглядит код:

using System;

using System.IO;

using System.ComponentModel;

namespace SimpleEvent

{

/// <summary>

/// Summary description for BusEntity.

/// </summary>

public class BusEntity

{

string _time = "";

public BusEntity()

{

Form1.Action += new

Form1.ActionEventHandler(EventManager_Action);

}

private void EventManager_Action(object sender,

ActionCancelEventArgs ev)

{

ev.Cancel =!DoActions();

if(ev.Cancel)

ev.Message = "Неправильное время";

}

private bool DoActions()

{

bool retVal = false;

DateTime tm = DateTime.Now;

if(tm.Second < 30)

{

_time = "Текущее время " + DateTime.Now.ToLongTimeString();

retVal = true;

}

else

_time = "";

return retVal;

}

public string TimeString

{

get {return _time;}

}

}

}

В конструкторе объявлен обработчик события Form.Action. Отметим сходство синтаксиса с ранее зарегистрированным событием Click. Поскольку для объявления события применяется тот же шаблон, синтаксис использования также согласован. Здесь стоит уточнить еще кое-что, а именно — как получить ссылку на событие Action без ссылки на Forml в классе BusEntity. Вспомним, что в классе Forml coбытие Action объявлено статическим. Это не требование, но существенно облегчает создание обработчика. Можно объявить событие общедоступным (public), но тогда придется ссылаться на экземпляр Forml.

Когда мы кодировали событие в конструкторе, то вызывали метод, добавленный в список делегатов Form_Action, в соответствии со стандартами именования. В обработчике должно быть принято решение о том, нужно ли отменять событие. Метод DoActions возвращает булевское значение на базе временного критерия, описанного выше. DoActions также устанавливает строке _time правильное значение.

Далее возвращенное DoActions значение устанавливается для свойства ActionCancelEventArgs Cancel. Вспомните, что класс EventArg обычно не делают ничего помимо того, что сохраняет в себе информацию, которой обмениваются отправитель и получатель. Если событие отменено (ev.Cancel = true), то свойство Message также получает строковое значение, описывающее причину отмены события. Теперь, если мы снова посмотрим на код обработчика события btnRaise_Click, видим, как используется свойство Cancel:

private void btnRaise_Click(object sender, EventArgs e)

{

ActionCancelEventArgs cancelEvent =

new ActionCancelEventArgs();

OnAction(this, cancelEvent);

if(cancelEvent.Cancel)

lblInfo.Text = cancelEvent.Message;

else

lblInfo.Text = _busEntity.TimeString;

}

Обратите внимание, что здесь создается объект ActionCancelEventArgs. Затем возбуждается событие Action с передачей ему вновь созданного объекта - ActionCancelEventArgs. Когда вызывается метод OnAction и возбуждается событие, то выполняется код обработчика события Action в объекте BusEntity. Если существуют другие объекты, зарегистрировавшие событие Action, их обработчики также выполняются. Следует иметь в виду, что если есть объекты, обрабатывающие это событие, то все они должны видеть объект ActionCancelEventArgs. Если необходимо узнать, какой именно объект отменил событие, и не сделали ли это сразу несколько объектов, то в составе класса ActionCancelEventArgs понадобится какая-то списочная структура.

После того как обработчики, зарегистрированные через делегат события, выполняется, можно будет опросить объект ActionCancelEventArgs, чтобы узнать, не был ли он отменен. Если это так, то Ibllnfo будет содержать значение свойства Message. Если же событие не было отменено, то Ibllnfo покажет текущее время.

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

Резюме

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

Как разработчику. NET, вам придется интенсивно применять делегаты и события, особенно при создании приложений Windows Forms. События — это средство, с помощью которого разработчик. NET может отслеживать разнообразные события Windows, которые возникают во время выполнения приложений. В противном случае пришлось бы отслеживать оконную процедуру (WndProc) и перехватывать сообщения WM_MOUSEDOWN вместо того, чтобы просто получать событие Click мыши для кнопки.

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

using System;

using System.Drawing;

using System.Collections;

using System.ComponentModel;

using System.Windows.Forms;

using System.Data;

using System.IO;

namespace SimpleEvent

{

/// <summary>

/// Summary description for Form1.

/// </summary>

public class Form1: System.Windows.Forms.Form

{

private System.Windows.Forms.Label lblInfo;

private System.Windows.Forms.Button btnRaise;

/// <summary>

/// Required designer variable.

/// </summary>

private System.ComponentModel.Container components = null;

BusEntity _busEntity = new BusEntity();

public delegate void ActionEventHandler(object sender, ActionCancelEventArgs ev);

public static event ActionEventHandler Action;

public Form1()

{

//

// Required for Windows Form Designer support

//

InitializeComponent();

btnRaise.Click += new EventHandler(btnRaise_Click);

}

/// <summary>

/// Clean up any resources being used.

/// </summary>

protected override void Dispose(bool disposing)

{

if(disposing)

{

if (components!= null)

{

components.Dispose();

}

}

base.Dispose(disposing);

}

#region Windows Form Designer generated code

#endregion

/// <summary>

/// The main entry point for the application.

/// </summary>

[STAThread]

static void Main()

{

Application.Run(new Form1());

}

private void btnRaise_Click(object sender, EventArgs e)

{

ActionCancelEventArgs cancelEvent = new ActionCancelEventArgs();

OnAction(this, cancelEvent);

if(cancelEvent.Cancel)

lblInfo.Text = cancelEvent.Message;

else

lblInfo.Text = _busEntity.TimeString;

}

protected void OnAction(object sender, ActionCancelEventArgs ev)

{

if(Action!= null)

Action(sender, ev);

}

}

public class ActionCancelEventArgs: System.ComponentModel.CancelEventArgs

{

string _msg = "";

public ActionCancelEventArgs(): base() {}

public ActionCancelEventArgs(bool cancel): base(cancel) {}

public ActionCancelEventArgs(bool cancel, string message): base(cancel)

{

_msg = message;

}

public string Message

{

get {return _msg;}

set {_msg = value;}

}

}

}


Понравилась статья? Добавь ее в закладку (CTRL+D) и не забудь поделиться с друзьями:  



double arrow
Сейчас читают про: