I have a question about GUI design, specifically with Java Swing and creating clean separation between presentation and model.
It's a bit difficult to describe, but essentially we have lots of reference data in our system (i.e. that would correspond to lookup tables in the DB). We want people to be able to edit them all from one screen.
So, in an ideal world what we'd like is a combo box in the top-left corner with a list of 'types' of reference data (so each corresponding to one table in the DB).
Then, when selected, a list of the data is populated below, also a filter (or search box). When one of these items is selected, the panel to the right is activated which will allow the actual data to be edited.
Now, here's the problem: each type of data we need to edit is different, so it has different fields etc. We could go with a generic solution but I'm not really a fan of them - there are lots of different validation rules for each etc, even for different clients, and it would be a nightmare to manage.
We're using the Presentation Model pattern to achieve some degree of separation between GUI code and the model but I can't think of a clean way of doing this which doesn't somehow blur the line of responsibilities a bit.
What are the ways you have solved problems like this?
[Note: apologies for the long question, hope it's understandable, I can re-phrase if necessary]
You could use the Factory Pattern to create a UI widget for the element that you are selecting. You could also use it to create a validation rule object depending on the type. This would give you some of the flexibility you desire.
So you can have something like:
IWidget widget = UIFactory.createFor(myObject.getType())
That can be invoked on the selection event to create the right widget to edit the selected element.
The IWidget could have methods such as:
validateData()
refreshData()
setDataElement(IDataElement element)
That would allow you to treat all UI widgets generically, but still have a special UI widget for each element type. I am assuming that the elements that you are selecting from the table all implement some IDataElement interface that contains the getType() method.
I used this solution tied together with the Eclipse Extension mechanism to plug-in new UI elements into my "base" solution, to have an extensible core and a high level of reuse. You could achieve something similar by injecting types and widgets into your factory, either manually or with Spring.
If you dont want to go down the generic path, you could have your model hold a mapping of combobox item -> panel name for use with a CardLayout. You could then create custom panels for the editing each of the reference data types. When the combo box selection is changed, you can save the current state in your model, request the panel name of the current selection, prepare your next panel for display and then have your CardLayout show it.
Related
I've been using a ComboBox to store some values and make a selection from those values, but the problem is, ComboBox, as it is, only allows one selection at the time and I need multiple selections, ie checkboxes, but that cannot be done via Vaadin. I figured if I could present checkboxes as the elements of the ComboBox, that would solve the issue, except adding components to a component that is not a layout doesn't seem to be possible.
I've done this tutorial https://vaadin.com/docs/-/part/framework/components/components-customcomponent.html
Basically it combines two Vaadin components into one panel and displays them together, but that's not what I need, as I need certain components to be placed inside a parent component.
So what are my options if I'm to do this?
This is not an answer to the question that you are asking (component within a component), but rather the underlying problem that you present. In other words, I believe your question is an example of an XY problem.
I think you want to use a Grid with multi-select turned on. In this mode, check boxes are automatically added to each row and there is a checkbox in the header to allow toggling all on/off, ability to filter, ability to sort columns, etc. See the documentation for more details.
I've been trying to freshen up on my Java knowledge and I've been building a small GUI program and I've been running into a bit of a problem.
Basically, I have a JList which I'm currently populating with strings from an object from one of my classes which implement AbstractListModel which we can call my ItemList class. It contains an ArrayList of objects of the type Item which implements Serializable.
But, what I'd like to do is rather than populate my JList with a bunch of strings I'd like to populate it with some kind of string + JTextField combination so I can see one property of each Item object while also being able to update another property by changing the JTextField.
Now, what I'm looking for is the simplest possible way of doing this, and I am assuming there is a (relatively) simple way to do this since it's such a common thing to want to do in a GUI application (although I wouldn't put it past Java and Swing to make it convoluted and complicated).
So, what is the correct way of doing this?
No need to ever use String objects. Instead:
Put Item objects in the JList.
Add a ListCellRenderer to the list, to show the Item object the most user friendly way.
When the user selects an item, show the details in a different place (I'm thinking a panel with 2 columns of labels and text fields, and two rows - one for each attribute, and perhaps a button to Save)
The edit controls would best be encapsulated in a panel that can then hidden when not required, & put in a variety of places, e.g.
Below the list
In the main part of the GUI
displayed in a JOptionPane or a (modal or not) JDialog
Here is an example of placing the 'view/edit panel' (the file details) below the selection component (the table).
I am trying to make a properties frame just like the one in netBeans (or Visual Studio). My problem is that I don't know exactly how to design it. First I thought I'll make it with JTable (2 columns, multiple rows) but then I realised that on the second column I will have different types of values (booleans, String, color choosers, etc.), but I think that JTable allows only 1 type of data to be placed in a column.
I would like someone to tell me "JTable allows multiple data types on the same column" and show me how to do it, or tell me a different approach to the problem.
You can perfectly tell a JTable to have a column that contains Object, this way you will be able to put whatever ou want in.
BUT.
You'll then have to implement a very good TableCellRenderer/TableCellEditor pair in order to display whatever the cell contains.
Another option would be to use a Grid or GridBag layout inside of a JScrollPane, then dynamically populate the cells of the grid with different editors depending on the data type of the property.
If you can use external libraries, the JGoodies FormLayout is really suited to create such dialogs. Just take a look at the screenshots in their demo.
There is also a rather good PDF available containing with some examples and explanations.
In my application, I have URN-identified data coming in from the server. I'm in the process of abstracting as far as possible so there is very little to no logical code in my views, and I'm using a generic presenter that wraps those views. All widgets have URNs, making it super easy to map incoming data to a specific widget (until now, a 1 to 1 relationship). This has worked well for pretty much every widget, and now I've reached a point where I'm tripped up.
Assume I have (just for simplicity's sake) two RadioButton elements on a view. These buttons belong to a "group" (just by setting their name values to the same thing), but obviously they're 2 distinct elements. I can't map my URN-identified data to a single widget as in every other case because, in this case, it is two widgets.
Here's an example of what I mean:
Utility Company is a ListBox, so just one widget there. I map each item in the list to a specific Enum value.
Utility Rate is a TextBox, so again just one widget to map.
For Energy Usage, they can select to use either an average for the year or input 12 monthly values. I'm stuck here. I can't map to just one of the RadioButton elements, because then I'd need some extra logic in the view to handle the behavior appropriately.
Am I stuck mapping to just one widget and sticking (unwanted) logic in my view to determine what the state of all of the elements should be based on the value that came in for the one widget that is mapped?
How should I handle this case?
Edit (Solution):
Following the concepts of jusio's answer, I came up with a workable solution. Because I didn't want to go sticking special case handling through my logic to take care of a non-widget, I created a RadioButtonSet faux widget (public class RadioButtonSet <T extends Enum<?> & HasDisplayText> extends Widget implements HasValueChangeHandlers<T>, HasValue<T>), into which I manually pass the radios I intend to group. Having done that, I can get or set its value and have it fire appropriate events when the user changes the selection. Then mapping the collection of radios is no different than doing so for a listbox. Thanks jusio.
I believe in your case you shouldn't treat radio buttons as two separate widgets, basically in your case you can treat the radio button group as combo box, because behavior is almost the same (the only problem is that you have additional master detail). So basically what you will have to do is to wrap real BO objects into some kind of RadioButtonGroupModel, and give it to view, view can take this model and generate radio buttons (with some editors or whatever else). I remember running into this problem when i was extending databinding FW for JFace, and this was the best way I could find to solve this problem.
If I understood correctly the problem, there are 2 possible solutions:
Give each RadioButton a unique URN (ex: oldURN_1 , oldURN_2)
When you send data for a URN, disable the other one
Keep the same Name for each RadioButton but add a number variable in the data the server sends indicating which radioButton it is supposed to use (ex: 0 for Average and 1 for Monthly)
I've got a blocking problem with the sorting functionality of a JTable; this made stall the development of the spare time open source project for 4 months now. Hope to be pointed into the right direction here.
Context: I'm working on extending the functionality of the ps3mediaserver to add a media library with pms-mlx. The UI of the media server has been done using swing.
Problem: When clicking on a column header in the JTable, a seemingly random column gets sorted instead of the one having been clicked.
Current implementation: Here's the description of the different components and classes being used for the implementation:
ETable: As alternate row colours aren't supported by default in the JTable, I've switched to the ETable extending the JTable. Source comes from here
FileDisplayTable: This is the class creating the table. In the init() method, the sorting is being enabled with 'table.setAutoCreateRowSorter(true);'
FileDisplayTableCellRenderer: Exists to always align cell content on the left
FileDisplayTableColumnModel: Does some mapping between internal types and column names
FileDisplayTableAdapter: This class implements com.jgoodies.binding.adapter.AbstractTableAdapter to map the objects with the table columns.
Possible solutions:
Preferably, I'd like to keep the current implementation and figure out how to correct the sorting, but I doubt someone can help me out with that!? Additionally their are some bits of code I had to add because of strange behaviours; they're commented in the code
The alternate option would be to change the JTable for another control altogether. I've made some research but didn't find the solution I was hoping for. The constraints are that
it must be embeddable in a swing UI
preferably it should support data bindings
support alternate row colours
row sorting
At some point it will be possible to open an editing dialogue, where the content of the row has to be retrieved, can be edited and when saved the row has to be updated.
Before reworking the entire thing I'd like to be sure the component will be able to handle all I want to do with it.
I'm more used to create GUIs using .NET in Visual Studio. It's quite different and a lot more difficult to do the same with swing. Please show me I'm wrong :)
[edit] If someone is willing to reproduce the problem, either get the source or the binaries, launch the application, navigate to the media library tab. In the Genral section import some videos by adding some video files. Go to the library section, click on apply to refresh the list and try to sort the table.
It may be useful to know that JTable columns can be dragged by the user. As a result, the view (JTable or a subclass) and model (an implementation of TableModel) may have different column numbers. Similarly, a RowSorter may affect the order or number of rows in the view as compared to the model. The related conversion methods are mentioned in How to Use Tables: Sorting and Filtering. In particular: "When using a sorter, always remember to translate cell coordinates."
Addendum: As an alternative, consider org.netbeans.swing.etable.ETable or it's subclass org.netbeans.swing.outline.Outline, depicted here.