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 Studio. NET if it isn’t already open, and create a new Visual C++ Console Application (.NET) 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*);

    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 __gc 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); } }; 

    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 sidebar, “How Does the __event Keyword Work?”

    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.

start sidebar
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:

  • add_OnFirstEvent, a public method that calls Delegate::Combine to add a receiver to this event’s invocation list. Rather than calling add_OnFirstEvent directly, you use the += operator on the event object, which calls the function for you.

  • remove_OnFirstEvent, a public method that calls Delegate::Remove to remove a receiver from this event’s invocation list. As with the add_ function, you don’t call this method directly but instead use the -= operator on the event object.

  • raise_OnFirstEvent, a protected method that calls Delegate::Invoke to call all the methods on this event’s invocation list.

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

end sidebar

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.

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

    // Event receiver class __gc class EvtRcv { EvtSrc* theSource; public: }; 

    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.

  2. 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 == 0) throw new ArgumentNullException("Must have event source"); // Save the source theSource = pSrc; }

  3. 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); }

    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.

  4. 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 == 0) throw new ArgumentNullException("Must have event source"); // Save the source theSource = pSrc; // Add our handlers theSource->OnFirstEvent += new FirstEventHandler(this, &EvtRcv::FirstEvent); theSource->OnSecondEvent += new SecondEventHandler(this, &EvtRcv::SecondEvent); }

    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.

  5. A matching -= operator lets you unsubscribe from events. 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 -= new FirstEventHandler(this, &EvtRcv1::FirstEvent); }

    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.

  1. Edit the _tmain function to create event source and receiver objects.

    int _tmain() { Console::WriteLine(S"Event Example"); // Create a source EvtSrc* pSrc = new EvtSrc(); // Create a receiver, and bind it to the source EvtRcv* pRcv = new EvtRcv(pSrc); return 0; } 

    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.

    int _tmain() { Console::WriteLine(S"Event Example"); // Create a source EvtSrc* pSrc = new EvtSrc(); // Create a receiver, and bind it to the source EvtRcv* pRcv = new EvtRcv(pSrc); // Fire events Console::WriteLine(S"Fire both events:"); pSrc->RaiseOne(S"Hello, mum!"); pSrc->RaiseTwo(S"One big step"); return 0; }

    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.

    click to expand

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

  2. Insert some code 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(S"Fire both events:"); pSrc->RaiseOne(S"Hello, mum!"); pSrc->RaiseTwo(S"One big step");

    This time you should see only the second message printed because the receiver is no longer handling the first event.

Microsoft Visual C++  .NET(c) Step by Step
Microsoft Visual C++ .NET(c) Step by Step
ISBN: 735615675
Year: 2003
Pages: 208

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net