< C++ .NET Delegates & Events 2 | Main | C++ .NET Delegates & Events 4 >


 

 

Delegates and Events 3

generate, fire and handle the events...

 

 

 

 

 

  1. What Are Events?

  2. Implementing an Event Source Class

  3. How Does the event Keyword Work?

  4. Implementing an Event Receiver

  5. Hooking It All Together

  6. Quick Reference

 

What Are Events?

 

Most, if not all, GUI platforms support the idea of events, and events are very heavily used in GUI programming. As an example, consider a button. Buttons don’t exist on their own but are used as part of a user interface and are contained by some other item. This item is usually a form, but it could also be some other control, such as a toolbar. The whole point of having a button on a form is so that the user can click it to tell the program something. For example, “the user clicked the OK button, so dismiss the dialog box” or “the user clicked the Print button on the toolbar, so print the document.” Events provide a formalized, standard mechanism that lets event sources (such as a button) hook up with event receivers (such as a form). Events in the .NET Framework implement a publish-and-subscribe mechanism, where event sources make public the events that they will raise, they publish them and event receivers tell the source which events they’re interested in, they subscribe to events. Event receivers can also unsubscribe when they no longer want to receive a particular event.

Events in the .NET Framework are based on multicast delegates, and it isn’t too hard to see how this will work. An event source declares a delegate for each event it wants to generate, such as Click, DoubleClick, and so on. An event receiver then defines suitable methods and passes them to the event source, which uses Combine to add them to its multicast delegates. When the time comes to fire the event, the event source calls Invoke on the delegate, thus calling the requisite functions in the receivers. The actual event mechanism simplifies the syntax so that you don’t have to deal with delegates directly, and it’s designed to fit in with the event mechanism that already exists in Visual Basic. The following exercise takes you through creating an event source class and event receiver classes that register themselves with the source and use the events when they’re fired.

 

Implementing an Event Source Class

 

1.        Open Visual C++/Studio. NET if it isn’t already opened and create a new CLR Console Application project named Event.

 

 

 

Creating a new CLR Console Application project named Event

 

2.        Event sources and receivers use delegates, so define a delegate for each of the events raised by the source. In this example, two events will be used, so open the Event.cpp source file and define the following two delegates immediately after the using namespace System; line:

 

// Delegates

delegate void FirstEventHandler(String^);

delegate void SecondEventHandler(String^);

 

Defining a delegate for each of the events raised by the source

 

The delegates define the signatures of the methods that event receivers have to implement to handle the events, so they’re often given names that end with Handler. Each of these events will simply pass a string as the event data, but you can make the data passed as complex as you want.

 

3.        Add the implementation of the event source class to the source file like this:

// Event source class

ref class EvtSrc

{

   public:

         // Declare the events

         event FirstEventHandler^ OnFirstEvent;

         event SecondEventHandler^ OnSecondEvent;

 

         // Event raising functions

         void RaiseOne(String^ pMsg)

         {

               OnFirstEvent(pMsg);

         }

 

         void RaiseTwo(String^ pMsg)

         {

               OnSecondEvent(pMsg);

         }

};

 

Adding the implementation of the event source class to the source file

 

The first thing to note is the use of the event keyword to declare two events. You need one event declaration for each event that you want to raise, and its type is that of the delegate associated with the event. So, in the case of the first event object, the type is FirstEventHandler^ to match the FirstEventHandler delegate. Using the event keyword causes the compiler to generate a lot of delegate handling code for you; if you’re interested in exactly what’s going on, see the following section. You can then use the event objects in the EvtSrc class to raise the events, simply by using them as if they were function calls and passing the appropriate argument.

 

How Does the event Keyword Work?

 

The event keyword isn’t only used in managed code, but can also be used to describe events in native C++ classes and COM events. When you declare an event member for a class in managed code, the compiler generates code to implement the underlying delegate mechanism. For the OnFirstEvent event object in the exercise, you get the following methods generated:

 

 

The raise method is protected so that it can be called only through the proper channels and not directly by client code.

 

Implementing an Event Receiver

 

You now have a class that can be used to fire events, so the next thing you need is a class that will listen for events and act on them when they’ve been generated.

 

4.        Add a new class to the project named EvtRcv.

// Event receiver class

ref class EvtRcv

{

   EvtSrc^ theSource;

   public:

};

 

Adding a new class to the project named EvtRcv

 

The receiver has to know the event sources it’s working with to be able to subscribe and unsubscribe, so add an EvtSrc^ member to the class to represent the one source you’ll be working with.

 

5.        Add a constructor to the class that takes a pointer to an EvtSrc object and checks that it isn’t null. If the pointer is valid, save it away in the EvtSrc^ member.

EvtRcv(EvtSrc^ pSrc)

{

   if (pSrc == nullptr)

         throw gcnew ArgumentNullException("Must have event source");

   // Save the source

   theSource = pSrc;

}

 

Adding a constructor to the class that takes a pointer to an EvtSrc object and checks that it isn’t null

 

6.        Define the member handler functions in EvtRcv that EvtSrc is going to call. As you know from our discussion of delegates, the signatures of these methods will have to match the signatures of the delegates used to define the events, as shown here:

// Handler functions

void FirstEvent(String^ pMsg)

{

   Console::WriteLine("EvtRcv: event one, message was {0}", pMsg);

}

 

void SecondEvent(String^ pMsg)

{

   Console::WriteLine("EvtRcv: event two, message was {0}", pMsg);

}

 

Adding codes to define the member handler functions in EvtRcv that EvtSrc is going to call

 

FirstEvent is the handler for the FirstEventHandler delegate, and SecondEvent is the handler for the SecondEventHandler delegate. Each of them simply prints out the string that they’ve been passed.

 

7.        Once you have the handlers defined, you can subscribe to the event source. Edit the constructor for the EvtRcv class so that it looks like the following code:

EvtRcv(EvtSrc^ pSrc)

{

   if (pSrc == nullptr)

      throw gcnew ArgumentNullException("Must have event source");

   // Save the source

   theSource = pSrc;

 

   // Add our handlers

   theSource->OnFirstEvent += gcnew FirstEventHandler(this, &EvtRcv::FirstEvent);

   theSource->OnSecondEvent += gcnew SecondEventHandler(this, &EvtRcv::SecondEvent);

}

Editing the constructor for the EvtRcv class  

 

You subscribe to an event using the += operator. In the code, you’re creating two new delegate objects, which will call back to the FirstEvent and SecondEvent handlers on the current object. This is exactly the same syntax you’d use if you were manually creating a delegate. The difference is in the += operator, which combines the newly created delegate with the event source’s multicast delegate. As you read in the preceding sidebar, += calls the compiler-generated add_OnFirstEvent method, which in turn calls Delegate::Combine. Although you’ve subscribed to all the events automatically in the constructor, you could also use member functions to subscribe to individual events as required. A matching -= operator lets you unsubscribe from events.

 

8.        Add the following member function to EvtRcv, which will unsubscribe from the first event:

// Remove a handler

void RemoveHandler()

{

   // Remove the handler for the first event

   theSource->OnFirstEvent -= gcnew FirstEventHandler(this, &EvtRcv::FirstEvent);

}

 

Adding the a member function to EvtRcv, which will unsubscribe from the first event

 

The syntax for using the -= operator to unsubscribe is exactly the same as that for the += operator to subscribe.

 

Hooking It All Together

 

Now that you’ve written the event source and event receiver classes, you can write some code to test them out.

 

9.        Edit the main function to create event source and receiver objects.

int main(array<System::String ^> ^args)

{

      Console::WriteLine(L"Event Example");

 

      // Create a source

      EvtSrc^ pSrc = gcnew EvtSrc();

      // Create a receiver, and bind it to the source

      EvtRcv^ pRcv = gcnew EvtRcv(pSrc);

 

    return 0;

}

 

Adding codes to create event source and receiver objects

 

Try building and run your program.

 

Event program output example

 

10.     The EvtSrc constructor takes no arguments, while the EvtRcv constructor has to be passed a valid EvtSrc pointer. At this point, the receiver is set up, listening for events to be fired from the source. Add the following codes.

// Fire events

Console::WriteLine(L"Fire both events:");

pSrc->RaiseOne(L"Hello, mum!");

pSrc->RaiseTwo(L"One big step");

 

Adding codes to fire events

 

11.     Calls to the source’s RaiseOne and RaiseTwo functions tell it to fire both events. When you run this code, you should see output similar to the following figure.

 

C++ .NET event program output example

 

The receiver has had both handlers called, so it has printed both the messages associated with the events.

 

12.     Insert some code shown below to call the RemoveHandler function of the receiver, and try firing both events again:

// Remove the handler for event one

pRcv->RemoveHandler();

 

// Fire events again

Console::WriteLine(L"Fire both events again:");

pSrc->RaiseOne(L"Hello, mum!");

pSrc->RaiseTwo(L"One big step");

 

Adding codes to call the RemoveHandler function of the receiver

 

13.     Rebuild and re-run your program. This time you should see only the second message printed because the receiver is no longer handling the first event as shown below.

 

C++ .NET event program output samples

 

Quick Reference

 

To

Do this

Define a delegate.

Use the delegate keyword with a function prototype. For example:

 

delegate void DelegateOne(double d);

 

Create a delegate bound to a static class member.

Use gcnew to create a Delegate object, and the address of the static function as a parameter. For example:

 

DelegateOne^ pDel = gcnew DelegateOne(&MyClass::MyFunc);

 

Create a delegate bound to a non-static class member.

Use gcnew to create a Delegate object, passing a pointer to the instance for the parameter. For example:

 

DelegateOne^ pDel = gcnew DelegateOne(pMyObject);

 

Execute the function bound to a delegate.

Use the delegate’s Invoke function, passing any parameters required. For example:

 

pDel->Invoke(22.7);

 

Create an event.

First define a delegate to define the handler routine for this event, as follows:

 

delegate void ClickHandler(int, int);

 

Then, in the event source class, use the event keyword to define an event object, like this:

 

event ClickHandler^ OnClick;

 

Raise an event.

Use the event object as if it were a function, passing any parameters. For example:

 

OnClick(xVal, yVal);

 

Subscribe to an event.

Use the += operator. For example:

 

mySrc->OnClick += gcnew ClickHandler(this, &myHandler);

 

Unsubscribe from an event.

Use the -= operator. For example:

 

mySrc->OnClick -= gcnew ClickHandler(this, &myHandler);

 

 

Table 1

 

 

 

 

Part 1 | Part 2 | Part 3 | Part 4 | Part 5 | Part 6 | Part 7

 


 

< C++ .NET Delegates & Events 2 | Main | C++ .NET Delegates & Events 4 >