Creating and Handling Managed Events

   

Now that you understand the basics behind the use of delegates, you're going to explore the subject a little deeper to see how delegates can be used for event handling. For this lesson, a simple console application will be fine. Click New, Project from the File menu and create a new managed C++ application and name it ManagedEvents. Then click OK to create the project.

For this application, you will create two managed classes: the CSource class, which will be responsible for firing events, and the CSink class, which will catch and handle these events as they are fired. First, you'll work on the CSource class.

A class that is responsible for creating and firing events is known as the event source. To specify that a class is the source for events, you use the event_source attribute. This attribute accepts a single parameter and, as such, must be enclosed with bracket symbols. The parameter tells the compiler what type of class it should expect. Here are the three possible values for this parameter:

  • native. Signifies that the class is a native nonmanaged C++ class

  • COM. Tells the compiler that the class uses the Component Object Model

  • managed. Used for any C++ class that uses managed extensions

For this lesson, you are obviously creating a managed C++ class. Therefore, preceding the name of the class declaration, you must add the [event_source(managed)] attribute, as shown in Listing 18.3.

Listing 18.3 Creating the Event Source and the Event Receiver Classes
 1: #include "stdafx.h"  2:  3: #using <mscorlib.dll>  4: #include <tchar.h>  5:  6: using namespace System;  7:  8: [event_source(managed)]  9: __gc class CSource 10: { 11: public: 12:     CSource(void){} 13:     ~CSource(void){} 14: }; 15: 16: [event_receiver(managed)] 17: __gc class CSink 18: { 19: public: 20:     CSink(void){} 21:     ~CSink(void){} 22: }; 23: 24: // This is the entry point for this application 25: int _tmain(void) 26: { 27:     return 0; 28: } 

Of course, an event source wouldn't be any good if there weren't any objects to handle the events that are fired. The event_receiver attribute is used to specify that a class is going to handle events from an event source. This attribute also accepts a parameter. This parameter is the same as the event_source parameter, which for the application you are creating is the managed parameter. The event_receiver attribute for the CSink class can be seen on line 8 of Listing 18.3.

The application you are creating is simply going to fire an event, and the event handler will output a string to the console signifying that the event has been handled. To mark a function within the event source to be designated as an event, you simply preface the function declaration with the __event keyword. That's all there is to it. Any time you are ready to fire that event, you call the function and preface the function call with the __raise keyword.

Create a function called EventTest that accepts a pointer to a character string. Preface that function declaration with the __event keyword. Next, create a function named FireEvent with no parameters and a void return value. For the implementation of the FireEvent function, fire the event, as explained earlier, by using the __raise keyword. Believe it or not, the event source class is finished. If you've used connection points in the past with ATL, you'll really appreciate the use of attributes to create events. Here's the final class declaration for the CSource class:

  1: [event_source(managed)]  2: __gc class CSource  3: {  4: public:  5:     CSource(void){}  6:     ~CSource(void){}  7:  8:     void FireEvent()  9:     { 10:        raise EventTest("Hello from CSource\n\n"); 11:    } 12: 13:     event void EventTest( char* lpszOut ); 14: }; 

The next step is to implement the event handler and the code to associate and disassociate the event source with the event receiver. The CSink class will implement three functions. The first function is the event handler itself. This can be any function name as long as the signature is the same as the event being fired from the event source. Therefore, create a function named EventHandler that accepts a character string pointer and has a void return type. For the implementation, output a meaningful string to the console. This function can be seen in Listing 18.4.

The next two functions will be used to associate the event with the event handler as well as to disassociate the event with the event handler. The process of associating the event and its handler is known as hooking. To hook these two objects together, you use the __hook keyword. The __hook keyword takes three parameters. The first parameter is the event and the class where that event is contained. This parameter takes the form &Class::EventName. The second parameter is a pointer to an already created instance of the source class. This parameter will be passed in from the _tmain method after the CSource class has been instantiated. Finally, the third parameter for the __hook attribute is the name of the receiver class and the associated event handler function name in the form &Class::EventHandlerName. As an option, you can use a fourth parameter that is a pointer to an instance of the event receiver class. If this is not specified, the default parameter is the instance of EventHandlerthe current receiver class by using the this pointer.

The process of unhooking an event from an event handler is the same as hooking them together. The only difference is the use of the __unhook keyword instead of the __hook keyword. The final class implementation for the CSink class is shown in Listing 18.4.

Listing 18.4 Final Implementation of the Event Receiver Class
 1: [event_receiver(managed)]  2: __gc class CSink  3: {  4: public:  5:     CSink(void){}  6:     ~CSink(void){}  7:  8:    void EventHandler( char* lpszOut )  9:    { 10:         Console::WriteLine( "Sink handled event from source: " ); 11:         Console::WriteLine( lpszOut ); 12:    } 13: 14:    void EnableEventHandling(CSource* pSource) 15:    {__ 16:        hook(&CSource::EventTest, pSource, &CSink::EventHandler); 17:    } 18:    void DisableEventHandling(CSource* pSource) 19:    {__ 20:        unhook(&CSource::EventTest, pSource, &CSink::EventHandler); 21:    } 22: }; 

All that remains is to finish the implementation of the _tmain function. The first step is to create an instance of a CSource object and a CSink object. Next, associate the event source's event with the event receiver's event handler by calling the EnableEventHandling member function implemented by the CSink class. After the association has been made, tell the event source to fire the event by calling the FireEvent method you created earlier. The last step is to disassociate the event and the event handler by calling the event receiver's DisableEventHandling method. Your application is now finished.

You may be asking yourself where delegates are involved here. After all, you haven't used the __delegate keyword anywhere and there is no delegate class involved in any of the code you've written. This just goes to show you the power of attribute programming. As you did with the earlier project, open the project properties and enable the Expand Attributed Source property. Compile your application and open the managedevents.mrg.cpp file. Look for the declaration of your event, which is denoted by the use of the __event attribute. Immediately following this, you see the attribute provider injected code. Here you see the familiar delegate class, which is the name of your event transformed into a class that derives from MulticastDelegate. Within that class you see the four functions mentioned earlier: the class constructor, Invoke, BeginInvoke, and EndInvoke. However, the injected code doesn't stop there, as it did in the last project. This time a pointer to the delegate class is already created for you as a member variable. There are also functions for adding and removing functions from the delegate using the multicast feature of delegates, which is the same method you used to create them earlier using the Combine function. Finally, the __raise attribute is transformed into an Invoke function call for the delegate class.

Within the event receiver, you can see some other familiar constructs. You can see how the event receiver class instantiates a new delegate and then passes it to the event source to add the event handler function to the main delegate, similar to the way you bound the function to the delegate upon a "button click" event earlier.

Once you compile and run your application, your output should appear similar to Figure 18.5.

Figure 18.5. Output of a managed application using events.

graphics/18fig05.jpg


   
Top


Sams Teach Yourself Visual C++. NET in 24 Hours
Sams Teach Yourself Visual C++.NET in 24 Hours
ISBN: 0672323230
EAN: 2147483647
Year: 2002
Pages: 237

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