Skip to main content

Binding JComboBox's Elements and Selected Item

Posted by pkeegan on May 22, 2008 at 7:08 PM PDT

This is part 3 in a series of posts that I'm doing to show how to use beans binding and JPA to create a Java desktop database application. In this installment, I concentrate on combo boxes, namely how to populate combo boxes from a table and then how to bind the user selection to a record. If you want to code along with me, be sure to read my previous two posts.

When we created the skeleton for this application, we (or, rather, the New Java Desktop Application wizard) did not take into account the foreign key from the Clients table to the Countries table. The only relationship between tables that was acknowledged was the one pertinent to the master/detail relationship between the Clients and the Orders tables. So now we will need to take some extra steps to establish the Clients/Countries relationship in the entity classes:

  1. Create an entity class for the Countries table by right-clicking the package that contains your classes and choosing New | Entity Classes from Database.
  2. In the generated Countries class add the following line below the @ID annotation, just as we did for the Clients and Orders classes.
        @GeneratedValue(strategy=GenerationType.IDENTITY)
  3. Press Ctrl-Shift-I to add the necessary import statements.
  4. Modify the Clients entity class so that countryId property is of type Countries instead of Integer and that it is joined with the Countries db table. The following changes are necessary:
    • Replace this field declaration and annotations
          @Column(name = "COUNTRY_ID")
          private Integer countryId;

      with this code:

          @JoinColumn(name = "COUNTRY_ID", referencedColumnName = "COUNTRY_ID")
          @ManyToOne
          private Countries countryId;
    • Change the type of the getCountryId() method from Integer to Countries.
    • In the setCountryId() method, change the types of countryId and oldCountryId from Integer to Countries.
    • Press Ctrl-Shift-I to add the imports for the pasted code.

We also need to update the column binding for the country field so that it refers to the country property of the Countries object instead of an Integer. (Code to use a country ID Integer was generated by the project since the skeleton was generated without having an entity class for the COUNTRIES table. If we don't make a change here, a ClassCastException will be thrown when you run the application.) Here are the steps:

  1. In the main form, right-click the top table and choose Table of Contents and then click the Columns tab.
  2. In the customizer, select the Country Id row.
  3. Change the Expression to ${countryId.country}. After you do so, the type should also change to String.
    masterdetail3-changeTableContents.png
  4. Change the Title from Country Id to Country (this affects the column heading in the running application).

Now it's time to do the binding for the Country combo box in the dialog.

  1. Switch back to the EditClient.java file and click Design at the top of the editor to work with the file in Design view.
  2. Right-click the combo box and choose Bind | elements.
  3. Click Import Data to Form, select the database connection, and select the Countries table. countriesList should appear as the binding source. Click OK.
  4. Right-click the combo box again and choose Bind | selectedItem.
  5. Select Form as the binding source and currentRecord | countryId as the expression. Click OK. (As you may recall from the last post, we are using a custom bean called CurrentRecord as a liaison between this dialog and the main form.)

The combo box is almost ready to work properly in the dialog. It is set up to draw its values from the Countries db table, and the item that the user selects is then applied to the country field in the current record. However, we still need to customize the rendering of the combo box, since the values bound to the combo box are Countries objects, not simple names. We will do that by specifying a custom cell renderer. (For JTables and JLists, the beans binding library enables you to specify display expressions, thus avoiding the need to create a custom renderer, but that feature does not exist yet for combo boxes.)

To get the combo boxes to render country names, do the following:

  1. Create a new class called CountryListCellRenderer in your project.
  2. Delete the generated class declaration and paste the following code below the package statement:
    import java.awt.Component;
    import javax.swing.DefaultListCellRenderer;
    import javax.swing.JList;

    public class CountryListCellRenderer extends DefaultListCellRenderer {

        @Override
        public Component getListCellRendererComponent(
                JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
            super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
            if (value instanceof Countries) {
                Countries c = (Countries) value;
                setText(c.getCountry());
            }
            return this;
        }
    }
  3. Compile the class.
  4. Select the EditClient form in the Source Editor (make sure that the Design view is selected).
  5. Drag the class from the Projects window to the white space surrounding the form, as shown in the screenshot below.

    Doing so adds the renderer to your form as a bean, much like dragging a component from the Palette adds that component to your form.

    masterdetail3-dragrenderer.png

  6. In the form, select the combo box.
  7. In the Properties window, scroll to the renderer property and choose countryListCellRenderer1 from the drop-down list for that property.
    masterdetail3-selectrenderer.png

The combo box should be ready to go - except for one thing. It doesn't have any values to display yet. You can go ahead and populate the table with a few SQL commands and then run the project. Or you can indulge me in this digression that demonstrates how you can quickly do this with a few hacks within the IDE (and shows you some handy features along the way).

First, create a separate form for adding countries to the db by doing the following:

  1. Right-click the package containing your classes and choose New | Other.
  2. Select the Swing GUI Forms | Master/Detail Sample Form and click Next.
  3. Give the class the name CountriesForm and click Next.
  4. Select the database connection, select the countries table.
  5. Since we won't be editing the Country_ID fields by hand (the values will be automatically generated), move the Country_ID column to the list of Available Columns. Then click Next.
  6. Click Finish to exit the wizard.

We have just essentially created another application with its own main class. In order to properly run this class, we need to temporarily make it the main class of the project. (Simply using the Run File command won't work since this command doesn't pick up classpath dependencies.) We can do so by creating a new project configuration.

  1. Choose Build | Set Main Project Configuration | Customize.
  2. Click New and then enter CountryEditing as the configuration name.
  3. Click the Browse button next to the Main Class field and select the CountriesForm class.
  4. Click OK.

    The configuration is automatically switched to the new configuration.

You can now start editing the list of countries.

  1. Choose Run | Run Main Project.
  2. In the simple application that runs, click New to create a new row and fill in a country.
    masterdetail3-CountriesForm.png src="http://weblogs.java.net/blog/pkeegan/archive/masterdetail3-CountriesForm.png" width="416" height="243" />
  3. Repeat step 2 a few times so that you have multiple countries to choose from.
  4. Choose Build | Set Main Project Configuration | so that the main application runs the next time we use the Run Project command.

Once you have some countries in the the Countries table, you can run the main application and see the combo box in action:

  1. Choose Run | Run Main Project.
  2. In the running application, click the first New button.
  3. Enter values into the various text fields and choose a country from the combo box.

    Notice that the values that you enter in the dialog box also appear in the top table in the main form, including the country you selected from the combo box.

    masterdetail3-combo-working.png

  4. Since we have not coded the buttons in the dialog box yet, move the dialog out of the way and click Save in the main form to save the changes to the database.

The application works, but it's still very rough around the edges. Here is some quick tidying up we can do now:

  1. Make the the columns in the table uneditable. You can do so by right-clicking the table, choosing Table of Contents, clicking the Columns tab, and then clearing the Editable checkbox for each of the items. This is particularly desirable for the Country column so that you can manage the what people enter for countries (e.g. to avoid misspellings) and better handle changes in country names (the change only needs to be made in one place), etc.)
  2. Change the text of the New buttons to distinguish them. I'm going to use New Client and New Order. You can change the text inline (by clicking the button once, pausing, and then clicking again). Or, if you want to change the text in every place that the action is used (such as from a menu), you can right-click the button, choose Set Action and change the Text attribute.
  3. Delete the superfluous main() method in the EditClient.java class.

We still have some work to do, such as:

  • Adding functionality to the Save and Cancel buttons in the dialog
  • Making it possible to edit existing records
  • Doing some currency formatting

I'll cover those topics and others in ensuing posts. Where time and personal knowledge allows, I'll try to field requests as well.

Comments

ComboBox Rederer

Mr. Keegan, Excellent post! Thank you. I was wondering in a very similar scenario, I implemented the renderer for a Date field uneditable ComboBox as following: GameDate.setRenderer(new DefaultListCellRenderer() { @Override public Component getListCellRendererComponent( JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); if (value instanceof Games) { Games meg = (Games)value; SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MMM-dd"); setText(value == null ? "" : dateFormatter.format(meg.getGameDt())); } return this; } }); But for some reason, frustratingly I cannot figure out, it does not seem to be working. Since I see the fields being displayed similar to the format "Sat Nov 07 00:00:00 PST 2009" . Games is an entity class created of a corresponding database table and GameDate is defined as a Temporal Date field of the entity. I am new to this and obviously must be making some silly error. I will be deeply obliged if you could kindly help me to get through my obvious frustration. Thank you, Tapas

Hi Patrick, I am using netbeans 6.5 and create a simple application containing Customers table and Coutries table. I generated the application using master/detail skeleton and in detail option page I select textfields rather than table option to utilize the editing component. I replace CountryId textfield component with JcomboBox. I don't have dififculty to showing proper country list on the JCombobox once the application being run but everytime I selec an item on the JCombobox the changing was not reflected on the Jtable. Please advise me what was wrong? since I don't have similar problem when the editing component were place on the separate form (JDialog Form)

Hi yoguess, In this blog entry, the Save functionality isn't yet set up. You will have follow the next entry to see that work. Also note that the event model is different. The Swing Application Framework uses enhanced action support, whereas the master/detail form uses straight event handlers, so there is some variation there.

hi patrick, i`m new to Gui using netBeans. i have build the whole project as suggested... now i have done some changes. instead of selecting "database Application", i have selected "basic Application" then made master/detail sample form. i have then kept some text fields and comboboxes. binded it as u have suggested. everything is working fine except when i select any item from combobox, the item is not getting saved in database.... please suggest me.... thanks in adv

Hmm, I guess I had would have to see your code to know for sure what is going wrong. Have you created both a custom editor and custom renderer for the combo box in the table column (and then specified the editor and renderer in the Table Contents/Columns dialog)? You might be merely missing the custom renderer.

Hello Patrick, first of all thanks for the nice tutorial. I did learn a lot how to handle things. As a novice in Java I do have to learn a lot after .NET and Visual Studio. But I like it and yourt tutorial showed that it is not such a long way to get where I want to get. I have one question. After playing with your tutorial I went further and added a combobox to the JTable (Custom editor). For example, I have a table called Customers and it has a join column AddressID connected with the entity Address. Now, the combox is populated with Address-Objects correctly. But when I select on Address I get the ClassCastException. OK the solution may be to override the getCellEditorValue() method of my custom editor. But from here I have no Idea what I should do in the overridden method. Can you give me a hint? Thanks

Yes, you can use toString() for this case. I don't do it because toString() can be used for any number of things, so I wanted to keep the separation. But that might be worth calling out in the final version of the tutorial.

There is any advice against doing a customized toString() method in Countries instead of using CountryListCellRenderer ?. I know that CountryListCellRenderer is better if you want to display icons and text but a customized toString() seems better for simple cases.

What about if I want to use a List box instead of the dropdown combo box? I tried making the application from the start again and I modified the SQL statement so that the "countries" in the "Client" table is set to string. When I get to making the Jdialog (EditClient), if I use a textbox for the countries (like the others) it works fine. I tried using a List box and I have successfully binded the elements property and when I run the application, it shows the list of countries from the countries table. But when I bind the selectedElement property, when I run it, I get the ClassCastException. How can I make it work? Is it a mistake that I modified the SQL statement? Thanks!