Planet JFX
Register
Advertisement

Introduction to Binding in JavaFX[]

JavaFX lets you bind, or link, attributes so that when one attribute changes, all attributes bound to it will automatically change as well. That sounds rather abstract, but it’s not difficult at all. Let’s start with a simple program to demonstrate binding. As we develop the program, we’ll also give some advice on programming practices. The program displays a frame with a Label and two Buttons. It looks like this...

Bind tutorial bind1

...and here’s the code

import javafx.ui.*;
        
Frame {
    title: "Bind Example 1"
    width: 300
    height: 75
    content:
        FlowPanel {
            content: [
                Label {
                    text: "0"
                },
                Button {
                    text: "Add 1"
                },
                Button {
                    text: "Subtract 1"
                }
            ]
        }
    visible: true
}

The object of this program is to activate the buttons to add or subtract 1 from the number shown in the label. One approach to solving this problem in a procedure-oriented language might use this pseudo-code:

  • “Listen” for an action on the button
  • Extract the text content from the label
  • Convert to an integer
  • Add (or subtract) one
  • Convert the value to a string and put that back into the label

Model/View/Controller[]

The problem with this approach is that it treats the number as an integral part of the label. That’s not how we think of it, though. In our minds, there’s an abstract counter somewhere (our model), and the label is a way to view its value. The buttons control the counter, and any change to its value is reflected in the view through the label.

This is a much more flexible approach to the problem. Instead of a Label, we could view the counter as a Slider or Spinner or even as some custom GUI component.

JavaFX is perfectly suited to the Model/View/Controller architecture, because it lets you bind an attribute of the model to the view. As soon as you change the value of an attribute in the model, the view changes automagically; you don’t need to write any code to make it happen.

Construct the Model[]

While it is possible to bind the text of the Label to a simple variable, the preferred method (again, for flexibility and extensibility) is to create a class for the model. Here’s the additional code to create a Counter class and initialize an instance of it. Go ahead and place this code after the import statement but before the Frame:

class Counter {
    attribute value: Integer;
}

attribute Counter.value = 0; // initial value for new instances

var count:Counter = new Counter();

Bind the Model to the View[]

The change to the Label looks like this:

Label {
    text: bind count.value.toString( )
}

We can’t say text: bind count.value because count.value is an Integer, and a Label requires its text attribute to be a String. That’s the bad news. The good news is that this example shows that JavaFX lets you bind an attribute to an expression, not just to a variable.

Add the Controller[]

Finally, add an action to each Button so that it can control the model. (See the entire file.)

Button {
    text: "Add 1"
    action: operation( ) {
        count.value = count.value + 1;
    }
},
Button {
    text: "Subtract 1"
    action: operation( ) {
        count.value = count.value - 1;
    }
}

Improving the Code[]

We wrote the action attributes the way we did in order to get the program up and running quickly, so that you could have a successful experience as soon as possible. While the program certainly works, the code isn’t organized well. Consider that our original problem was to separate the model and the view. Our code is merging model and view by making the Button know how much to increase or decrease the counter’s value. That code properly belongs in the Counter class, which we rewrite as follows:

class Counter {
    attribute value: Integer;
    operation increase( );
    operation decrease( );
}

attribute Counter.value = 0;

operation Counter.increase( ) {
    value++;
}

operation Counter.decrease( ) {
    value--;
}

Go ahead and place this code after the

import javafx.ui.*;
statement but before the
var count:Counter = new Counter(); statement

Each of the buttons is rewritten to call the appropriate operation.

(See the entire file.)

Button {
    text: "Add 1"
    action: operation( ) {
        count.increase( );
    }
},
Button {
    text: "Subtract 1"
    action: operation( ) {
        count.decrease( );
    }
}

Multiple Binding[]

We wrap up this introduction by showing that you can have more than one item bound to a particular atttribute. We will change our example so that, as all good accountants know, negative numbers are displayed in red and non-negative numbers in black. This requires binding the color of the Label to the value of the counter. Here’s the additional code for the Label. (See the entire file.)

Label {
    text: bind count.value.toString( ),
    foreground: bind
        if (count.value >= 0) then
            black:Color
        else
            red:Color                       
    }

Important! That’s an if expression, not an if statement. Use the keywords then and else, and do not use curly braces.

You might wonder why we put this code directly on the Label rather than making it an operation in the Counter class. The reason is that we always want to keep the model and view separate. The counter knows how to add and subtract, but has no idea what color it should be; that’s the job of the Label.


orm of a class) to hold the information that will change, then bind the class’s attributes to the view.

Advertisement