Introducing the ControlsFX TableFilter

Introducing the ControlsFX TableFilter

This is a guest post by Thomas Nield, a contributor to ControlsFX. Enjoy 🙂


Last year I spent a decent amount of time contributing a TableFilter control to the ControlsFX project. It was inspired by Eugene Ryzhikov’s SwingBits project which contained a table filtering control for Swing. After recently making some enhancements, it seemed like a good time to share the TableFilter and what it can do. I hope it enriches your UI much like the rest of the ControlsFX project has done for me.

The TableFilter behaves very similarly to Excel's spreadsheet filter. Right-clicking on a column header will make it visible, and a searchable checklist of distinct values for that column allows you to quickly filter for data.

Here is how it works:

Step 1: Have a TableView with a TableFilter applied

TableView<Person> tableView = ...
//add TableColumns
TableFilter filter = new TableFilter(myTableView);

Step 2: Right click the header for the column you want to filter on

Step 3: Either search for items you want to filter on or uncheck values you want to filter out

Step 4: Click APPLY to execute the filter

You can always remove the filter by right-clicking any column and clicking RESET to clear the filter for that particular column, or RESET ALL to remove all filters for all columns.

How it Works

This filter control is highly intuitive to business users. From my experience, many expect it especially if they have worked with Excel. But for something that is so intuitive to use, there are a surprising number of complicated moving parts the developer has to account for:

  • A distinct set of values must be maintained for each column
  • Values that are selected/unselected must be tracked
  • If data is added or removed, the distinct values must be rebuilt while persisting the selected/unselected values
  • Each column can have a separate filter applied, and only mutually inclusive items that qualify with all filters can be displayed
  • Users may want distinct values to be grayed out if they are no longer visible due to a recent filter in another column
  • There may be a need to customize search "strategies" for distinct values, such as wildcards, regular expressions, date and number comparisons, etc.

The TableFilter control does all the above, and after a lot of work I have used it in production for about six months. My clients have been extremely satisfied with it and it has worked reliably.

The TableFilter will swap out the ObservableList of items with a FilteredList. This FilteredList will only display items that qualify on all column's respective filters. This means you can execute filters on more than one column in combination.

For instance, we have already filtered to records where the last name is "Warren". We can add another filter where the age is 16. Note that when I right-click the "Age" column, values that are no longer visible due to the "Last Name" filter are grayed out. You can use the "NONE" button to unselect all values and pick the value(s) you are interested in.

STEP 1: Right-Click "Age" and apply a second filter

STEP 2: Hit "Apply" to execute the second filter

You can remove all filters from all columns by right-clicking any column and clicking "RESET ALL".

Custom Search Strategies

One simple but highly flexible configuration you can do with the TableFilter is change the search behavior on the search box. By default, the search box will filter distinct values based on the inputted String, and pass it to the contains() method of each distinct value's toString(). Effectively, whatever input you type in the search box will match distinct values that contain it.

You can change this behavior easily by passing a BiPredicate<String,String> lambda, where the first String parameter is the "input" from the search box, and the second String value is the "target" representing each distinct value. Using these two String inputs, you can do any matching logic that returns a boolean.

For example, you can implement the search box behavior to use regular expressions for the table filters.

TableFilter<Person> tableFilter = new TableFilter<>(table);

tableFilter.setSearchStrategy((input,target) -> {
    try {
        return target.matches(input);
    } catch (Exception e) {
        return false;
    }
});

Now I can use regular expressions to search through the distinct values.

Note I had to use the try-catch because event-driven text inputs will likely contain broken regular expressions, especially as they are being typed. When the typed input is currently not a valid regular expression, I just default the qualification to false.

Not that all values for all columns are searched by their toString() values. This makes sense since you are searching by typing in String inputs, so the distinct values should be compared to the input as String values as well. You can do other behaviors, like using startsWith() instead of contains() or even a wildcard pattern system.

Currently, the API is set up to use one search strategy for all column filters. I may explore column-specific search behaviors later but for now I feel it would complicate the API. But with a little effort, you can create more complex search strategies that recognize numbers, dates, and even simple math expressions. For example, you could evaluate if the two String inputs are numbers. If the numeric input starts with a "less than" symbol, as in "<120", you would then only qualify distinct values that are less than 120. This sounds involved but I have found it is not much work, and it just requires a few nested case statements. I have even developed implementations that can work with date strings of various formats.

Updating Data in the TableView

Because the TableFilter commandeers the TableView's backing ObservableList and replaces it with a FilteredList, you will get errors when trying to modify the backing ObservableList of items returned from getItems().

To modify the "backing list" of items for a TableView, call the getBackingList() method on the TableFilter and modify that instead to update the TableView.

TableFilter<Person> tableFilter = new TableFilter<>(table);
ObservableList<Person> items = tableFilter.getBackingList();

Conclusions

I hope you find the TableFilter useful. It is currently available in the ControlsFX project but an improved version (which includes the custom search strategy and fixes for nullability issues) is available in the coming release. Please let us know what you think and do not hesitate to get involved if you have ideas or improvements.

JavaFX links of the week, March 7

Some nice links this week – enjoy! 🙂

JavaFX links of the week, February 29

Welcome to the leap-day edition of JavaFX links of the week! 🙂

JavaFX links of the week, February 23

A day late this week – I was out of action yesterday (and over the weekend) with a head cold. Now that I’m getting back on my feet, here are the links from the past week – enjoy!

JavaFX links of the week, February 15

A lighter week this week, with only a few links that I could find. Nonetheless – enjoy 🙂

JavaFX links of the week, February 8

Another public holiday, so another slightly delayed links post. Lets get the links posted so I can get back to a day off 🙂

JavaFX links of the week, February 1

I know I say this often – but we’re already a month down into 2016. Where does time go!?! Well, obviously a lot of it goes into what people are doing in the links below, so we really owe them all a debt of gratitude and a little of our time to read what they’re up to. Enjoy 🙂

JavaFX links of the week, January 25

Half a day late, but it was a public holiday today, so I was out enjoying the sun! 🙂 On with the links…

JavaFX links of the week, January 18

JavaFX links of the week, January 11

January 11, 2016 – the day before my birthday! 🙂 I hope everyone had a great festive break and took the opportunity to recharge their batteries. I know I did! However, because I was away from the web (and literally without access for a number of days), I’ve certainly failed to capture all links over the last few weeks. If I missed anything ping me to let me know, and I’ll include it next week. Let’s get on with the links – enjoy 🙂

  • One of the big things to happen over the holidays was the announcement that Android is moving away from Apache Harmony and will be switching to OpenJDK. Many posts about this topic managed to get a lot of the concepts muddled. The Gluon blog has a summary that appears fairly accurate, given the fact that a lot remains unknown about the likely outcomes of this change.
  • Speaking of Gluon, there is an interview on their website with Neil Matatall about Brakeman Pro 1.0 being released. Interestingly it is a commercial JavaFX application built with JRuby.
  • I did a blog post about node picking in JavaFX. This is a topic I see asked about very frequently, and I had just recently written some code to do this. Hopefully it is helpful for someone else – although hopefully most people will have managed to write it themselves – it isn’t very complex! 🙂
  • Voxxed posted a short video with Johan Vos from Gluon about ‘Reasons to Get Excited About Java on Mobile‘.
  • Dirk Lemmermann has posted about the power of CSS in JavaFX. He has also created a YouTube playlist of JavaFX applications in the ‘real world’. It’s great to see some JavaFX applications coming out from behind the curtains – I see some really great stuff in my travels, and it is a shame that not more of it is shown publicly. These kinds of efforts are great!
  • Gerrit Grunwald has been busy working on (yet another!) gauge library, this time called Medusa. As always they look excellent, so take a look if you are wanting to put some gauges in your applications.
  • Jens Deters has released FontAwesomeFX 8.8, which fixes a few issues and adds a number of weather icons to the mix.
  • Speaking of icon packs, Andres Almiray has also released an API for using them, which he calls Ikonli.
  • Recently I was notified of TuioFX by a few of the developers. This is a project that ‘simplifies the development of cross-platform applications for multi-touch, multi-user interactive tabletops and surfaces.’
  • Robert Ladstätter has posted about fx-tictactoe – A TicTacToe application built using JavaFX and Scala.
  • Carl has posted about ‘Ground Floor or Ground Zero? Why I’m Bullish on JavaFX in 2016‘.