Skip to main content

Using Beans Binding to Search in a Table

Posted by pkeegan on June 5, 2008 at 10:42 AM PDT

Now and again someone will ask me how you can search records in a desktop database app. Here's a reasonably simple way to do so, using mechanisms that exist in Swing and the Beans Binding library. We will create a binding between the rowSorter property of the master table in the example in my previous entries and a text field that I've just added for the search string. For this binding we will need a binding converter so that the table knows how to respond to the search string.

To follow along, you can either continue with the project created in previous entries or begin with a new NetBeans project (Java Desktop Application project template) that connects to a database.

Let's get started. First of all, we'll add a label and a text field for the search field as shown below.

masterdetail5-searchfield.png

Now we will add a converter class to the project.

  1. Create a new Java class in your project. Call it RowSorterToStringConverter.
  2. Replace the generated code in the new class with the following code:
    package clientpurchaseapp;

    import javax.swing.JTable;
    import javax.swing.RowFilter;
    import javax.swing.table.TableRowSorter;
    import org.jdesktop.beansbinding.Converter;

    /**
    * Binding converter between String and regex RowFilter (encapsulated by RowSorterToStringConverter).
    *  */
    public class RowSorterToStringConverter extends Converter {

        private JTable table;

        public JTable getTable() {
            return table;
        }

        public void setTable(JTable table) {
            this.table = table;
        }

        @Override
        public Object convertForward(Object value) {
            return value.toString();
        }

        @Override
        public Object convertReverse(Object mask) {
            TableRowSorter sorter = new TableRowSorter(table.getModel());

            // The following statement makes the filter case-sensitive. If you want
            //filter to work in a case-insensitive way, uncomment the line below, comment
            //the 7 code lines below
            //sorter.setRowFilter(RowFilter.regexFilter(".*" + mask + ".*"));

            //The following 7 lines create a case-insensitive filter. If you want
            //the filter to be case-sensitive, comment them out and uncomment the
            //line above
            String m = mask.toString();
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < m.length(); i++) {
                char c = m.charAt(i);
                sb.append('[').append(Character.toLowerCase(c)).append(Character.toUpperCase(c)).append(']');
            }
            sorter.setRowFilter(RowFilter.regexFilter(".*" + sb + ".*"));
            return sorter;
        }
    }
  3. Adjust the package statement, if necessary.
  4. Save the file and compile it. Compiling the file enables you to treat it as a bean that you can add to the form by dragging and dropping from within the IDE's GUI builder.
  5. Drag the class from the Projects window and drop it in white area surrounding the form, as shown in the screenshot below.
    masterdetail5-dragconverter.png

    A node called rowSorterToStringConverter1 should appear in the Inspector window.

    masterdetail5-inspector.png

  6. Select the rowSorterToStringConverter1 node and set its table property to masterTable.

We'll use this converter when we create the binding.

To create the binding:

  1. In the main form, right-click the text field and choose Bind | text.
  2. In the Bind dialog, select masterTable as the binding source and rowSorter as the expression.
    masterdetail5-binding-basic.png
  3. Click the Advanced tab of the dialog box.
  4. From the Converter combo box, select rowSorterToStringConverter1.
    masterdetail5-binding-advanced.png
  5. Click OK to close the dialog and generate the binding code.

Now when you run the application, you should be able to type in the Search Filter field and see that the list of rows is reduced to only rows that contain text matching what you have typed.

masterdetail5-filter-working.png

Note: If you have been following this whole series of posts, you will need to make a few changes to get the New Record and Edit Record buttons to work correctly. The Edit Record button doesn't work correctly because the number of the record to display is determined according to the records displayed but applied to the whole list of records in the database. In other words, if you select the first record in a filtered list, the first record of the whole database appears for editing in the dialog.

masterdetail5-synchproblem.png

The New Record button fails with an exception because the code to select the new row is determined according to number of records in the database table, not according to the number of records currently displayed in the table.

To fix the first problem, replace the line:

ec.setCurrentRecord(list.get(masterTable.getSelectedRow()));

with:

ec.setCurrentRecord(list.get(masterTable.convertRowIndexToModel(masterTable.getSelectedRow())));

To fix the second problem, replace the line:

int row = list.size() - 1;

with:

int row = masterTable.getRowCount() - 1;
Related Topics >>

Comments

You could consider using the java.util.regex.Pattern.CASE_INSENSITIVE flag to switch regex modes.

thank you for interesting posts. i wonder if i could combine filters so that i could for example show - in addition with pattern filtering - only rows where phone is missing. ( i put a jcheckbox and if it is selected then show all rows, else show only those rows with missing phone ).
have a nice time in Prague. i'm sure you already tasted legendary Czech bier! :-) thanks again