Wednesday, November 25, 2009

Off to a new topic: JavaFX Script and bidirectional bindings

I did try JavaFX/F3 two years ago and what I liked at that time was the possibility to bind variables so that updates could be propagated back and forth (bidircetional) to GUIs without having to implement a lot of listeners and call functions. However, this does not seem to work as nicely anymore (Netbeans with JavaFX kit 1.7). Only unidirectional bindings seem to work. That means that developers still need to create function to handle updates. More on the subject of bidirectional bindings can be found here and here.

(Though, I suggested on the mailing list once that JavaFX should have different levels of access to variables in order to provide encapsulation that otherwise was missing, and that was actually added! I don't know if it was on my suggestion or from somebody else since nobody commented on my email, but I gladly accept the honor of introducing that mechanism to JavaFX :-). So everything is not that bad with JavaFX...).

Nevertheless, the binding does not work as it did. Although, there is a rather undocumented keyword "inverse" that in certain circumstances can be used to create a bidirectional binding. But, in order to get around the limitations of "inverse", I have come up with the solution shown below, that at least, provides a nice separation between the bidirectional updating from GUI to model and back again. The idea is to use a binding class that mediates the updates between a "StackEntry" and the GUI using the "replace" keyword.


import javafx.scene.control.TextBox;
import javafx.stage.Stage;
import javafx.scene.Scene;


class StackEntry {
var title: String = "first" ;
var notes: String = "";
}

class StackEntryBinding {
var entry:StackEntry = null on replace {
title = entry.title;
notes = entry.notes;
};

var title: String = entry.title on replace {
println("new title={title}");
entry.title = title;
};
var notes: String = entry.notes on replace {
println("new notes={notes}");
entry.notes = notes;
};


}

var entry = StackEntry {};
var currentEntry:StackEntryBinding = StackEntryBinding {
entry: bind entry;
};

def foo = currentEntry;

def input = TextBox {
text: bind foo.title with inverse;
}

println("textBox={input.text}");

currentEntry.title = "second";

println("textBox={input.text}");

foo.title = "third";

println("currentEntry.entry.title={currentEntry.entry.title}");

input.text="fourth";

println("foo.title={foo.title}");

entry = StackEntry {};

println("currentEntry.entry.title={currentEntry.entry.title}");
println("textBox={input.text}");

Stage {
scene:Scene {
content: input
}

}

// The resulting output looks like this:

new title=first
new notes=
textBox=first
new title=second
textBox=second
new title=third
currentEntry.entry.title=third
new title=fourth
foo.title=fourth
new title=first
currentEntry.entry.title=first
textBox=first

No comments: