Subscribing to user control events

Posted by rbf on 26-Feb-2009 03:29

According to the docs there are 2 ways to subscribe to user control event:

1. Make the controls public (or protected I suppose) so the parent can directly subscribe to events on the user control.

Drawback: the parent needs to know the specifics of the user control.

2. Use a proxy event handler.

This allows abstracting the events to a higher level.

Do I understand correctly that this approach cannot be implemented using the Visual Designer since it requires adding an input parameter (THIS-OBJECT) to the NEW statement?

Has anyone found the best of both worlds?

All Replies

Posted by rbf on 26-Feb-2009 06:39

Additional question: I am creating dynamic controls (in this case tree nodes) in my user control. How do I subscribe to events on those nodes from the parent???

Posted by Peter Judge on 26-Feb-2009 08:04

Additional question: I am creating dynamic controls

(in this case tree nodes) in my user control. How do

I subscribe to events on those nodes from the

parent???

In the snippet below, I subscribe menu items to the Click event programatically/dynamically. The line that says

oItem:Click:Subscribe(OnClick)

could be something like

oItem:Click:Subscribe(oSomeGenericEventHandler:OnClick).

That line could be called from anywhere, really. You might need to make the Items (or Nodes, in your case) collection public if it isn't already.

Is that what you were after?

-- peter

+

Posted by Peter Judge on 26-Feb-2009 08:18

2. Use a proxy event handler.

This allows abstracting the events to a higher level.

Do I understand correctly that this approach cannot

be implemented using the Visual Designer since it

requires adding an input parameter (THIS-OBJECT) to

the NEW statement?

You'll have to do something yourself. User Controls have a Parent property which refers to the form, so the UC could do a callback to the Form, passing itself (or something else) in.

-- peter

Posted by rbf on 27-Feb-2009 02:18

Is that what you were after?

No.

My problem is how do I subscribe a method in the parentForm to such an event.

Strong typing prevents me from defining that in the user control.

Posted by rbf on 27-Feb-2009 02:19

You'll have to do something yourself. User Controls

have a Parent property which refers to the form, so

the UC could do a callback to the Form, passing

itself (or something else) in.

Can you give an example of defining that upfront in the user control?

Posted by Admin on 27-Feb-2009 03:01

That's where an Interface comes handy... The parent implements an Interface with the event handling method and the UserControl can CAST the Parent to the Interface.

Strong typing rocks! And Interfaces are your best friend...

But why not use a real event? You might abuse some event of the UserControl - like TextChanged... The UserControl can raise the event using the (protected) method OnTextChanged. You need to pass in the NEW System.EventArgs object that will be passed to the event handlers.

You should also be able to sub-class the System.EventArgs to make is a Peter.VanDam.EventArgs with additional information.

One question: What are you using the UserControl for? If it's only using about a single control in the UC an ABL Inherited Control might make the like a little easier.

Posted by rbf on 27-Feb-2009 03:13

That's where an Interface comes handy... The parent

implements an Interface with the event handling

method and the UserControl can CAST the Parent to the

Interface.

How (without modifying the NEW statement generated by the Visual Designer when I drop the UC on the parent form)?

Strong typing rocks! And Interfaces are your best

friend...

I am sure you are right but I don't get yet how interfaces become my friend here.

But why not use a real event? You might abuse some

event of the UserControl - like TextChanged... The

UserControl can raise the event using the (protected)

method OnTextChanged. You need to pass in the NEW

System.EventArgs object that will be passed to the

event handlers.

You should also be able to sub-class the

System.EventArgs to make is a Peter.VanDam.EventArgs

with additional information.

One question: What are you using the UserControl for?

If it's only using about a single control in the UC

an ABL Inherited Control might make the like a little

easier.

Without understanding what you are talking about above, here is what the UC does: it creates an entirely populated Outlook Bar. I pass in a menu temp-table and it figures out what to create. The first level is the group level. If there is one level below the group level, normal items are created within the group. It there are more levels within a group, a treeview is created.

When the user clicks an item or double-clicks a treeview leaf, I want to invoke a method in the parent that NEWs a form. All the parent container needs to receive is the menu item number. It should not be aware of the specifics of the Outlook Bar. It does not even want to know that the menu items are visualized in an Outlook Bar.

So what I need is a callback to, let's say 'startMenuItem' in the parent when the user selects a menu item in the Outlook Bar passing in the menu item number.

Posted by Admin on 27-Feb-2009 06:19

How (without modifying the NEW statement generated by

the Visual Designer when I drop the UC on the parent

form)?

For all my suggestions there's no need to change a single line of generated code!!! (not even a NEW statement).

For the Interface way, the Form needs to implement an Interface (when you create a new Form, that can be done in the wizzard, later it's safe to add that to the CLASS block):

IEventHandler needs one method that returns VOID and expects

sender as System.Object and e as System.EventArgs.

In the UserControl (in the ParentChanged event handler), use:

To "abuse" the TextChanged event of the user control, do this in an event handler INSIDE the user control:

In the Form you need to manually code the event subscription for the UserControl.TextChanged event. The event is there, valid, usable,... but hidden from the property sheet (that's possible in .NET and used because by default the TextChanged event of the UserControl has really no use).

Attached is a sample. Code goes into a InterfaceForEvents folder.

Without understanding what you are talking about

above, here is what the UC does: it creates an

entirely populated Outlook Bar. I pass in a menu

temp-table and it figures out what to create. The

first level is the group level. If there is one level

below the group level, normal items are created

within the group. It there are more levels within a

group, a treeview is created.

Ok. In this case the UserControl may be better because the inheritted control looses the Visual Design capabilities.

[View:~/cfs-file.ashx/__key/communityserver-discussions-components-files/19/EnhancedEventArgs.cls:550:0]

[View:~/cfs-file.ashx/__key/communityserver-discussions-components-files/19/IEventHandler.cls:550:0]

[View:~/cfs-file.ashx/__key/communityserver-discussions-components-files/19/SampleUserControl.cls:550:0]

[View:~/cfs-file.ashx/__key/communityserver-discussions-components-files/19/SampleUserControl.resx:550:0]

[View:~/cfs-file.ashx/__key/communityserver-discussions-components-files/19/TestForm.cls:550:0]

[View:~/cfs-file.ashx/__key/communityserver-discussions-components-files/19/TestForm.resx:550:0]

Posted by Admin on 27-Feb-2009 06:31

A side note: I'd rather go the TextChanged event handler way with a customized EventArgs object.

The THIS-OBJECT.Parent does not need to point to a Form at all. I the UserControl is in a Split-Container than one of the SplitContainerPanels is the parent, not the Form.

Also the event reduces the dependencies.

Posted by Admin on 27-Feb-2009 06:36

You'll have to do something yourself. User Controls

have a Parent property which refers to the form, so

Not necessarily. It could be any container control in the Form (like a SplitContainerPanel).

Posted by rbf on 27-Feb-2009 07:59

Hi Mike,

Thank you for your example. I am starting to get the picture. The code runs correctly, but when I try to load testform.cls in the Visual Designer I get the following error:

SYSTEM ERROR in a .NET event handler

Exception code: C0000005 ACCESS_VIOLATION

Fault address: 10281FD7 01:00280FD7 C:\progress\OE10.2A\bin\prow32.dll

Invalid cast from Progress.Windows.Form to InterfaceForEvents.IEventHandler.

The error occurs at the line that contains all the magic:

IF VALID-OBJECT(THIS-OBJECT:Parent) THEN

ultraButton1:Click:Subscribe(CAST(THIS-OBJECT:Parent, InterfaceForEvents.IEventHandler):ClickEventHandler) .

--> SampleUserControl_ParentChanged InterfaceForEvents.SampleUserControl (G:\IBISappl\query\InterfaceForEvents\SampleUserControl.cls) at line 90

and the Visual Designer crashes.

We are getting very close...

Posted by Admin on 27-Feb-2009 08:19

I've logged the crash already with tech support.

The CAST issue was my mistake. Try

Posted by rbf on 27-Feb-2009 08:59

Well, this works but to say it is pretty... It means I have to subscribe my parent form to some low-level or fake event and figure out in the parent form how to interpret it. This is hardly what I would call 'hiding the implementation'.

Strong typing rocks! And Interfaces are your best friend...

Let me rephrase that to:

"Strong typing can be pretty limiting, and you need interfaces to create workarounds".

Thanks for the help and the honor of my own personal name space

I can continue development now.

I remain open to suggestions!

Posted by Admin on 27-Feb-2009 13:31

"Strong typing can be pretty limiting, and you need

interfaces to create workarounds".

That's not fair to the interfaces.

1.) In this case the Interface is used as a way to make it reuable. If you would not be using an Interface, the UserControl could do the same and CAST/TYPE-OF THIS-OBJECT:Parent to the actual class of the Form. But then you could not reuse the UserControl in a different Form. The Interface enable reusability in this case.

2.) Interfaces are considered the good practice for object to object communication. Eclipse for Java enforces the creation of Interfaces. I like that.

And it's not fair to strong-typing at all. Imagine a PUBLISH with a wrong spelled event name. Nobody would ever notice...

to interpret it. This is hardly what I would call

'hiding the implementation'.

Well - I could live with the event stuff. The Form does not need to know anything about the internals of the UserControl and vice versa. Using the TextChanged event is just something that needs to be defined.

It would be very similar if we could define our own events between ABL classes... So actually that's the missing and limiting functionality!!!!!!!!!!!!!!!!!!!!!!!!!!! (10.2B, please help!)

I remain open to suggestions!

A more general aproach would be the COM event modell. An event subscriber needs to implement an Interface with (potentially) a number of event handlers. The event publisher manages the list of subscribers (in variables of the Interface type or a temp-table of Progress.Lang.Object). Then you can use the Interface when required to execute a method directly.

I am using that in my base class modell and it wokrs pretty well (between data providers, toolbars, browsers and viewers).

The only drawback with this technique is that every event subscriber needs to implement ALL event methods. But I can live with that.

So time for real OO events in the ABL!!!!!!!!

Posted by rbf on 02-Mar-2009 06:30

OK I have got it working now with the TextChanged event and thanks to EnhancedEventArgs.cls the implementation is now hidden and pretty generic.

That is, I am handling both the treeView MouseDoubleClick and the ItemClick event in the user control and raise the 'enhanced' TextChanged event there.

The interesting thing is that I do not need to be bothered with the Parent at all in the User Control using this approach. The line

THIS-OBJECT:OnTextChanged(oEnhancedEventArgs).

in the User Control does not require anyone to be subscribed to that event. That is exactly what I was looking for, as it mimics PUB/SUB in the ABL. Therefore I also don't need to check the interface of the Parent in the User Control. Actually I don't even need the Interface (it all works without).

1.) In this case the Interface is used as a way to

make it reuable. If you would not be using an

Interface, the UserControl could do the same and

CAST/TYPE-OF THIS-OBJECT:Parent to the actual class

of the Form. But then you could not reuse the

UserControl in a different Form. The Interface enable

reusability in this case.

I don't quite understand that in relation to what I found above.

2.) Interfaces are considered the good practice for

object to object communication. Eclipse for Java

enforces the creation of Interfaces. I like that.

And it's not fair to strong-typing at all. Imagine a

PUBLISH with a wrong spelled event name. Nobody would

ever notice...

Well actually that is exactly what I have achieved here and what I wanted...

to interpret it. This is hardly what I would call

'hiding the implementation'.

Well - I could live with the event stuff. The Form

does not need to know anything about the internals of

the UserControl and vice versa. Using the TextChanged

event is just something that needs to be defined.

Yes I can see that now.

It would be very similar if we could define our own

events between ABL classes... So actually that's the

missing and limiting

functionality!!!!!!!!!!!!!!!!!!!!!!!!!!! (10.2B,

please help!)

Yes that is what I actually was looking for but this comes pretty close.

The only flaw is that the TextChanged event is abused to achieve this.

I remain open to suggestions!

A more general aproach would be the COM event modell.

An event subscriber needs to implement an Interface

with (potentially) a number of event handlers. The

event publisher manages the list of subscribers (in

variables of the Interface type or a temp-table of

Progress.Lang.Object). Then you can use the Interface

when required to execute a method directly.

I am using that in my base class modell and it wokrs

pretty well (between data providers, toolbars,

browsers and viewers).

The only drawback with this technique is that every

event subscriber needs to implement ALL event

methods. But I can live with that.

Well I seem to have eliminated that drawback now.... isn't it?

So time for real OO events in the ABL!!!!!!!!

Yes that would be much more intuitive.

If 'OO events' would be similar to PUB/SUB.

Thanks for all the help as always.

-peter

Posted by Thomas Mercer-Hursh on 02-Mar-2009 11:51

There was a nice discussion in the notes that came out with the 10.1A beta dealing with what was implemented, what wasn't, and where forward thinking was heading about strongly typed events. Yes, we want the characteristic of PUB/SUB that one doesn't care if anyone is listening, but we would also like to have strong typing to preserve the advantages of OO.

Posted by Admin on 02-Mar-2009 13:35

The interesting thing is that I do not need to be

bothered with the Parent at all in the User Control

using this approach. The line

HIS-OBJECT:OnTextChanged(oEnhancedEventArgs).

The PUBLISHes a .NET event. But to make it clear (and hopefully not to disappoint you), there is no convention the enforces the presence of such a method in a given Control or .NET class. And in some classes this method is private or protected (like the OnPositionChanged in the Progress.Data.BindingSource).

1.) In this case the Interface is used as a way to

make it reuable. If you would not be using an

Interface, the UserControl could do the same and

CAST/TYPE-OF THIS-OBJECT:Parent to the actual

class

of the Form. But then you could not reuse the

UserControl in a different Form. The Interface

enable

reusability in this case.

I don't quite understand that in relation to what I

found above.

There's no relation - we have been discussion a number of possible ways to solve the task. Interfaces are not required for events.

...

Well actually that is exactly what I have achieved

here and what I wanted...

Yepp. .NET Events are strong typed.

This thread is closed