A common mistake that I do over and over again is that I put certain code into the skin class of a custom control while it should have been inside the control class itself.

I just noticed this again when going through the list of bugs filed for the PopOver control of ControlsFX. I contributed this control, hence I feel responsible for fixing those bugs (when time permits). One of the issues mentioned that it is hard or impossible to change the styling of the PopOver control. After some investigation I realised that I am adding a stylesheet to the root pane of the PopOver inside the skin class. This is working but it causes a problem when applications have already added their stylesheets when the PopOver is created. The PopOver stylesheet will then override the application’s stylesheet and only because it is added too late.

The only way to actually style a PopOver is to listen to the skin property and wait for the skin to be set. Once set a stylesheet can be added to its children. Not a very elegant solution.

In case of the PopOver control I do have a very good excuse for adding the stylesheet inside the skin class. PopOver extends PopupControl and you can not add a stylesheet to it. PopupControl and its superclass PopupWindow inherit their styling from the owning window. However, what I could have done is to create a root pane inside the PopOver control, add a stylesheet to it and make it accessible via a getter method. Then the skin could have retrieved the root pane from the control and do whatever it needs to do with it so that it becomes a PopOver.

To summarize: do not add anything to your control inside the skin of the control if you want applications to be able to change it because the skin will most likely be set after applications have configured the control, hence overriding it. Typical examples are:

  • styling: you can set style classes but do not add stylesheets
  • events: any event handler you “set” in the skin will clear all previously handlers of the same type, e.g. control.setOnMouseClicked(…) will remove all mouse click event handlers that were “added” by the application via control.addEventHandler(MouseEvent.MOUSE_CLICKED, …)