I have a JPanel with a set of items (for example combo boxes and text fields). Some action listeners are implemented on those items to register user updates.
If the user selects a value in a JComboBox (for example), the action listener captures the event. The corresponding underlying bean method is called and the panel is refreshed. Changing can have an impact on other fields displayed in the pane.
The problem is that when the panel is refreshed, all listeners are triggered, and they call for a refresh themselves. This leads to an infinite loop.
How can I avoid this? I can't get rid of the listeners, because I need to capture user updates, but I don't want these to fire when I am only refreshing the panel content.
One option is to have a central boolean value or some indicator that each listener can check to prevent the chaining of events.
Another option is to not refresh the field if the value does not change. That way each component is updated at most once per refresh.
I can't get rid of the listeners, because I need to capture user updates, but I don't want these to fire when I am only refreshing the pane content
Then remove the listeners, refresh the pane content and then restore the listeners. This way the listeners only fire when a user change is made.
I think that if your problem is in combobox it just points to a bug. Really, if user changes the value of the combobox, that somehow triggers refresh of the pane the value of the combo box should not be changed second time! So if it is onValueChanged() (or something like this) it should not be called at all when pane is being refreshed.
But if for some reason it happens you can verify whether the old and new values are the same and exit the listener.
If this still does not help I'd suggest you some non-standard solution: try to investigate the stack trace into the listener. Can you identify whether the listener was called as a direct reaction to user's action or after the pane refresh? In this case you can create utility method and put it in the beginning of all relevant listeners.
My applications also suffered from this problem, and solution with the flag, that I should check in every listener and enable/disable in code, feels not very good for me. I always forgot to set this flag to true/false in necessary places.
That is why I decide to implement another solution.
I just subclass all default swing components that I am using often, and implemented custom ValueChanged event that I fire after mouse/keyboard/clipboard/etc events. Now I am always know, that if ValueChanged event is fired, it means, that value was issued by user, not by code. Event handling in this way much more cleaner. This solution solves my problem.
Related
I have three relative comboboxes lists that take data from the database, and if I change one, the other also changes through SetModel () method. The problem is that whenever a new pattern, the data in the other lists are changed, the listener again reacts to them. How can I solve this problem? Thanks in advance.
the listener again reacts to them
remove the listener
do your processing
add the listener back to the component
I have a TitleAreaDialog with a TableViewer which allows the user to select a row from the table. The problem is, that the content of the table may change over time. I would like to implement a refresh behaviour commonly found in browsers (e.g. by pressing F5 the content of the table should refresh).
Below is a screenshot which should hopefully make the scenario a little clearer:
It looks like there is a possible solution in this question, but I think it is flawed for several reasons:
The listener isn't properly detached (e.g if I reopen my dialog I have two filters on my Display)
It doen't add the listener to the TitleAreaDialog or a Widget where I believe it belongs from an architectural point of view.
I would like to avoid manual listener-attaching/detaching (e.g. the listener should get disposed together with the TitleAreaDialog)
Long story short: What is the proper way of adding a KeyListener to a TitleAreaDialog (or Dialog in general) without using the filter mechanism as described in the aforementioned question?
I know that this question somehwhat fails in the SSCCE department, but any pointers into the right direction are highly appreciated.
Adding a Listener for key events is a tricky thing. You want the Listener to be fired when none of the contained Controls has focus and you want it to fire even if a child of the Dialog has focus.
There are basically two solutions to this problem:
The obvious choice: Use addFilter when the Dialog is created and removeFilter when the dialog is closed (in close()).
Create a Listener for SWT.KeyUp and add it to ALL children of the Dialog. This is necessary for the event to fire independent of the focus control.
I prefer solution 1, since it's less clutter and SWT will take care of everything (well, except for adding and removing the filter). Adding a Listener to all child controls is nothing you really should do, but it would do the job as well.
If you don't want to add and remove the filter each time, create a subclass Dialog or TitleAreaDialog that does it once, and reuse it by subclassing again.
If adding and removing the filter is too much hassle in general, then I'm afraid there is no easier solution.
I'm making a map editor using java swing for my tile based java game. the swing application has two major components, the "upper" component is the game map preview, and the "lower" component is modifyable properties of the map, like its height and width.
Currently the user types in to a jtextfield for the map width, then I use a change listener to set that value to the GameMap object. The GameMap object when changed fires a notification event to GameMapListeners, the primary listener it has is the preview display of the map inside the swing application.
This lets the user change the map width and instnatly see the results in a preview pane.
Now I want to go to the other way. I want the user to be able to click and drag the edges of the map in the preview pane, but then the results need to then be sent to the properties panel so it shows the updated width value.
This is where the problem is, if I update the jtextfield it'll fire a change event, which would update the GameMap and update the preview display, and then that would fire an event that changes the jtextfield again (so on and on until the program crashes due to stack overflow)
Are there any kind of design patterns i could use instead, or is there some common way to solve this issue?
In this type of case, you have at least two choices...
You Could
Remove the listener to the other component when you want to trigger a change, adding it back after you've raised the event...
You Could
Change the state of a flag to indicate that you should ignore any changes that might come in, resetting after you're raised the event...
Which one you choose will depend on how much code you want to add and how readily available the reference to the listeners in question are (ie, if you don't have a reference to the listener you want to remove, it's kind of hard to implement)
If I update the jtextfield it'll fire a change event, which would update the GameMap and update the preview display, and then that would fire an event that changes the jtextfield again (so on and on until the program crashes due to stack overflow).
When you have a situation like this, you can temporarily remove event listeners, fire the change event, and add the event listeners back. Yes, this is as much of a pain as it sounds, but it's a good way to prevent the stack overflow.
You can see a detailed explanation as well as a working example of managing event listeners in my Sudoku Solver Swing GUI article.
You can use action events for a JTextField. Action events don't trigger when you change the component programmatically.
Here is the scenario. I have an swing applet with tons of checkboxes. some of them are disabled/unchecked when checking another. Each ItemStateChange() event executes a method to parse the entire form for changes. Is there a way to tell if an ItemStateChange() event was triggered due to a mouse click or from a setSelected() call?
The ItemStateChange() for each checkbox has the standard parameter java.awt.event.ItemEvent evt
I'd like to only call the processOrder() method once when a box is clicked. Right now it fires for each change thats made, regardless of whether the change happened from setSelected(). Sometimes there are 10+ parseForm(); calls from a single click.
You can't tell whether the source of the event is a mouse click or a setSelected call from the ItemEvent.
It sounds like you have a loop in your check box logic. You might want to add a controller that handles the events and sets each checkbox yet ignores events that occur due to calling setSelected on other check boxes.
Is there a way to tell if an ItemStateChange() event was triggered due to a mouse click or from a setSelected() call?
If your application manually invokes the setSelected() method then you can use code like:
checkBox.removeItemListener(...);
checkBox.setSelected(...);
checkBox.addItemListener(...);
If you are able to change to use a MouseListener instead of an ItemListener and respond to the mouseClicked() event you will only receive the events for the checkbox selected by the user.
Under certain circumstances, I need a JTabbedPane to remain on one pane until the user supplies certain information. Essentially, when this circumstance occurs, I need the current pane to become modal.
How can I implement this? I was thinking I could catch whatever event is triggered when the pane changes, and reset back to the pane I want to stick on. But I'm worried that this won't be quite right, that depending on when the event actually fires the transition to the new pane will happen after I call the method to set the pane to the pane I want, or some other similar race condition. Is there a better way? Is there a way I can make this approach work?
I would suggest setting the other tabs to disabled. This has a positive effect of providing the user feedback that they cannot click out of the tab. Otherwise they may be madly clicking and wondering why it will not let them leave the tab.
Simply set them enabled again after the required fields are completed.
just disable the JTabbedPane:
pane.setEnabled(false);
and enable it if all fields are correctly set (or whatever condition)
You could use a CardLayout along with JPanels to do what you want and not use JTabbedPanes. Since you need to use the tabbed panes, I would suggest that once the condition has been reached that you want to force the user to stay on that tab set that tab to be the selected one by using.
setTabComponentAt(int index, Component component)
or
setSelectedIndex(int index)
Set a flag indicating that the user should not be able to proceed until completing whatever it is you want them to do and have all the other tabs be disabled using setEnabledAt(int index, boolean enabled)
.
Once the user has completed what they needed in order to continue set the flag accordingly and reenable the other tabs.
I haven't the time to try that solution out but I think it should work.