I have a SWING GUI class that instantiates a custom JPanel for a portion of the display. This custom class has buttons and textfields and etc. My GUI class that owns the custom JPanel also has a controller class that handles the modification of the my data models. How can I pass actions from the custom panel to it's owner (my gui class) to handle the events?
I've had the thought that perhaps I can add to my constructor of the custom panel a reference to my controller class in the gui so that I can then set it as the actionListener on my buttons. Is this approach advisable? Is there a better approach?
Your View code (your custom JPanel) should have a Controller field (or some other way of obtaining your controller class). That way when you receive an action from the user - e.g. a mouse click on a button - you can call controller.doTheAppropriateAction(). Either pass the Controller in at construction, or use a Javabean setter pattern on it, and set it just after construction in your start-up logic (which sounds like your "GUI class"). I prefer the Javabean pattern, because GUI Editors need no-parameter constructors.
You should register your View as a Listener to the relevant Controller (or Model) classes, so that you will automatically be told when anything changes - so you can repaint() your Components (or do something more advanced). That will involve setting up your own interface (for the View to implement) and listener handling logic in the Controller (or Model).
Lombok PG takes the boilerplate out of the latter.
akf gives an alternative: register your Controller as an ActionListener to your View code. The advantage of that approach is that your View code will not be tied to a specific Controller - but the disadvantage is that your Controller will be tied to your View code. I tend to re-use Controller code for different UI implementations (e.g. Swing, GWT, JSP) so my Controllers and Models are never dependent on any particular View or atomic user action.
You can pass a reference to your parent GUI as an ActionListener to your custom panel. Your custom panel can then register your ActionListener-implementing parent class with all of your buttons, etc.
Your parent class would then get notified on each action.
Related
I'm looking for a way to cleanly organize my UI code in Swing.
Let's say my UI-code is structured in the following way:
class MainWindow extends JFrame {
// SomePanel panel is added here
}
class SomePanel extends JPanel {
// buttons, checkboxes and so on are added here
}
Lets say I'm instantiating a MainWindow-object inside my main method:
MainWindow frame = new MainWindow("I am an App");
What is the best practice for listening to ActionEvents of buttons (which are declared inside SomePanel, which is declared inside MainWindow) from within my main-method?
Thank you very much for your help!
Use a PropertyChangeEvent, seen here and here, to communicate results from one container to another. Other ways to implement the observer pattern are mentioned here.
Addendum: You're suggesting writing custom ActionEvents?
EventListenerList is another way to implement the observer pattern. Such a list is common to every JComponent, and it is appropriate when more than one event type must be managed. JFreeChart is another popular example that uses diverse events to update chart subcomponents when the data model is changed.
I have started trying to create normal MVC Swing components. I have no problems with M and C, but V had thrown at me one problem which I cannot normally solve.
The problem is: Controller is main class of the component (MyComponent, for example), and it extends JComponent. View is ui delegate (MyCompanentUI) extended from ComponentUI class. All what delegate does is adds JTextField in MyCompanent and provides data binding between MyComponentModel and this field. It works just fine. But how I can bind events from JTextField to MyComponent?
If user wants to handle some events he adds listeners to MyComponent, but all real events (mouse, focus, keys, etc.) intercepted by JTextField, about which user does not really knows.
So is there any normal way to do this, except catching events and translate it to original component by hands? Or is there another way to create delegate and I just really do it all wrong?
UPD:
Thanks for your response, trashgod.
But I had something different in my mind. I was talking about something like "events inheritance", like in the case of "inheritsPopupMenu" method. So that then key, focus or mouse event happens to the component one does not process it itself, but directly transfer it to parent component. But it seems impossible, because I have noticed JSpinner has exactly the same issue - you cannot get almost any event notification from this very component.
If you are writing your own JComponent subclass and want to allow for custom UI delegates, I'd start with Kirill Grouchnikov's How to Write a Custom Swing Component.
If you are writing a composite that includes an existing JComponent subclass, such as JTextField, see if you can leverage the existing Action instances described in How to Use Key Bindings. ScrollAction is an example. You can learn the names of such actions from the component's source(s) or using #camickr's handy utility seen in the article Key Bindings.
I have a GUI with nested panels(tabbed with nested panels and etc). I need to pass domain object to deeply nested panel. I can think of two ways:
Instantiate all gui objects in one place, like frame class. That
would make passing domain objects dead simple, but Frame class will
be huge and hardly maintanable.
Each panel has its own class, where we instantiate and layout its
components. Now its easy to maintain and classes are clean, but how
do I pass down the chain my domain objects? I dont want to chain-pass
them through constructors of panels that shouldn't even know their
existance. And top level panels would have a ton of these objects to
start with.
Niether way seems like a soulution. How do you usually aproach this?
When I put together a Java Swing GUI, I have a data model for each major GUI element. Note that this isn't an MVC pattern. This is more like a local MV pattern. If you want, you can consider GUI element listeners as the "controller".
Each panel has its own class, where we instantiate and layout its
components. Now its easy to maintain and classes are clean, but how
do I pass down the chain my domain objects?
You have the right idea, although you shouldn't have to do much passing.
My JFrame (or JApplet) will have an associated model class of global type fields. An instance of this model class will usually be passed along to the children elements. This is to allow children elements to react properly when a menu option is selected (as an example)
My JPanel(s) will have an associated model class that maintains the state of text or button children elements.
More complicated children elements, like a JList or a JTree, already have an associated data model. I will probably wrap these associated data models into the JPanel model class for convenience.
The children elements will trigger some sort of selection or action listener. Some of these listeners might need access to model classes besides the model class associated with the parent. In this case, you're going to have to pass the instances of your model classes to the listeners.
This is sort of a Chain of Responsibility pattern. What I would do is have something that creates a map with all of your display objects in it and pass it from constructor to constructor. That way every instance can take what it needs from the map without caring what else is there.
I'm building my first Swing app and am trying to figure out how my JDialogs - exclusively invoked when the user selects a JMenuItem - can update the components in the main client area of the JFrame which is the parent "window" of the whole app.
This is the design I've come up with, but don't know if its: (1) just plain bad, (2) not the standard (thus best) way, or (3) if I'm totally off-base here. Any suggestions are enormously appreciated.
Basically, the user selects a JMenuItem, which launches a JDialog. The user interacts with the components on the dialog, and then clicks "Done". If everything validates, the JDialog closes, and I want the parent window (a JFrame) to have its state updated (and eventually rippled out into its components).
My design:
Have an AppStateController that is a member of the JFrame subclass (my application). I would then create an AppStateChangeListener and AppStateChange EventObject subclass so that whenever a dialog validates and closes, it fires an AppStateChange event. Since the parent JFrame is the only registered listener to that event, I could define a handler that gets the event passed to it. I would make sure the AppStateChangeEvent had enough metadata to describe all the possible changes.
In theory, I like this approach: it should be clean and free of "spaghetti"-type calls to multiple controls every time a different event fires. However, I fear it may be overkill.
What do best practices dictate here? I'm not really a GUI person!
Java has several ways to implement the observer pattern; several are discussed here.
The mechanism prescribed by EventListenerList is probably the most general, as it would allow you to define your own event and listener types, and it is familiar to Swing programmers. Instead of making the JFrame a listener, let the highest level JComponent do so. Each instance of JComponent has a suitable protected member, listenerList.
Bound Properties are also an excellent choice, as shown here.
If you go with Observable, you'll need to use a delegate.
Addendum: As concrete examples, jfreechart uses the EventListenerList scheme to manage chart, dataset and series events. In contrast, jcalendar uses bean properties to notify listeners of widget selections.
When writing a graphical interface, using Java, what's the appropriate way of switching between the different windows of the application, when clicking a button for example? I.E. what are the windows supposed to be, JPanels, JFrames...? And how do all the components 'see' the 'domain controller' (the class that links the graphical package to the application logic package)?
Any guide or reference would be appreciated.
You start your application with your Controller. In the constructor of your controller, you are going to initialize the first GUI you want to open, lets say GUI_A:
private GUI_A gui_a = null;
Controller() {
gui_a = new GUI_A(this);
}
As you might notice, I called the constructor of GUI_A with one parameter: this. this is referencing the instance of the current class, so this is type of Controller. The constructor of GUI_A has to look something like this:
private Controller controller = null;
GUI_A(Controller ctrl) {
controller = ctrl;
}
This is a simple way to get the GUI known to the Controller.
The next thing you would do is displaying GUI_A:
gui_a.setVisible(true);
If you now want to handle button-clicks, you would do it like this:
First, you add the action-performed method to your button. And, as it is best practice in MVC, you don't want to do logic in your view/GUI. So you also create a corresponding method in your Controller for the action-performed, and call it from your GUI:
// Controller
GUI_A_button1_actionPerformed(ActionEvent evt) {
// Add your button logic here
}
// GUI_A
button1_actionPerformed(ActionEvent evt) {
controller.GUI_A_button1_actionPerformed(evt);
}
Usually you don't need to pass the ActionEvent-var to the Controller, as you will not need it often. More often you would read a text out of a TextField and pass it on to your Controller:
// Controller
GUI_A_button1_actionPerformed(String text) {
// Add logic for the text here
}
// GUI_A
button1_actionPerformed(ActionEvent evt) {
controller.GUI_A_button1_actionPerformed(textField1.getText());
}
If you now want to access some fields on your GUI_A from the Controller, be sure not to mark the fields as public in your GUI, but to create public methods which handle how to display the values.
The preferable way is using Actions. You can attach action to each control. When user action happens (e.g. click on button) the appropriate Action is called. Actions can delegate calls deeper into the application logic and call graphical components (JFrams, etc).
suggestion: use tabbed-panel should do this, JPanel is just a Java container, while JFrame should be the outside windows, they are different things. there should be several JPanels on top of One JFrame. your app can have multiple JFrames.
When writing a graphical interface, using Java, what's the appropriate way of switching between the different windows of the application, when clicking a button for example?
Add an ActionListener to the button. In the actionPerformed(ActionEvent) method, do what needs to be done.
I.E. what are the windows supposed to be, JPanels, JFrames...?
I would recommend making the main window a JFrame and using either a JDialog or JOptionPane for most of the other elements. Alternately, multiple GUI elements can be added into a single space in a number of ways - CardLayout, JTabbedPane, JSplitPane, JDesktopPane/JInternalFrame, ..
And how do all the components 'see' the 'domain controller' (the class that links the graphical package to the application logic package)?
One way is to pass a reference to the object between the UIs.