Discussion:
Subclassing behavior in JavaFX Controls
Pete Moss
2014-09-17 18:36:48 UTC
Permalink
I am starting to work with the TableView JavaFX Control. I see that it has
some nominal keyboard handling, but it is incomplete for my needs. From
what I understand about the JavaFX Control architecture, all of the JavaFX
Control-derived classes use a Skin class created via
Control.createDefaultSkin() which, in turn, contains a behavior class that
is derived from BehaviorSkinBase.

However, an Oracle publication says that Control developers should NOT use
BehaviorSkinBase since that is a private API.

So here is the problem. I am trying to augment the behavior of the
TableView class. The keyboard handling is done in TableViewBehavior so it
would be nice for me to supply a subclassed version of TableViewBehavior to
augment that behavior or override some of it. But I can't. If you look at
the TableViewSkin class, here is the ctor that gets used:

public TableViewSkin(final TableView<T> tableView) {
super(tableView, new TableViewBehavior<T>(tableView));

...
}

Because this is the only ctor available for TableViewSkin, I don't see any
way I can subclass TableViewSkin and supply my own behavior instance. I
can, of course, create my own skin, but then I would lose the code in
TableViewSkin (unless I shamelessly copied it, which I don't want to do).

It seems to me there could be a couple of different ways to fix this:
1) Add an alternative ctor that takes a behavior instance as a second
argument. Then refactor the original ctor to call this, eg,
public TableViewSkin(final TableView<T> tableView, BehaviorSkinBase
behavior) {
super(tableView, behavior);

...
}

public TableViewSkin(final TableView<T> tableView) {
this(tableView, new TableViewBehavior<T>(tableView));
}

2) Add a factory method for the behavior instance:
public TableViewSkin(final TableView<T> tableView) {
BehaviorSkinBase behavior = behaviorFactory(tableView);

super(tableView, behavior);

...
}

protected BehaviorSkinBase behaviorFactory(TableView<T> tableView)
{
return new TableViewBehavior<T>(tableView);
}

I see this as a shortcoming in the JavaFX architecture. If I were building
a custom Control, it would not be so bad since I would provide an
additional Skin ctor that takes a behavior class as an arg, but it seems
that the way the code is currently structured, it is difficult to subclass
a JavaFX Control and subclass its skin and behavior too.

Any thoughts on how to workaround/solve this would be greatly appreciated.
Tomas Mikula
2014-09-17 19:02:35 UTC
Permalink
Hi Pete,

work on this is tracked in https://javafx-jira.kenai.com/browse/RT-21598

In terms of the current API, I think the best solution is to add event
handlers and/or event filters on the TableView.

Regarding your proposed fixes, I have a blog post where I argue that
the wiring between the Behavior and the ("View" aspect of the) Skin
should be reversed, i.e. that the Behavior should hold a reference to
the View, not vice versa. That is, for example, against both your
proposed fixes. (Note that the blog post represents my personal view
only.) http://tomasmikula.github.io/blog/2014/06/11/separation-of-view-and-controller-in-javafx-controls.html

Incidendally, I'm currently working on a solution to add/override
keyboard shortcuts. I should be able to publish something soon.

Best,
Tomas
Post by Pete Moss
I am starting to work with the TableView JavaFX Control. I see that it has
some nominal keyboard handling, but it is incomplete for my needs. From
what I understand about the JavaFX Control architecture, all of the JavaFX
Control-derived classes use a Skin class created via
Control.createDefaultSkin() which, in turn, contains a behavior class that
is derived from BehaviorSkinBase.
However, an Oracle publication says that Control developers should NOT use
BehaviorSkinBase since that is a private API.
So here is the problem. I am trying to augment the behavior of the
TableView class. The keyboard handling is done in TableViewBehavior so it
would be nice for me to supply a subclassed version of TableViewBehavior to
augment that behavior or override some of it. But I can't. If you look at
public TableViewSkin(final TableView<T> tableView) {
super(tableView, new TableViewBehavior<T>(tableView));
...
}
Because this is the only ctor available for TableViewSkin, I don't see any
way I can subclass TableViewSkin and supply my own behavior instance. I
can, of course, create my own skin, but then I would lose the code in
TableViewSkin (unless I shamelessly copied it, which I don't want to do).
1) Add an alternative ctor that takes a behavior instance as a second
argument. Then refactor the original ctor to call this, eg,
public TableViewSkin(final TableView<T> tableView, BehaviorSkinBase
behavior) {
super(tableView, behavior);
...
}
public TableViewSkin(final TableView<T> tableView) {
this(tableView, new TableViewBehavior<T>(tableView));
}
public TableViewSkin(final TableView<T> tableView) {
BehaviorSkinBase behavior = behaviorFactory(tableView);
super(tableView, behavior);
...
}
protected BehaviorSkinBase behaviorFactory(TableView<T> tableView)
{
return new TableViewBehavior<T>(tableView);
}
I see this as a shortcoming in the JavaFX architecture. If I were building
a custom Control, it would not be so bad since I would provide an
additional Skin ctor that takes a behavior class as an arg, but it seems
that the way the code is currently structured, it is difficult to subclass
a JavaFX Control and subclass its skin and behavior too.
Any thoughts on how to workaround/solve this would be greatly appreciated.
Pete Moss
2014-09-17 19:35:49 UTC
Permalink
Tomas,

Thanks for the fast reply.

I read your blog post that you referenced and I agree with you 100%. I am a
crusty MVC person and I agree, that having the Skin class (View) hold a
reference to the Behavior class (Controller) rubs me the wrong way from an
MVC perspective.

I was looking at the JavaFX source code and trying to figure out the
easiest source code path to a fix. But I agree with you that a slight
redesign would be better to bring it closer to a purer MVC approach (to
loosely quote General Jack D. Ripper, we must strive for a Purity Of
Essence). ;)

Doing what you propose would provide a much greater degree of flexibility
to subclass the skin, behavior or both.

I read the back and forth between you and Jonathan Giles on your blog post,
and although I agree with you that a refactoring and restructuring would be
better, I understand Jonathan's point of view, given that the horse is out
of the barn with the current APIs and what you are proposing is a little
more invasive.

Which brings me back to my post and my desires to subclass skin, behavior
or both for existing JavaFX Controls. It seems impossible to subclass skin
and behavior. Replace, yes, but not subclass (augment).

Again, any suggestions for workarounds would be appreciated.
Post by Tomas Mikula
Hi Pete,
work on this is tracked in https://javafx-jira.kenai.com/browse/RT-21598
In terms of the current API, I think the best solution is to add event
handlers and/or event filters on the TableView.
Regarding your proposed fixes, I have a blog post where I argue that
the wiring between the Behavior and the ("View" aspect of the) Skin
should be reversed, i.e. that the Behavior should hold a reference to
the View, not vice versa. That is, for example, against both your
proposed fixes. (Note that the blog post represents my personal view
only.)
http://tomasmikula.github.io/blog/2014/06/11/separation-of-view-and-controller-in-javafx-controls.html
Incidendally, I'm currently working on a solution to add/override
keyboard shortcuts. I should be able to publish something soon.
Best,
Tomas
Post by Pete Moss
I am starting to work with the TableView JavaFX Control. I see that it
has
Post by Pete Moss
some nominal keyboard handling, but it is incomplete for my needs. From
what I understand about the JavaFX Control architecture, all of the
JavaFX
Post by Pete Moss
Control-derived classes use a Skin class created via
Control.createDefaultSkin() which, in turn, contains a behavior class
that
Post by Pete Moss
is derived from BehaviorSkinBase.
However, an Oracle publication says that Control developers should NOT
use
Post by Pete Moss
BehaviorSkinBase since that is a private API.
So here is the problem. I am trying to augment the behavior of the
TableView class. The keyboard handling is done in TableViewBehavior so it
would be nice for me to supply a subclassed version of TableViewBehavior
to
Post by Pete Moss
augment that behavior or override some of it. But I can't. If you look at
public TableViewSkin(final TableView<T> tableView) {
super(tableView, new TableViewBehavior<T>(tableView));
...
}
Because this is the only ctor available for TableViewSkin, I don't see
any
Post by Pete Moss
way I can subclass TableViewSkin and supply my own behavior instance. I
can, of course, create my own skin, but then I would lose the code in
TableViewSkin (unless I shamelessly copied it, which I don't want to do).
1) Add an alternative ctor that takes a behavior instance as a second
argument. Then refactor the original ctor to call this, eg,
public TableViewSkin(final TableView<T> tableView, BehaviorSkinBase
behavior) {
super(tableView, behavior);
...
}
public TableViewSkin(final TableView<T> tableView) {
this(tableView, new TableViewBehavior<T>(tableView));
}
public TableViewSkin(final TableView<T> tableView) {
BehaviorSkinBase behavior = behaviorFactory(tableView);
super(tableView, behavior);
...
}
protected BehaviorSkinBase behaviorFactory(TableView<T> tableView)
{
return new TableViewBehavior<T>(tableView);
}
I see this as a shortcoming in the JavaFX architecture. If I were
building
Post by Pete Moss
a custom Control, it would not be so bad since I would provide an
additional Skin ctor that takes a behavior class as an arg, but it seems
that the way the code is currently structured, it is difficult to
subclass
Post by Pete Moss
a JavaFX Control and subclass its skin and behavior too.
Any thoughts on how to workaround/solve this would be greatly
appreciated.
Tomas Mikula
2014-09-17 19:57:55 UTC
Permalink
Hi Pete,
Post by Pete Moss
Tomas,
Thanks for the fast reply.
I read your blog post that you referenced and I agree with you 100%. I am a
crusty MVC person and I agree, that having the Skin class (View) hold a
reference to the Behavior class (Controller) rubs me the wrong way from an
MVC perspective.
I was looking at the JavaFX source code and trying to figure out the easiest
source code path to a fix. But I agree with you that a slight redesign would
be better to bring it closer to a purer MVC approach (to loosely quote
General Jack D. Ripper, we must strive for a Purity Of Essence). ;)
Doing what you propose would provide a much greater degree of flexibility to
subclass the skin, behavior or both.
I read the back and forth between you and Jonathan Giles on your blog post,
and although I agree with you that a refactoring and restructuring would be
better, I understand Jonathan's point of view, given that the horse is out
of the barn with the current APIs and what you are proposing is a little
more invasive.
The only thing that is out is SkinBase, which is an encapsulation of
both the View and the Controller. Whether and how you separate the
view and the controller within your instance of SkinBase is left
completely up to you, as far as the current public API goes. This
means it is not too late for a change. It is not even invasive:
current controls can keep using the private BehaviorSkinBase, or be
moved to the new solution gradually.
Post by Pete Moss
Which brings me back to my post and my desires to subclass skin, behavior or
both for existing JavaFX Controls. It seems impossible to subclass skin and
behavior. Replace, yes, but not subclass (augment).
I believe that overriding keyboard shortcuts should be doable without
subclassing the skin (or behavior). I'm working on something right now
and hope to publish something soon.

Best,
Tomas
Post by Pete Moss
Again, any suggestions for workarounds would be appreciated.
Post by Tomas Mikula
Hi Pete,
work on this is tracked in https://javafx-jira.kenai.com/browse/RT-21598
In terms of the current API, I think the best solution is to add event
handlers and/or event filters on the TableView.
Regarding your proposed fixes, I have a blog post where I argue that
the wiring between the Behavior and the ("View" aspect of the) Skin
should be reversed, i.e. that the Behavior should hold a reference to
the View, not vice versa. That is, for example, against both your
proposed fixes. (Note that the blog post represents my personal view
only.)
http://tomasmikula.github.io/blog/2014/06/11/separation-of-view-and-controller-in-javafx-controls.html
Incidendally, I'm currently working on a solution to add/override
keyboard shortcuts. I should be able to publish something soon.
Best,
Tomas
Post by Pete Moss
I am starting to work with the TableView JavaFX Control. I see that it has
some nominal keyboard handling, but it is incomplete for my needs. From
what I understand about the JavaFX Control architecture, all of the JavaFX
Control-derived classes use a Skin class created via
Control.createDefaultSkin() which, in turn, contains a behavior class that
is derived from BehaviorSkinBase.
However, an Oracle publication says that Control developers should NOT use
BehaviorSkinBase since that is a private API.
So here is the problem. I am trying to augment the behavior of the
TableView class. The keyboard handling is done in TableViewBehavior so it
would be nice for me to supply a subclassed version of TableViewBehavior to
augment that behavior or override some of it. But I can't. If you look at
public TableViewSkin(final TableView<T> tableView) {
super(tableView, new TableViewBehavior<T>(tableView));
...
}
Because this is the only ctor available for TableViewSkin, I don't see any
way I can subclass TableViewSkin and supply my own behavior instance. I
can, of course, create my own skin, but then I would lose the code in
TableViewSkin (unless I shamelessly copied it, which I don't want to do).
1) Add an alternative ctor that takes a behavior instance as a second
argument. Then refactor the original ctor to call this, eg,
public TableViewSkin(final TableView<T> tableView, BehaviorSkinBase
behavior) {
super(tableView, behavior);
...
}
public TableViewSkin(final TableView<T> tableView) {
this(tableView, new TableViewBehavior<T>(tableView));
}
public TableViewSkin(final TableView<T> tableView) {
BehaviorSkinBase behavior = behaviorFactory(tableView);
super(tableView, behavior);
...
}
protected BehaviorSkinBase behaviorFactory(TableView<T> tableView)
{
return new TableViewBehavior<T>(tableView);
}
I see this as a shortcoming in the JavaFX architecture. If I were building
a custom Control, it would not be so bad since I would provide an
additional Skin ctor that takes a behavior class as an arg, but it seems
that the way the code is currently structured, it is difficult to subclass
a JavaFX Control and subclass its skin and behavior too.
Any thoughts on how to workaround/solve this would be greatly appreciated.
Tomas Mikula
2014-09-25 17:38:00 UTC
Permalink
Hi Pete,
Post by Tomas Mikula
I believe that overriding keyboard shortcuts should be doable without
subclassing the skin (or behavior). I'm working on something right now
and hope to publish something soon.
have a look at https://github.com/TomasMikula/WellBehavedFX. Does it
satisfy your keyboard handling needs?

Regards,
Tomas

Loading...