Wednesday Jun 25, 2008
Wednesday Jun 25, 2008
by Patrick Keegan
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.
Now we will add a converter class to the project.
RowSorterToStringConverter.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;
}
}
A node called rowSorterToStringConverter1 should appear in the Inspector window.
rowSorterToStringConverter1 node and set its table property to masterTable.We'll use this converter when we create the binding.
To create the binding:
masterTable as the binding source and rowSorter as the expression.

rowSorterToStringConverter1.

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.
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.
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;