Handling events on controls contained by ABL-derived .NET classes
When an ABL-derived class is a container of other .NET controls, such as an ABL extension of Progress.Windows.UserControl (see Creating custom .NET forms and controls), the events on these contained controls might be of interest to the class containing the ABL-derived user control, such as an ABL extension of Progress.Windows.Form.
You can use two models to expose events on a .NET control contained by an ABL-derived user control to the ABL-derived form that contains the user control:
Make the contained controls public — Make the object reference for the contained .NET controls public, allowing the ABL-derived form to subscribe directly to events on these controls.
Delegate the event publishing for privately contained controls to ABL class events — If you do not want the contained .NET control to be public, publish public ABL class events on behalf of events on the private control. In other words, subscribe to a given event on the privately contained control and republish it as an ABL class event of the control container. The ABL form object can then respond to the public ABL event without having direct access to the private .NET control whose published event causes the ABL event to be published.
The following contrived class examples, UserControl1 and Form1, show how you might handle events on public controls.
The ABL-derived user control (UserControl1) contains a System.Windows.Forms.Button control that is referenced by a PUBLIC Button1 property.
CLASS UserControl1 INHERITS Progress.Windows.UserControl:
DEFINE PUBLIC PROPERTY Button1 AS System.Windows.Forms.Button NO-UNDO
GET.
PRIVATE SET.
CONSTRUCTOR UserControl1( ):
Button1 = NEW System.Windows.Forms.Button( ). THIS-OBJECT:Controls:Add(Button1). /* Add button to user control */
/* Set any other control properties */
...
END CONSTRUCTOR.
...
END CLASS.
The ABL-derived form (Form1) subscribes to the Click event directly on the Button1 property, like this.
CLASS Form1 INHERITS Progress.Windows.Form:
DEFINE PRIVATE VARIABLE rUControl AS UserControl1 NO-UNDO.
...
CONSTRUCTOR Form1( ):
rUControl = NEW UserControl1(THIS-OBJECT). rUControl:Button1:Click:Subscribe(THIS_OBJECT:Button1_Click). THIS-OBJECT:Controls:Add(rUControl). /* Add user control to form */
/* Set any other control properties */
...
END CONSTRUCTOR.
METHOD VOID Button1_Click(o AS System.Object, e AS System.EventArgs):
/* Process the o and e parameters of the .NET click event */
...
END METHOD.
END CLASS.
If you want an ABL-derived user control to keep all of its contained controls private, you can instead:
Define an ABL class event that corresponds to each .NET control event whose handling you want to delegate. It could have the same or a different signature from the .NET control event, depending on how you need the client form to handle it.
Define and subscribe an ABL method as a handler for each .NET control event. The primary action of this method is to publish the ABL event that corresponds to the control event it is handling. Thus, the form containing the ABL-derived user control can handle the user control event as a delegate for the privately contained .NET control event.
The code for the following contrived ABL-derived .NET container classes (UserControl2 and Form2) demonstrates how to use an ABL event to delegate the handling of an event on a control that is contained privately and not directly accessible from outside its control container.
USING System.Windows.Forms.* FROM ASSEMBLY.
CLASS UserControl2 INHERITS Progress.Windows.UserControl:
DEFINE PRIVATE VARIABLE rButton2 AS System.Windows.Forms.Button NO-UNDO.
DEFINE PUBLIC EVENT UserControl2Click SIGNATURE VOID ( pcInfo AS CHARACTER, pArgs AS System.EventArgs ). ...
CONSTRUCTOR UserControl2():
rButton2 = NEW System.Windows.Forms.Button( ). /* Subscribe UserControl2 to the rButton2:Click event */ rButton2:Click:Subscribe(THIS-OBJECT:Button2_Click). THIS-OBJECT:Controls:Add(rButton2). /* Add button to user control */
/* Set any other control properties */
...
END CONSTRUCTOR.
METHOD VOID Button2_Click( sender AS System.Object, e AS System.EventArgs ):
DEFINE VARIABLE cInfo AS CHARACTER NO-UNDO.
/* Set cInfo to the Text property of the control to identify it */
cInfo = sender:Text.
UserControl2Click:Publish (cInfo, e).
END METHOD.
...
END CLASS.
The ABL-derived UserControl2 defines a public ABL event (UserControl2Click) to delegate handling of the .NET Click event on a privately contained Button control, rButton2. To accomplish this, the ABL event handler that the user control defines and subscribes to this Click event (Button2_Click( )) publishes UserControl2Click. The container for the user control can then access the associated event behavior of the private button control by handling the public event that the user control defines for it.
Note that the parameter list defined for the UserControl2Click event includes an application-specified context string (pcInfo) and the System.EventArgs object (e) that is passed in the Button2_Click( ) event handler parameter list. The purpose of pcInfo, in this case, is to pass a string to the subscribed event handler for UserControl2Click that identifies the private .NET control that has published the Click event while keeping the control object reference private. Thus, the ABL event parameter list ignores (and hides) the sender parameter, which is the object reference to the private control instance typically passed into a .NET control's event handler.
Note: Depending on the application, the delegating ABL event might also pass temp-tables and other application data with which a given control event is associated.
The following ABL-derived Form2 then subscribes its own event handler (UserControl2Click_Handler( )) to the public UserControl2Click event on its contained ABL-derived UserControl2 instance in order to respond to the Click event on the button that the user control privately contains.
CLASS Form2 INHERITS Progress.Windows.Form:
DEFINE PRIVATE VARIABLE rUControl AS UserControl2 NO-UNDO.
...
CONSTRUCTOR Form2( ):
rUControl = NEW UserControl2().
rUControl:UserControl2Click:Subscribe UserControl2Click_Handler. THIS-OBJECT:Controls:Add(rUControl). /* Add user control to form */
/* Set any other control properties */
...
END CONSTRUCTOR.
METHOD VOID UserControl2Click_Handler ( pcInfo AS CHARACTER, e AS System.EventArgs ):
/* Process the pcInfo and e parameters passed by the user control */
...
END METHOD.
END CLASS.
In summary, an ABL event used to delegate the handling of privately contained control events provides the following features:
The subscribing ABL-derived form does not determine what events it can handle on .NET controls contained by its user control. Only the ABL-derived user control determines the events that its client form can handle.
The delegating ABL event defined by the ABL-derived user control passes all information about the private control that fires a .NET event to any subscribed handler for the delegating ABL event, allowing the defining user control to effectively prevent any direct reference to the private control from its client form.
Note that this approach might be practical only for simple controls and a few number of events. In some cases, it is impossible to properly handle a control event without an object reference to the original publisher. For example, if the privately contained control is a grid, there are too many events associated with the rows and columns of a grid to delegate in this way, and any practical response to some of these events requires direct access to the grid reference.