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?
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???
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
+
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
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.
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?
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.
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.
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]
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.
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).
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...
I've logged the crash already with tech support.
The CAST issue was my mistake. Try
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!
"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!!!!!!!!
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
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.
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.