Creating a Custom Event

WFC controls support a standard set of events that they inherit from the Control class, such as click, mouseDown, keyPress, and so on. Your control automatically exposes these events, and they are available in the host application without any special effort on your part unless you want to override the events to add special functionality.

Note   For information about creating event handlers — receiving events in a host application — see Handling Events in WFC. For details about how to capture a standard event and process it within your control, see Capturing User Interaction with a Control.

In some instances, however, you might want to create custom events that are unique to your control. For example, you might want your control to use an event to notify the host application of a change in status, such as the completion of an initialization procedure, or to report an error condition.

Note   By convention, to indicate a change in the value of a property, you create on<property>Changing and on<property>Changed methods using the techniques described below. For additional details, see Providing Property Change Notification.

Custom events rely on Visual J++ delegate technology. To implement a custom event, you create a delegate that can bind event handlers in an application to your event. This topic provides information on the following aspects of creating custom events:

Finally, at the end of this topic you can find a complete example that shows the process in the context of a simple control.

Using Delegates for Events

The event model for WFC uses delegates to bind events to the methods used to handle them. To define an event in Visual J++, you subclass the Event class, and then create a delegate class based on com.ms.lang.Delegate that binds to it. The delegate allows other classes to register for event notification by specifying a handler method. When the event occurs (is invoked), the delegate calls the bound method.

Delegates can be bound to a single method or to multiple methods, referred to as multicasting. When creating a delegate for an event, you typically create a multicast event. A rare exception might be an event that resulted in a specific procedure (such as displaying a dialog box) that it would not be sensible to repeat multiple times per event.

A multicast delegate maintains an invocation list of the methods it is bound to. The multicast delegate supports a combine method to add a method to the invocation list and a remove method to remove it.

A control fires an event by invoking the delegate for that event. The delegate in turn calls the bound method. In the most common case (a multicast delegate) the delegate calls each bound method in the invocation list in turn, which provides a one-to-many notification. This strategy means that the control does not need to maintain a list of target objects for event notification — the delegate handles all registration, notification, and unregistration.

Delegates also allow multiple events to be bound to the same method, allowing a many-to-one notification. For example, a button click event and a menu command click event can both invoke the same delegate, which then calls a single method to handle these two separate events the same way.

The binding mechanism used with delegates is dynamic — a delegate can be bound at run time to any method whose signature matches that of the event handler. This feature allows you to set up or change the bound method depending on condition and to dynamically attach an event handler to a control.

Creating a Custom Event Class

If you want to pass event-specific data with the event (similar to the x and y properties of the Control class' mouseMove event), you must create a custom event class and define its data. To create a custom event, you subclass the Event class. To make the event a top-level public class, it must reside in a separate .java file. In your class you define members to hold the event data.

The following example shows how you can create a simple error event class that has two fields, one for an error number and another for the text of the error message:

// ErrorEvent.java
import com.ms.wfc.core.*;
public class ErrorEvent extends Event{
   public final int errorNumber;
   public final String errorText;
   public ErrorEvent( int eNum, String eTxt){
      this.errorNumber = eNum;
      this.errorText   = eTxt;
   }
}

Creating a Delegate

To allow binding for your event, create a handler class which declares a delegate specifying the signature for your event. If you want to be able to bind the delegate to multiple methods, declare it as a multicast delegate. The delegate should be a top-level class, it should therefore be in a separate file.

The following example shows a handler class for the ErrorEvent class illustrated earlier:

// ErrorEventHandler.java
import com.ms.wfc.core.*;
public final multicast delegate void ErrorEvent(Object sender, 
   ErrorEvent event);

Multicast delegates must return void, because they cannot return the results of more than one method.

Implementing a Custom Event

To implement a custom event in your control, you provide a way for the host application to register for the event. First create a private instance of your delegate. Then create an addOn<event> method that the host can call to register for an event and bind it to a specific method. If the delegate is multicast, call the delegate's combine method in your addOn<event> method to add the user's method to the delegate's invocation list.

The following example shows how you can perform these steps for the ErrorEvent class illustrated earlier:

// Create instance of delegate
private ErrorEventHandler err = null;
// Call delegate's combine method to add binding to invocation list
public final void addOnErrorEvent(ErrorEventHandler handler){
   err = (ErrorEventHandler)Delegate.combine(err, handler);
}

Typically, you also provide a removeOn<event> method so the host can unregister for the event:

public final void removeOnErrorEvent(ErrorEventHandler handler){
   err = (ErrorEventHandler)Delegate.remove(err, handler);
}

If you are using a non-multicast delegate, the syntax for the addOn<event> method is simpler:

private ErrorEventHandler err = null;   // Create instance of delegate
public final void addOnErrorEvent(ErrorEventHandler handler){
   err = handler;
}

Note   The Component class from which the control derives contains utility code to add, remove, and fire events.

Exposing the Event at Design Time

Exposing an event at design time is similar to exposing a property. First, you create an instance of the EventInfo class indicating the new event's class, name, and delegate. You then you override the superclass' getEventsEventInfo method, adding the superclass' existing events and then your new event.

Note   The event name should start with a lowercase letter (for example, errorEvent), unless the first two letters are uppercase (such as MIDIChildActivated).

The following example shows the ClassInfo class for the ErrorEvent event shown earlier:

public static class ClassInfo extends Control.ClassInfo{
   public static EventInfo errEvt = new EventInfo(MyControl.class,
       "errorEvent", ErrorEventHandler.class);
   public void getEvents(IEvents events({
      super.getEvents(events);
      events.add(errEvt);
   }
}

Firing an Event

To fire the event, you create an instance of your event class, passing to it the event-specific parameters that you defined in the class. You then invoke the delegate, as in the following example:

// the err delegate must already exist
int errNumber = 1020;
String errText = "Invalid value."
ErrorEvent e = new ErrorEvent(errNumber, errText);
err.invoke(this, e);

Most events in WFC include a protected on<eventname> member that lets subclasses override the event and determine the order in which the event is triggered through placement of the call to super.on<eventname>. The following shows the protected member for ErrorEvent:

protected void onErrorEvent(ErrorEvent event){
   if(err != null){
      err.invoke(this, event);
   }
}

If you include the protected member, you can fire the event within your control by calling it instead of directly calling the invoke method, as in this example:

if(value > 20){
   onErrorEvent(new ErrorEvent(1020, "Invalid value"));
}

A Complete Example

The following shows a complete, simple control that includes the ErrorEvent event described in the preceding sections:

 // MyControl.java
import com.ms.wfc.ui.* ;
import com.ms.wfc.core.* ;
import com.ms.lang.Delegate;

public class MyControl extends Control {
   private int myProp = 1;
   public int getMyProp(){
      return myProp;
   }
   public void setMyProp(int value){
      // Fires error event if property value exceeed 200
      if(value > 200){
         onErrorEvent(new ErrorEvent(1020, "Invalid value"));
      }
      myProp = value;
   }

   protected void onPaint( PaintEvent p){
      super.onPaint(p);
      Graphics g=p.graphics;
      g.drawString( getText(), 0, 0);
   }

   // event setup to be able to fire event "ErrorEvent"
   private ErrorEventHandler errDelegate = null;
   public final void addOnErrorEvent(ErrorEventHandler handler){
      errDelegate = (ErrorEventHandler)Delegate.combine(errDelegate,
       handler);
   }
   public final void removeOnErrorEvent(ErrorEventHandler handler){
      errDelegate = (ErrorEventHandler)Delegate.remove(errDelegate,
       handler);   
   }
   protected void onErrorEvent(ErrorEvent event){
      if(errDelegate != null){
         errDelegate.invoke(this, event);
      }   
   }
      
   public static class ClassInfo extends Control.ClassInfo{
      public static final PropertyInfo myProp = new 
         PropertyInfo(MyControl.class, "myProp", int.class);
      public void getProperties(IProperties props){
         super.getProperties(props);
         props.add(myProp);
      }
      
      public static EventInfo evt = new EventInfo(MyControl.class, 
         "errorEvent", ErrorEventHandler.class);
      public void getEvents(IEvents events){
         super.getEvents(events);
         events.add(evt);
      }
   }
}