subscribe user control to event published by its parent

Posted by Admin on 11-Sep-2009 05:50

First post so please be kind

I've created a simple user control which is a button bar. An example of what I want to achieve: I have a save button on the user control (that will be reused on different update screens). I want the save button on the user control to be initially grayed out. When the user changes some data (somewhere in the parent), I want to publish an event eg, "eUpdate", which my user control will subscribe to and enable the save button.

I've tried various combinations of the subscribe statement in my user control, but always get complaints from architect.

eg. SUBSCRIBE PROCEDURE THIS-OBJECT:pUpdate TO "eUpdate" IN THIS-OBJECT:PARENT.

and SUBSCRIBE TO "eUpdate" IN THIS-OBJECT:PARENT RUN-PROCEDURE pUpdate.

I'd created a method to handle the event, but havnt got far enough to do anything other than a holder.

    METHOD PRIVATE VOID pUpdate():

    END METHOD.

Apologies if this is a really simple thing, I'm still learning and my searches so far have not thrown up anything useful.

All Replies

Posted by rbf on 11-Sep-2009 06:26

Hi Martyn,

I have bad news and good news for you.

The bad news is that PUB/SUB is not supported in classes in 10.2A.

The good news is that strongly typed events, the equivalent in OO, will be supported in 10.2B which appears to be due by the end of this year (YMMV).

Meanwhile, there is a trick you can use by simulating a .NET event which should be described somewhere in this forum. If you cannot find it I can post it.

Don't be afraid to ask questions! There are no stupid questions, just stupid answers.

-peter

Posted by rbf on 11-Sep-2009 07:00
Posted by Admin on 11-Sep-2009 08:57

Thanks for the reply Peter.

The example is interesting, but the opposite to what I want to do. Instead of subscribing to events coming from the user control, I want to be able to do something like publish an event to the user control.

for example, I would have a save button which starts out disabled. When the user of a screen where this control is used, updates some piece of data, I want to send a message to my user control so that I can tell it to enable the save button.

Seems as though this isn't possible as yet?

Posted by Admin on 11-Sep-2009 09:03

mbutterworth schrieb:

Instead of subscribing to events coming from the user control, I want to be able to do something like publish an event to the user control.

Wouldn't a method in the UserControl do the job then? The Container does know UserControl and so it can invoke a custom method when required. There's no need for an event when it's a 1 to 1, parent to child communication.

The Container should as well be able to subscribe a UserControl's method to an event of another Control:

textBox1:TextChanged:Subscribe (userControl1:TextChangedHandler) .

In both cases the method in the usercontrol needs to be public.

Posted by Admin on 11-Sep-2009 09:28

Yes, directly calling a method in the user control would work, and is what I think I'll have to do.

The reason for wanting to use events was to make more generic code. I could create a template for use by each update screen, which include publish events to enable save buttons etc. If a particular screen has the user control, it would handle it. If the screen did not use that user control, the event would get ignored.

If I use a hard coded method call, I need to know the variable name of the user control, and I then have to write code to call the method. I could not use this in a generic template as the screen may or may not contain that user control, and its var name could also differ.

It seems like quite a fundamental thing that I want to do, and I'm surprised its proving to be such a hurdle

Posted by marko.rueterbories on 11-Sep-2009 09:46

Hi Martyn,

instead of directly accessing the variable of the user control you could implement a readable (get) property of type user contol inside your form and invoke the needed methods using this property. Then you only have to change the implementation of the property where you give the correct variable name of your UserControl. The other code in the class is then more generic.

DEFINE PROPERTY IOToolUserControl as MyUserControl NO-UNDO

GET:

     RETURN myUserControl1.

END GET.

Greets,

Marko

Posted by Peter Judge on 11-Sep-2009 09:48

 

It seems like quite a fundamental thing that I want to do, and I'm surprised its proving to be such a hurdle

As was earlier pointed out, you'll have to wait for 10.2B for strongly-typed events, but in principle, what's wrong with something like this?

class MyUserControl inherits UserControl:

  constructor ():

    ...

    this-object:ParentChanged:Subscribe(OnParentChanged).

  end.  

  method pub void OnParentChanged(...):

    /* you might need to climb the Parent tree if using nested UC's */

    if valid-object(Parent) then

     /* EnableSaveButtons is an event defined in the Form */

     Parent:EnableSaveButtons:Subscribe(this-object:EnableSaveButtonsHandler).

  end.

  method EnableSaveButtonsHandler(...):

  /* do whatever*/

  end.

end class.

Prior to 10.2B(Beta), you'll need to create the EnableSaveButtons event in an external .P and use the ABL (procedural) PUBLISH and SUBSCRIBE functionality. Or use the methodology used in the thread that  (the other) Peter pointed you to earlier.

Edit: I should note that you can probably not do the ParentChanged thing, and just subscribe to the EnableSaveButtons event in the user control's constructor.

-- peter

Posted by Admin on 12-Sep-2009 06:23

The code in the property getter should also be able to locate the user Control on it's own:

DEFINE PROPERTY IOToolUserControl as MyUserControl NO-UNDO

GET:

    DEFINE VARIABLE i AS INTEGER NO-UNDO .

    DO i = 0 TO THIS-OBJECT:Controls:Count:

        IF TYPE-OF (THIS-OBJECT:Controls[i], MyUserControl) THEN

            CAST (THIS-OBJECT:Controls[i], MyUserControl) .

    END.

    RETURN ?.

END GET.

Alternatively I'd suggest using an Interface type instead of the exact type of that UserControl.

When the UserControl can also be placed on a tab folder, panel, splitContainer, etc. you need to search the controls using a recursive method. Ultimately I'd move the code to locate the specific userControl to a common form base class, so that all Forms can leverage the code.

Posted by jquerijero on 18-Sep-2009 10:23

You can also follow this simple rule;

- Parent communicates with the child (usercontrol) through the child's properties and methods.

- Child communicates with the parent through events it publishes which the parent has to subscribe. (custom event is not supported in OE)

For your posted approach;

If you start having the child subscribing to the parent's event, you might as well just (1) set the control access to PUBLIC, (3) cast the MyUserControl:Parent to the appropriate type and (3) have the usercontrol handles the event you are interested in. This doesn't promote a reusable usercontrol, but it should still work.

ex.

Inside your UserControl

CAST(MyUserControl:Parent, MyFormType):LastNameTextBox:TextChanged:SUBSCRIBE(. . .)

I think you really just need to write a method inside your UserControl class that the parent can call at the appropriate time.

Posted by Admin on 18-Sep-2009 10:32

jquerijero schrieb:

This doesn't promote a reusable usercontrol, but it should still work.

An Interface for the Form would increase the reusability of the UserControl, but still it's only a poor substitute for an event.

This thread is closed