« [Tip] Servoy Tip for Lost Passwords | Main | [Article] Customizing Security for Servoy Solutions »
October 30, 2007
[Tip] Calling Servoy Methods from Beans Using Listeners
by Jeff Bader
Freight Logistics
Before I write too much I want to point out that the method outlined below is one which is a combination of trial and error and forum input, and may or may not be the most effective way to utilize listeners in Servoy (which, to the best of my knowledge is undocumented?). I am hoping that by posting this article, Servoy and/or some of the Servoy forum's more active members help to further clarify or demonstrate how exactly to handle listeners in the most proper way. In fact, I really feel that a "Java for Servoy Developers" whitepaper would be most beneficial to the community if there is anyone up to the task?
Servoy + Beans
Servoy's inclusion of Java beans gives Servoy developers access to a whole wide range of great UI elements and other tools. For a non-java programmer though (such as myself), they are a bit foreign, and can feel pretty disconnected from Servoy. Beans are very powerful though, and can integrate nicely within your solutions if you know how to use them.
In my case, I wanted a specific bean, the JList bean to integrate tightly with my solution by firing a servoy method each time a selection was made from the JList's "defaultListModel" (AKA list of values). What I found out was if I wanted my JList bean and my Servoy solution to play nice I needed to use a "listener."
Listener Basics
A listener "listens" for some event to occur and allows you to programatically do something when it happens. Different types of listeners can be added to different types of Beans. For example, a JList bean can have a "listSelectionListener" added to it, while a JInternalFrame can have an "internalFrameListener" added to it etc. Different listeners have different events that they listen for, so an event for a listSelectionListener and an internalFrameListener will be different. I have only worked with JLists and their associated listSelectionListeners, so that is what is demonstrated below.
Step 1: Creating a listSelctionListener
Before we can attach a listener to a bean, we need to create it. The amount of code it takes to do this is minimal.
var listener = new Packages.javax.swing.event.ListSelectionListener({ valueChanged: myServoyCallbackMethod });
In the above code, we are creating a new listSelectionListener and specifying the event that will fire the callback (valueChanged), and what callback to fire when the event is triggered (myServoyCallbackMethod). Once we do this, the listener is ready to be added to a bean.
Step 2: Adding a listener to a bean
Assuming you have a JList bean on your form, the following code adds the listener we just created to the JList...
elements.myJList.addListSelectionListener(listener);
So...believe it or not, that's it. We have created the listener, added it to a JList and asked that listener to fire a Servoy callback method whenever the valueChanged* event occurs. Not much to it.
*Re: "valueChanged"
If you use "valueChanged" your callback method will fire for any change of state on the JList.
| Posted by Jeff Bader on October 30, 2007 at 04:32 PM in Tips | Permalink
Comments
I LOVE this tip! I saw this a few days ago and started running with it. Here's the code for running a method when clicking a JButton:
var listener = new Packages.java.awt.event.MouseListener( { mouseClicked: yourMethodName } )
elements.bn_button1.addMouseListener(listener)
Posted by: David Workman | Oct 30, 2007 5:56:18 PM
Great Tip. I love it......
Posted by: Arup Ranjan Sahoo | Oct 31, 2007 8:58:49 AM
"Sliders" and "color choosers" use...
var listener = new Packages.javax.swing.event.ChangeListener({ stateChanged: yourMethodName });
elements.slider.addChangeListener(listener);
Posted by: Jeff Bader | Nov 1, 2007 10:21:07 AM
OK - this sounds great (AKA easy) but being a Bean NooB I have a question.
Where is the
var listener = new Packages.javax.swing.event.ListSelectionListener({ valueChanged: myServoyCallbackMethod });
Defined - i.e. where do I put this code? In the form on which the Bean is placed?
Posted by: Ian Cordingley | Nov 12, 2007 1:48:55 PM
I believe that once you create and attach the listener it is living in mem until you quit your solution. I'm not sure if it matters whether you create it in a global method or in a method on the same form as your bean.
Anyone else have more info?
Posted by: Jeff Bader | Nov 13, 2007 9:11:53 AM
When you start to play around with Java code in Servoy, you have to know what you're doing.
When you start to reference objects like you do here (the reference is the method that gets called on a specific event that the listener listens to), you really need to know what you are doing.
If you do it wrong, you can cause memory leaks. When you stick to Servoy code, Servoy takes care of all that.
When you start to include Java code and reference objects, you are in charge of doing things correctly.
Java cleans up objects from memory automatically if they are not references anymore, thus avoiding memory leaks.
Therefor: when playing around with this stuff: know what you're doing. Learn about java, objects, references and garbage collection, just to name a few concepts.
As long as you use either global methods for the callback OR use a form method for callback for a listener that is attached to an element on the same form, you should be save though.
Paul
Posted by: Paul | Nov 28, 2007 3:27:41 PM
Hey Paul,
For the sake of clarification, I think you are saying that the method outlined works just fine so long as my callback is a Servoy method; global or local?
Thanks
Posted by: Jeff Bader | Nov 29, 2007 10:49:34 AM
No, that is not what I'm saying...
Using globals as callback is fine: Globals all exist in memory for the duration of the session.
Using form method can cause memory leaks, if you take a formmethod from one form and use it as a callback for a listener on an element on another form: That way you create references from one form to the other and the referenced form will never drop out of the form cache unless the reference is removed (for example because the form on which the listener is set on an element drops out of the cache).
Paul
Posted by: Paul | Dec 1, 2007 5:43:38 AM
Seems reasonable.
So if you attach a listener to a "bean" on form A, don't set the callback to a method on form B. Either set it to a method on form A or to a global method only.
Now what if your "bean" is not on any form? Meaning what if you have assigned the swing component to a global variable of type media? I assume then that you would only want to use a global method as the callback, or would it be OK, under that scenario, to use a form method on any form you want?
Thanks for the great info Paul.
Posted by: Jeff Bader | Dec 1, 2007 9:25:21 AM
No, in that case just use a global method
Posted by: Paul | Dec 1, 2007 9:49:23 AM
Update to this post...
I was having issues with listSelectionListeners firing more than once. This was due to the fact that if you are using the valueChanged event, the listSelectionListener will fire once when the value is changed from the old value and once when the value is changed to the new value (twice). To prevent this, test for a false value from getValueIsAdjusting(). Meaning if the value is not adjusting run your Servoy code, otherwise don't do anything.
My technique is to add a return at the top of the Servoy method that I have attached to the listener.
Just an FYI
Posted by: Jeff | Oct 1, 2008 11:41:34 AM