Expandable Table Rows in JavaFX

Not so fully featured 

WPF tables offer a nice feature for showing a row "details" that is not supported in Java (Swing or FX). Someone recently asked me if it was possible to implement something similar in Java FX. After a lot of Google searches I realized there is nothing out there in the community to accomplish anything even remotely like this. You know what that means right? I HAD to do it. Uncharted territory is so much fun!

Included Files

package: codemonkeycorner.expandabletablerows

IExpandableTableRow - we need some extra properties to provide this functionality
AbstractExpandableTableRow - Our new Row (You have to provide the expanded content)
ExpandableTableRowSkin - How we actually layout the row / expanded content

package: codemonkeycorner.expandabletablerows.example

Person - example data
PersonRow (extends AbstractExpandableTableRow) - provides content for the details view
TestRunner - Creates an example table using the person data and row

Example in Action .....

Not quite so fast.....

To be honest I thought I had solved this issue really quickly without much hassle, right up until I hit the sort button on a column. Thats when I realized that the rows are reused when you sort, the data associated with the rows changes. This is great for performance but caused a real headache for the expanding rows. That meant I could not keep the expanded state of a row local. There had to be some external keeper (the data itself in my example) to store that data. That also meant every time the item changed I needed to update the layout as well for size and content. It IS all complete now however due this problem I am still not entirely happy with the results. My original solution was a bit more clear cut and had better separation of concerns.

WPF Dependency Properties vs INotifyPropertyChanged

Distinguishing between wants and needs 

If you are doing WPF development you will not get very far before running into this decision. Should you use DependencyObject(and DependencyProperty) or should you implement INotifyPropertyChanged to support binding. This question boils down into two really. When do you "need" to use one or the other, and when might you "want" to choose one over the other.

So when do we need  to use DependencyObject?

<MyControl MyDependencyProperty="{Binding PropertyPath}"  />

The only time you must use a DependencyObject is when you want someone to be able to make it the target of a binding expression. Typically (though not always) this is only the case if you have created a custom control with properties that you want to be bindable rather than as a view model. This is the one case where you cannot use INotifyPropertyChanged.

So when do we need  to use INotifyPropertyChanged?

  • If your object/property needs to implement a custom Equals/Hashcode.
DependencyObject seals both Equals and GetHashCode methods, so if you need to override you must use INotifyPropertyChanged.

  • If your object/property needs to be Serializable
DependencyObject is not marked as Serializable. DependencyProperty fields do not serialize correctly even if you mark them as Serializable. DependencyProperty values are not stored local to the object (if they are stored at all).

When might I want  to use one over the other?

Performance (memory and speed) - DependencyObject wins

  • Binding works faster with DependencyObject rather than an object that implements INotifyPropertyChanged.

  • DependencyObject uses less memory than a CLR(Common Language Runtime) object.
This can be a significant difference if objects are commonly using the default values. DependencyObject uses a static field for the default, and only creates storage space for the DependencyProperty if a value is set. This is the reason a DependencyObject is not serializable. 

However ....
It should be noted that in most cases you would never notice the difference. Only in cases with a very large property set, or with more real-time operations would you need the performance difference.

Multi-threaded - CLRs win

  • If you are using a background thread to create your objects you might want to use CLRs instead. 
DependencyObjects have thread affinity (they can only be accessed from the thread they were created on). Binding to a DependencyObject that you have created on a background thread will cause an exception. 

However ....
You could dispatch your object creation to the UI thread. Keep in mind though if you are constantly having to bounce thread calls to create your objects and set values then you may be losing much of the benefit of using your background thread for processing.

Control over notification - CLRs win

  • You get to decide when to send notification of changes. 
This can be useful if for instance you wanted to batch multiple changes into one UI operation.

However ....
You do get other very useful mechanisms built into the DependencyObject/DependencyProperty system. Validate and Coerce callbacks are very useful in certain situations.

In conclusion....

There is no best to use for all cases. Instead you should pick the one that suits your needs. Hopefully this post has helped you to determine which one that will be!