In previous posts I've demonstrated binding to an attribute, and binding to a sequence. Now I'd like to show you how to bind to a function. This is a very powerful feature because whenever the value of the function changes, the bound variable is updated. Here's a screenshot of today's example program, in which the user selects the diameter of a circle from a slider widget. The program calculates and displays the area of the circle, as well as drawing a circle with the selected diameter in pixels.
Here's the JavaFX Script code for this example:
/*
* BindToFunctionExample.fx - A compiled JavaFX program that demonstrates
* binding to a function.
*
* Developed 2008 by James L. Weaver (jim.weaver at lat-inc.com)
* to serve as a JavaFX Script example.
*/
import javafx.ui.*;
import javafx.ui.canvas.*;
import java.lang.Math;
import java.lang.System;
class CircleModel {
attribute diameter:Number;
function getArea():Number {
Math.PI * Math.pow(diameter / 2, 2);
}
}
Frame {
var cModel = CircleModel {}
width: 480
height: 560
title: "Bind to Function Example"
background: Color.WHITE
content:
BorderPanel {
center:
Canvas {
content: [
Circle {
cx: 240
cy: 250
radius: bind cModel.diameter * 2
stroke: Color.PURPLE
strokeWidth: 1
fill: Color.CYAN
},
Text {
font:
Font {
face: FontFace.SANSSERIF
style: FontStyle.BOLD
size: 24
}
x: 20
y: 10
stroke: Color.RED
fill: Color.RED
content: bind "Diameter: {cModel.diameter}"
},
Text {
font:
Font {
face: FontFace.SANSSERIF
style: FontStyle.BOLD
size: 24
}
x: 240
y: 10
stroke: Color.RED
fill: Color.RED
content: bind "Area: {%3.2f cModel.getArea()}"
}
]
}
bottom:
Slider {
min: 0
max: 100
border:
TitledBorder {
title: "Diameter:"
}
value: bind cModel.diameter with inverse
minorTickSpacing: 5
majorTickSpacing: 10
paintTicks: true
paintLabels: true
labels: [
SliderLabel {
value: 0
label:
SimpleLabel {
text: "0"
}
},
SliderLabel {
value: 50
label:
SimpleLabel {
text: "50"
}
},
SliderLabel {
value: 100
label:
SimpleLabel {
text: "100"
}
}
]
}
}
visible: true
onClose:
function():Void {
System.exit(0);
}
}
Binding to a Function
As shown above, the slider is bi-directionally bound to the diameter attribute of the CircleModel instance. This attribute affects the value of the getArea() function, so as the value of the diameter attribute changes, the content attribute bound to the getArea() function will reflect its value.
Formatting Output
Another thing that you'll notice in this example is that the value of the getArea() function is formatted to two decimal places when displayed in the Text graphical node. This is due to the formatting capabilities of JavaFX Script that I began discussing in the Happy New Year post.
JavaFX Script Boot Camp Registration Now Open!
I will be offering a 2.5 day JavaFX Script day "boot camp" on Wednesday, April 9 through Friday, April 11, 2008 (ending at noon) in Indianapolis, Indiana. This course is designed to get you quickly up to speed in JavaFX Script application development. A primary reference for this course is my JavaFX Script book, but the course has its own syllabus which includes material covered in the book as well as up to the minute developments in compiled JavaFX Script. Registration is now open, and I am accepting 12 students in this pilot class. The cost of this pilot class will be 900 USD, and additional people from the same company will be 600 USD. You'll need to bring your laptop computer with the latest versions of the JavaFX Script downloads (which I'll specify in more detail as the class date approaches). I'm looking forward to teaching this class and hope that you can attend!
Regards,
Jim Weaver
JavaFX Script: Dynamic Java Scripting for Rich Internet/Client-side Applications
Immediate eBook (PDF) download available at the book's Apress site
These are good questions, t_e_r and EP! I will work up some example programs for your scenarios and post them.
Posted by: Jim Weaver | February 19, 2008 at 04:41 PM
Jim,
Thank you for your excellent examples. This one raises a few questions:
1) Sometimes you need to be able to update a variable using more than one control. For example, let's say I add a text field that is bound to the diameter. As the slider moves, the diameter changes, and the textfield updates. But what would the pattern be so that I can also enter the diameter into the text field and have the model update?
2) What if I only have a textfield and no slider, how does "bind with inverse" work between different types like double and string?
TextField{
columns: 5
value: bind "{cModel.diameter}" //with inverse
},
Button{
text: "Update"
action: function(){
// cModel.diameter = ???
}
}
Posted by: EP | February 19, 2008 at 04:24 PM
I am one of your earliest readers - I bought your ebook back in Oct. I have also been studying your blog assiduously over the last few days, trying to decide whether to switch to compiled javafx. I very much appreciate your being such a steady font of good information, when so much is in flux.
I struggled with the interpreted update(replace) trigger and bind. They seemed inconsistent, and not of a generality I would expect. You could bind to a function, but only if its argument(s) changed, thus making it not possible to bind to a function with no arguments. And even with arguments, there are qualifications as to whose function it is.
Since a function may depend on an arbitrary number of other attributes and other functions, including a java method, is it now possible to bind to a java method? (This is very much needed if you want to drive the actions from the data(streamed) side sometimes.) And is binding recursive?
Posted by: t_e_r | February 16, 2008 at 02:02 AM