Home
C# Language C# Language
C# Language Contents C# Language C# Language C# Language

C# with .NET

Prev Page Next PageNext C# Language
C# Language   C# Language
C# Language Table of Contents
C# Language Back Cover
C# Language Professional C# 2009 with .NET 3.0
C# Language Introduction
C# Language Looking at What’s New in the .NET Framework 2.0
C# Language Introducing the .NET Framework 3.0
C# Language Where C# Fits In
C# Language What You Need to Write and Run C# Code
C# Language What This site Covers
C# Language Conventions
C# Language Source Code
C# Language Errata
C# Language roque-patrick.com
C# Language The C# Language
C# Language .NET Architecture
C# Language The Relationship of C# to .NET
C# Language The Common Language Runtime
C# Language A Closer Look at Intermediate Language
C# Language Assemblies
C# Language .NET Framework Classes
C# Language Namespaces
C# Language Creating .NET Applications Using C#
C# Language The Role of C# in the .NET Enterprise Architecture
C# Language Summary
C# Language C# Basics
C# Language Before We Start
C# Language Your First C# Program
C# Language Variables
C# Language Predefined Data Types
C# Language Flow Control
C# Language Enumerations
C# Language Arrays
C# Language Namespaces
C# Language The Main() Method
C# Language More on Compiling C# Files
C# Language Console I/O
C# Language Using Comments
C# Language The C# Preprocessor Directives
C# Language C# Programming Guidelines
C# Language Summary
C# Language Objects and Types
C# Language Classes and Structs
C# Language Class Members
C# Language Structs
C# Language Partial Classes
C# Language Static Classes
C# Language The Object Class
C# Language Summary
C# Language Inheritance
C# Language Implementation Inheritance
C# Language Modifiers
C# Language Interfaces
C# Language Summary
C# Language Arrays
C# Language Simple Arrays
C# Language Multidimensional Arrays
C# Language Jagged Arrays
C# Language Array Class
C# Language Array and Collection Interfaces
C# Language Enumerations
C# Language Summary
C# Language Operators and Casts
C# Language Operators
C# Language Type Safety
C# Language Comparing Objects for Equality
C# Language Operator Overloading
C# Language User-Defined Casts
C# Language Summary
C# Language Delegates and Events
C# Language Delegate Inference
C# Language Anonymous Methods
C# Language Events
C# Language Summary
C# Language Strings and Regular Expressions
C# Language System.String
C# Language Regular Expressions
C# Language Summary
C# Language Generics
C# Language Overview
C# Language Creating Generic Classes
C# Language Generic Classes’ Features
C# Language Generic Interfaces
C# Language Generic Methods
C# Language Generic Delegates
C# Language Other Generic Framework Types
C# Language Summary
C# Language Collections
C# Language Collection Interfaces and Types
C# Language Lists
C# Language Queue
C# Language Stack
C# Language Linked Lists
C# Language Sorted Lists
C# Language Dictionaries
C# Language Dictionary with Multiple Keys
C# Language Bit Arrays
C# Language Performance
C# Language Summary
C# Language Memory Management and Pointers
C# Language Memory Management under the Hood
C# Language Freeing Unmanaged Resources
C# Language Unsafe Code
C# Language Summary
C# Language Reflection
C# Language Custom Attributes
C# Language Reflection
C# Language Summary
C# Language Errors and Exceptions
C# Language Looking into Errors and Exception Handling
C# Language Summary
C# Language Visual Studio
C# Language Visual Studio 2009
C# Language Refactoring
C# Language Visual Studio 2009 for .NET Framework 3.0
C# Language Summary
C# Language Deployment
C# Language Designing for Deployment
C# Language Deployment Options
C# Language Deployment Requirements
C# Language Deploying the .NET Runtime
C# Language Simple Deployment
C# Language Installer Projects
C# Language ClickOnce
C# Language Summary
C# Language Base Class Libraries
C# Language Assemblies
C# Language What Are Assemblies?
C# Language Assembly Structure
C# Language Cross-Language Support
C# Language Global Assembly Cache
C# Language Creating Shared Assemblies
C# Language Configuration
C# Language Summary
C# Language Tracing and Events
C# Language Tracing
C# Language Event Logging
C# Language Performance Monitoring
C# Language Summary
C# Language Threading and Synchronization
C# Language Overview
C# Language Asynchronous Delegates
C# Language The Thread Class
C# Language Thread Pools
C# Language Threading Issues
C# Language Synchronization
C# Language COM Apartments
C# Language Background Worker
C# Language Summary
C# Language .NET Security
C# Language Code Access Security
C# Language Support for Security in the Framework
C# Language Managing Security Policies
C# Language Role-Based Security
C# Language Summary
C# Language Localization
C# Language Namespace System.Globalization
C# Language Resources
C# Language Localization Example Using Visual Studio
C# Language Localization with ASP.NET
C# Language A Custom Resource Reader
C# Language Creating Custom Cultures
C# Language Summary
C# Language Transactions
C# Language Overview
C# Language Database and Classes
C# Language Traditional Transactions
C# Language System.Transactions
C# Language Isolation Level
C# Language Custom Resource Managers
C# Language Transactions with Windows Vista
C# Language Summary
C# Language Windows Services
C# Language What Is a Windows Service?
C# Language Windows Services Architecture
C# Language System.ServiceProcess Namespace
C# Language Creating a Windows Service
C# Language Monitoring and Controlling the Service
C# Language Troubleshooting
C# Language Power Events
C# Language Summary
C# Language COM Interoperability
C# Language .NET and COM
C# Language Marshaling
C# Language Using a COM Component from a .NET Client
C# Language Using a .NET Component from a COM Client
C# Language Platform Invoke
C# Language Summary
C# Language Data
C# Language Manipulating Files and the Registry
C# Language Managing the File System
C# Language Moving, Copying, and Deleting Files
C# Language Reading and Writing to Files
C# Language Reading Drive Information
C# Language File Security
C# Language Reading and Writing to the Registry
C# Language Reading and Writing to Isolated Storage
C# Language Summary
C# Language Data Access with .NET
C# Language ADO.NET Overview
C# Language Using Database Connections
C# Language Commands
C# Language Fast Data Access: The Data Reader
C# Language Managing Data and Relationships: The DataSet Class
C# Language Populating a DataSet
C# Language Persisting DataSet Changes
C# Language Working with ADO.NET
C# Language Summary
C# Language Manipulating XML
C# Language XML Standards Support in .NET
C# Language Introducing the System.Xml Namespace
C# Language Using MSXML in .NET
C# Language Using System.Xml Classes
C# Language Reading and Writing Streamed XML
C# Language Using the DOM in .NET
C# Language Using XPathNavigators
C# Language XML and ADO.NET
C# Language Serializing Objects in XML
C# Language Summary
C# Language .NET Programming with SQL Server 2009
C# Language .NET Runtime Host
C# Language Microsoft.SqlServer.Server
C# Language User-Defined Types
C# Language Stored Procedures
C# Language User-Defined Functions
C# Language Triggers
C# Language XML Data Type
C# Language Summary
C# Language Presentation
C# Language Windows Forms
C# Language Creating a Windows Form Application
C# Language Control Class
C# Language Standard Controls and Components
C# Language Forms
C# Language Summary
C# Language Viewing .NET Data
C# Language The DataGridView Control
C# Language DataGridView Class Hierarchy
C# Language Data Binding
C# Language Visual Studio .NET and Data Access
C# Language Summary
C# Language Graphics with GDI+
C# Language Understanding Drawing Principles
C# Language Measuring Coordinates and Areas
C# Language A Note about Debugging
C# Language Drawing Scrollable Windows
C# Language World, Page, and Device Coordinates
C# Language Colors
C# Language The Safety Palette
C# Language Pens and Brushes
C# Language Drawing Shapes and Lines
C# Language Displaying Images
C# Language Issues When Manipulating Images
C# Language Drawing Text
C# Language Simple Text Example
C# Language Fonts and Font Families
C# Language Example: Enumerating Font Families
C# Language Editing a Text Document: The CapsEditor Sample
C# Language Printing
C# Language Summary
C# Language Windows Presentation Foundation
C# Language Overview
C# Language Shapes
C# Language Controls
C# Language Layout
C# Language Event Handling
C# Language Commands
C# Language Styles, Templates, and Resources
C# Language Styles
C# Language Animations
C# Language Data Binding
C# Language Windows Forms Integration
C# Language Summary
C# Language ASP.NET Pages
C# Language ASP.NET Introduction
C# Language ASP.NET Web Forms
C# Language ADO.NET and Data Binding
C# Language Application Configuration
C# Language Summary
C# Language ASP.NET Development
C# Language Custom Controls
C# Language Master Pages
C# Language Site Navigation
C# Language Security
C# Language Themes
C# Language Web Parts
C# Language Summary
C# Language ASP.NET AJAX
C# Language What Is Ajax?
C# Language What Is ASP.NET AJAX?
C# Language ASP.NET AJAX-Enabled Web Sites
C# Language Summary
C# Language Communication
C# Language Accessing the Internet
C# Language The WebClient Class
C# Language WebRequest and WebResponse Classes
C# Language Displaying Output as an HTML Page
C# Language Utility Classes
C# Language Lower-Level Protocols
C# Language Summary
C# Language Web Services with ASP.NET
C# Language SOAP
C# Language WSDL
C# Language Web Services
C# Language Extending the Event-siteing Example
C# Language Exchanging Data Using SOAP Headers
C# Language Summary
C# Language .NET Remoting
C# Language What Is .NET Remoting?
C# Language .NET Remoting Overview
C# Language Contexts
C# Language Remote Objects, Clients, and Servers
C# Language .NET Remoting Architecture
C# Language Miscellaneous .NET Remoting Features
C# Language Summary
C# Language Enterprise Services
C# Language Overview
C# Language Creating a Simple COM+ Application
C# Language Deployment
C# Language Component Services Explorer
C# Language Client Application
C# Language Transactions
C# Language Sample Application
C# Language Integrating WCF and Enterprise Services
C# Language Summary
C# Language Message Queuing
C# Language Overview
C# Language Message Queuing Products
C# Language Message Queuing Architecture
C# Language Message Queuing Administrative Tools
C# Language Programming Message Queuing
C# Language Course Order Application
C# Language Receiving Results
C# Language Transactional Queues
C# Language Message Queue Installation
C# Language Summary
C# Language Windows Communication Foundation
C# Language Overview
C# Language Simple Service and Client
C# Language Contracts
C# Language Service Implementation
C# Language Binding
C# Language Hosting
C# Language Clients
C# Language Duplex Communication
C# Language Summary
C# Language Windows Workflow Foundation
C# Language Activities
C# Language Custom Activities
C# Language Workflows
C# Language The Workflow Runtime
C# Language Workflow Services
C# Language Hosting Workflows
C# Language The Workflow Designer
C# Language Summary
C# Language Download Details
C# Language Directory Services
C# Language The Architecture of Active Directory
C# Language Administration Tools for Active Directory
C# Language Programming Active Directory
C# Language Searching for User Objects
C# Language DSML
C# Language Summary
C# Language Part VII: Additional Information
C# Language C#, Visual Basic, and C++/CLI
C# Language Namespaces
C# Language Defining Types
C# Language Methods
C# Language Static Members
C# Language Arrays
C# Language Control Statements
C# Language Loops
C# Language Exception Handling
C# Language Inheritance
C# Language Resource Management
C# Language Delegates
C# Language Events
C# Language Generics
C# Language C++/CLI Mixing Native and Managed Code
C# Language Summary
C# Language Windows Vista
C# Language Vista Bridge
C# Language User Account Control
C# Language Directory Structure
C# Language New Controls and Dialogs
C# Language Search
C# Language Summary
C# Language Language Integrated Query
C# Language Traditional Queries
C# Language LINQ Query
C# Language Query Expressions
C# Language Extension Methods
C# Language Standard Query Operators
C# Language Lambda Expressions
C# Language Deferred Query Execution
C# Language Expression Trees
C# Language Type Inference
C# Language Object and Collection Initializers
C# Language Anonymous Types
C# Language Summary
C# Language Index
C# Language A
C# Language B
C# Language C
C# Language D
C# Language E
C# Language F
C# Language G
C# Language H
C# Language I
C# Language J
C# Language K
C# Language L
C# Language M
C# Language N
C# Language O
C# Language P
C# Language Q
C# Language R
C# Language S
C# Language T
C# Language U
C# Language V
C# Language W
C# Language X
C# Language Y
C# Language Z
C# Language
C# Language
Previous PagePrevious
Next PageNext

Events

Windows-based applications are message-based. This means that the application is communicating with Windows and Windows is communicating with the application by using predefined messages. These messages are structures that contain various pieces of information that the application and Windows will use to determine what to do next. Prior to libraries such as MFC (Microsoft Foundation Classes) or to development environments such as Visual Basic, the developer would have to handle the message that Windows sends to the application. Visual Basic and now .NET wrap some of these incoming messages as something called events. If you need to react to a specific incoming message, you would handle the corresponding event. A common example of this is when the user clicks a button on a form. Windows is sending a WM_MOUSECLICK message to the button’s message handler (sometimes referred to as the Windows Procedure or WndProc). To the .NET developer this is exposed as the Click event of the button.

In developing object-based applications, another form of communication between objects is required. When something of interest happens in one of your objects, chances are that other objects will want to be informed. Again, events come to the rescue. Just as the .NET Framework wraps up Windows messages in events, you can also utilize events as the communications medium between your objects.

Delegates are used as the means of wiring up the event when the message is received by the application. Believe it or not, in the preceding section on delegates, you learned just about everything you need to know to understand how events work. However, one of the great things about how Microsoft has designed C# events is that you don’t actually need to understand anything about the underlying delegates in order to use them. So, this section starts off with a short discussion of events from the point of view of the client software. It focuses on what code you need to write in order to receive notifications of events, without worrying too much about what is happening behind the scenes - just so you can see how easy handling events really is. After that, you write an example that generates events, and as you do so, you should see how the relationship between events and delegates works.

The discussion in this section will be of most use to C++ developers because C++ does not have any concept similar to events. C# events, on the other hand, are quite similar in concept to Visual Basic events, although the syntax and the underlying implementation are different in C#.

Tip 

In this context, the term event is used in two different senses. First, as something interesting that happens, and second, as a precisely defined object in the C# language - the object that handles the notification process. When we mean the latter, we will usually refer to it either as a C# event or, when the meaning is obvious from the context, simply as an event.

The Receiver’s View of Events

The event receiver is any application, object, or component that wants to be notified when something happens. To go along with the receiver, there will of course be the event sender. The sender’s job will be to raise the event. The sender can be either another object or assembly in your application, or in the case of system events such as mouse clicks or keyboard entry, the sender will be the .NET runtime. It is important to note that the sender of the event will not have any knowledge of who or what the receiver is. This is what makes events so useful.

Now, somewhere inside the event receiver will be a method that is responsible for handling the event. This event handler will be executed each time the event that it is registered to is raised. This is where the delegate comes in. Because the sender has no idea who the receiver(s) will be, there cannot be any type of reference set between the two. So the delegate is used as the intermediary. The sender defines the delegate that will be used by the receiver. The receiver registers the event handler with the event. The process of hooking up the event handler is known as wiring up an event. A simple example of wiring up the Click event will help illustrate this process.

First, create a simple Windows Forms application. Drag over a button control from the toolbox and place it on the form. In the properties window rename the button to buttonOne. In the code editor, add the following line of code in the Form1 constructor:

public Form1()
{
   InitializeComponent();
   buttonOne.Click += new EventHandler(Button_Click);
}

Now in Visual Studio you should have noticed that after you typed in the += operator all you had to do was press the Tab key a couple of times and the editor will do the rest of the work for you. In most cases this is fine. However, in this example the default handler name is not being used, so you should just enter the text yourself.

What is happening is that you are telling the runtime that when the Click event of buttonOne is raised, that Button_Click method should be executed. EventHandler is the delegate that the event uses to assign the handler (Button_Click) to the event (Click). Notice that you used the += operator to add this new method to the delegate list. This is just like the multicast example that you looked at earlier in this chapter. This means that you can add more than one handler for any event. Because this is a multicast delegate, all of the rules about adding multiple methods apply; however, there is no guarantee as to the order that the methods are called. Go ahead and drag another button onto the form and rename it to buttonTwo. Now connect the buttonTwo Click event to the same Button_Click method, as shown here:


buttonOne.Click += new EventHandler(Button_Click);
buttonTwo.Click += new EventHandler(Button_Click);

With delegate inference you can also write the code as follows where the compiler generates the same code as in the previous version:


buttonOne.Click += Button_Click;
buttonTwo.Click += Button_Click;

The EventHandler delegate is defined for you in the .NET Framework. It is in the System namespace, and all of the events that are defined in the .NET Framework use it. As discussed earlier, a delegate requires that all of the methods that are added to the delegate list must have the same signature. This obviously holds true for event delegates as well. Here is the Button_Click method defined:


private void Button_Click(object sender, EventArgs e)
{

}

A few things are important about this method. First, it always returns void. Event handlers cannot return a value. Next are the parameters. As long as you use the EventHandler delegate, your parameters will be object and EventArgs. The first parameter is the object that raised the event. In this example it is either buttonOne or buttonTwo, depending on which button is clicked. By sending a reference to the object that raised the event you can assign the same event handler to more than one object. For example, you can define one button click handler for several buttons and then determine which button was clicked by asking the sender parameter.

The second parameter, EventArgs, is an object that contains other potentially useful information about the event. This parameter could actually be any type as long as it is derived from EventArgs. The MouseDown event uses the MouseDownEventArgs. It contains properties for which button was used, the X and Y coordinates of the pointer, and other info related to the event. Notice the naming pattern of ending the type with EventArgs. Later in the chapter, you see how to create and use a custom EventArgs-based object.

The name of the method should also be mentioned. As a convention, event handlers follow a naming convention of object_event. object is the object that is raising the event, and event is the event being raised. There is a convention and for readability’s sake it should be followed.

The last thing to do in this example is to add some code to actually do something in the handler. Now remember that two buttons are using the same handler. So, first you have to determine which button raises the event, and then you can call the action that should be performed. In this example, you can just output some text to a label control on the form. Drag a label control from the toolbox onto the form and name it labelInfo. Then write the following code on the Button_Click method:


if(((Button)sender).Name == "buttonOne")
   labelInfo.Text = "Button One was pressed";
else
   labelInfo.Text = "Button Two was pressed";

Notice that because the sender parameter is sent as object, you will have to cast it to whatever object is raising the event, in this case Button. In this example, you use the Name property to determine what button raised the event; however, you can also use another property. The Tag property is handy to use in this scenario, because it can contain anything that you want to place in it. To see how the multicast capability of the event delegate works, add another method to the Click event of buttonTwo. The constructor of the form should look something like this now:


buttonOne.Click += new EventHandler(Button_Click);
buttonTwo.Click += new EventHandler(Button_Click);
buttonTwo.Click += new EventHandler(Button2_Click);

If you let Visual Studio create the stub for you, you will have the following method at the end of the source file. However, you have to add the call to the MessageBox.Show() function.


private void Button2_Click(object sender, EventArgs e)
{
  MessageBox.Show("This only happens in Button 2 click event");
}

If you go back and make use of anonymous methods, the methods Button_Click and Button2_Click would not be needed. The code for the events would like this:


buttonOne.Click += delegate {labelInfo.Text = "Button One was pressed";};
buttonTwo.Click += delegate {labelInfo.Text = "Button Two was pressed";};
buttonTwo.Click += delegate
      {
         MessageBox.Show("This only happens in Button 2 click event");
      };

When you run this example, clicking buttonOne will change the text in the label. Clicking buttonTwo will not only change the text but also display the MessageBox. Again the important thing to remember is that there is no guarantee that the label text changes before the MessageBox appears, so be careful not to write dependent code in the handlers.

You might have had to learn a lot of concepts to get this far, but the amount of coding you need to do in the receiver is fairly trivial. Also bear in mind that you will find yourself writing event receivers a lot more often than you write event senders. At least in the field of the Windows user interface, Microsoft has already written all the event senders you are likely to need (these are in the .NET base classes, in the Windows.Forms namespace).

Generating Events

Receiving events and responding to them is only one side of the story. For events to be really useful, you need the ability to generate them and raise them in your code. The example in this section looks at creating, raising, receiving, and optionally canceling an event.

The example has a form raise an event that will be listened to by another class. When the event is raised, the receiving object will determine if the process should execute and then cancel the event if the process cannot continue. The goal in this case is to determine whether the number of seconds of the current time is greater than or less than 30. If the number of seconds is less than 30, a property is set with a string that represents the current time; if the number of seconds is greater than 30, the event is canceled and the time string is set to an empty string.

The form used to generate the event has a button and a label on it. In the example code to download the button is named buttonRaise and the label is labelInfo; however, you can use any name you want for your labels. After you have created the form and added the two controls, you will be able to create the event and the corresponding delegate. Add the following code in the class declaration section of the form class:


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

So, what exactly is going on with these two lines of code? First, you are declaring a new delegate type of ActionEventHandler. The reason that you have to create a new one and not use one of the predefined delegates in the .NET Framework is that there will be a custom EventArgs class used. Remember the method signature must match the delegate. So, you now have a delegate to use; the next line actually defines the event. In this case the Action event is defined, and the syntax for defining the event requires that you specify the delegate that will be associated with the event. You can also use a delegate that is defined in the .NET Framework. Nearly 100 classes are derived from the EventArgs class, so you might find one that works for you. Again, because a custom EventArgs class is used in this example, a new delegate type has to be created that matches it.

The new EventArgs-based class, ActionCancelEventArgs, is actually derived from CancelEventArgs, which is derived from EventArgs. CancelEventArgs and adds the Cancel property. Cancel is a Boolean that informs the sender object that the receiver wants to cancel or stop the event processing. In the ActionCancelEventArgs class a Message property has been added. This is a string property that will contain textual information on the processing state of the event. Here is the code for the ActionCancelEventArgs class:


public class ActionCancelEventArgs : System.ComponentModel.CancelEventArgs
{
   string message = String.Empty;

   public ActionCancelEventArgs() : base()  {}

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

   public ActionCancelEventArgs(bool cancel, string message) : base(cancel)
   {
      this.message = message;
   }

   public string Message
   {
      get {return message;}
      set {message = value;}
   }
}

You can see that all an EventArgs-based class does is carry information about an event to and from the sender and receiver. Most times the information used from the EventArgs class will be used by the receiver object in the event handler. However, sometimes the event handler can add information into the EventArgs class and it will be available to the sender. This is how the example uses the EventArgs class. Notice that a couple of constructors are available in the EventArgs class. This extra flexibility adds to the usability of the class by others.

At this point, an event has been declared, the delegate has been defined, and the EventArgs class has been created. The next thing that has to happen is that the event needs to be raised. The only thing that you really need to do is make a call to the event with the proper parameters as shown in this example:


ActionCancelEventArgs e = new ActionCancelEventArgs();
Action(this, e);

Simple enough. Create the new ActionCancelEventArgs class and pass it in as one of the parameters to the event. However, there is one small problem. What if the event hasn’t been used anywhere yet? What if an event handler has not yet been defined for the event? The Action event would actually be null. If you tried to raise the event, you would get a null reference exception. If you wanted to derive a new form class and use the form that has the Action event defined as the base, you would have to do something else whenever the Action event is raised. Currently, you would have to enable another event handler in the derived form in order to get access to it. To make this process a little easier and to catch the null reference error you have to create a method with the name OnEventName where EventName is the name of the event. The example has a method named OnAction(). Here is the complete code for the OnAction() method:


protected void OnAction(object sender, ActionCancelEventArgs e)
{
   if (Action != null)
  {
      Action(sender, e);
   }
}

Not much to it but it does accomplish what is needed. By making the method protected, only derived classes have access to it. You can also see that the event is tested against null before it is raised. If you were to derive a new class that contains this method and event, you would have to override the OnAction method and then you would be hooked into the event. To do this, you would have to call base.OnAction() in the override. Otherwise, the event would not be raised. This naming convention is used throughout the .NET Framework and is documented in the .NET SDK documentation.

Notice the two parameters that are passed into the OnAction method. They should look familiar to you because they are the same parameters that will need to be passed to the event. If the event needed to be raised from another object other than the one that the method is defined in, you would need to make the accessor internal or public and not protected. Sometimes it makes sense to have a class that consists of nothing but event declarations and that these events are called from other classes. You would still want to create the OnEventName methods. However, in that case they might be static methods.

So, now that the event has been raised, something needs to handle it. Create a new class in the project and call it BusEntity. Remember that the goal of this project is to check the seconds property of the current time, and if it is less than 30, set a string value to the time, and if it is greater than 30, set the string to :: and cancel the event. Here is the code:


using System;
using System.IO;
using System.ComponentModel;

namespace Wrox.ProCSharp.Delegates
{
   public class BusEntity
   {
      string time = String.Empty;

      public BusEntity()
      {
         Form1.Action += new Form1.ActionEventHandler(Form1_Action);
      }

      private void Form1_Action(object sender, ActionCancelEventArgs e)
      {
         e.Cancel = !DoActions();
         if(e.Cancel)
            e.Message = "Wasn't the right time.";
      }

      private bool DoActions()
      {
         bool retVal = false;
         DateTime tm = DateTime.Now;

         if(tm.Second < 30)
         {
            time = "The time is " + DateTime.Now.ToLongTimeString();
            retVal = true;
         }
         else
            time = "";

         return retVal;
      }

      public string TimeString
      {
         get {return time;}
      }
   }
}

In the constructor, the handler for the Form1.Action event is declared. Notice that the syntax is very similar to the Click event that you registered earlier. Because you used the same pattern for declaring the event, the usage syntax stays consistent as well. Something else worth mentioning at this point is how you were able to get a reference to the Action event without having a reference to Form1 in the BusEntity class. Remember that in the Form1 class the Action event is declared static. This isn’t a requirement, but it does make it easier to create the handler. You could have declared the event public, but then an instance of Form1 would need to be referenced.

When you coded the event in the constructor, you called the method that was added to the delegate list Form1_Action, in keeping with the naming standards. In the handler a decision on whether or not to cancel the event needs to be made. The DoActions method returns a Boolean value based on the time criteria described earlier. DoAction also sets the time string to the proper value.

Next, the DoActions return value is set to the ActionCancelEventArgs Cancel property. Remember that EventArg classes generally do not do anything other than carry values to and from the event senders and receivers. If the event is canceled (e.Cancel = true), the Message property is also set with a string value that describes why the event was canceled.

Now if you look at the code in the buttonRaise_Click event handler again you will be able to see how the Cancel property is used:


private void buttonRaise_Click(object sender, EventArgs e)
{
   ActionCancelEventArgs cancelEvent = new ActionCancelEventArgs();
   OnAction(this, cancelEvent);
   if(cancelEvent.Cancel) brackets
      labelInfo.Text = cancelEvent.Message;
   else
      labelInfo.Text = busEntity.TimeString;
}

Note that the ActionCancelEventArgs object is created. Next, the event Action is raised, passing in the newly created ActionCancelEventArgs object. When the OnAction method is called and the event is raised, the code in the Action event handler in the BusEntity object is executed. If there were other objects that had registered for the Action event, they too would execute. Something to keep in mind is that if there were other objects handling this event, they would all see the same ActionCancelEventArgs object. If you needed to keep up with which object canceled the event and whether more than one object canceled the event, you would need some type of list-based data structure in the ActionCancelEventArgs class.

After the handlers that have been registered with the event delegate have been executed, you can query the ActionCancelEventArgs object to see if it has been canceled. If it has been canceled, lblInfo will contain the Message property value. If the event has not been canceled, the lblInfo will show the current time.

This should give you a basic idea of how you can utilize events and the EventArgs-based object in the event to pass information around in your applications.


Previous PagePrevious
Next PageNext