Data Binding de JavaFX, Des liens directs et instantanés entre variables
>> 11 March 2009
Un mécanisme permettant de lier une variable à une autre (ou plusieurs variables), ou une expression, le changement de la variable liée (avec bind), entraine le changement automatique de la variable cible. Très utile pour synchroniser des composants graphiques avec des données.
def x= bind y;
//la variable cible "x", change automatiquement avec les changements de "y"
Le mot clé "bind" associe une variable cible avec une expression et permet de lier :
- Une simple variable à une autre variable de types de base
- Une variable à un objet
- Une variable au résultat d'une expression
- Une variable à la valeur de retour d'une fonction
- Une variable à un bloc d'expressions.
Il y a deux types de liens (bind)
- unidirectionnel avec le mot bind
- bidirectionnel (bind ... with inverse).
Le lien unidirectionnel fonctionne dans un seul sens, modifiant la variable cible.
def x= bind y;//la variable cible est "x"
Le lien bidirectionnel effectue des changements dans les deux sens.
var y=10;
def x bind with inverse y; //"x" change avec des changements de "y" et inversement
Note :
Au moment d'un changement effectué par un lien (bind) un minimum de recalculation est fait
Exemple :
Lier la variable "x" à la variable "y". Remarquez que nous avons utilisé "var x" dans cet exemple pour le comparer avec l'exemple suivant, normalement les variables liées sont déclarées avec "def", pour éviter le changement direct.
var y = 10;
var x = bind y;
println("y = {y} : x = {x}");
y=5;
println("y = {y} : x = {x}");
Exemple :
Le même que le précédent. Quand on essaye de modifier une variable liée, le compilateur affiche des erreurs, même étant déclarée avec "var"
var y = 10;
var x = bind y;
println("y = {y} : x = {x}");
y=5;
println("y = {y} : x = {x}");
x=20;
println("y = {y} : x = {x}");
Exemple :
Deux variable sont liées avec "bind with inverse". Le changement de "y" change la valeur de "x" et inversement.
var y = 10;
var x = bind y with inverse;
println("y = {y} : x = {x}");
y=5;
println("y = {y} : x = {x}");
x=20;
println("y = {y} : x = {x}");
Exemple :
"def" à la place de "var"
/**
* @author Kaesar ALNIJRES
*/
var x = 5;
def y = bind x;
for(i in [0..5])
{
x+=i;
println("x est maintenant ={x} et y = {y}");
}
Lier des variables d'instance à des variables externes
Note :
Quand on utilise bind avec un objet littéral d'une classe on provoque une création d'objet avec chaque changement des variables externes.
La variable d'instance "x" est liée à la variable "i"
La variable d'instance "y" est liée à la variable "i2"
Exemple :
/**
* @author Kaesar ALNIJRES
*/
var i = 5;
var i2 = 4;
class Point
{
var x: Integer;
var y: Integer;
}
def p = bind Point
{
x:i;
y:i2;
}
println("p.x = {p.x} et p.y = {p.y}");
Lier des variables d'instance suite :
Il est aussi possibles de lier des variables d'instance à des variables externes, simplement , en liant chaque variable d'instance à une variable externe (comme pour les variables simples)
Note :
Dans ce cas pas de création de nouveau objet à chaque changement de variables externes
Exemple :
/**
* @author Kaesar ALNIJRES
*/
var i = 5;
var i2 = 4;
class Point
{
var x: Integer;
var y: Integer;
}
def p = Point
{
x: bind i;
y: bind i2;
}
for(n in [0..5])
{
i+=n;
i2+=n;
println("p.x = {p.x} et p.y = {p.y}");
}
Utiliser bind avec des fonctions
Pour une fonction préfixée par "bind" chaque changement de ses paramètres, re-invoque cette fonction avec les nouveaux paramètres.
Exemple :
/**
* @author Kaesar ALNIJRES
*/
function ajouterChiffres(n1:Integer,n2:Integer):Integer {
n1 + n2;
}
var num1 = 5;
var num2 = 4;
def result = bind ajouterChiffres(num1,num2);
println(result);
num1=1;//changement d'un paramètre
println(result);
Exemple :
Le même que le précédent
/**
* @author Kaesar ALNIJRES
*/
function ajouterChiffres(n1:Integer,n2:Integer): Integer{
var r=n1+n2;
println(r);
r;
}
var num1 = 5;
var num2 = 4;
def result = bind ajouterChiffres(num1,num2);
num1=1;//changement d'un paramètre
Utiliser une variable globale dans une fonction avec bind
Le changement d'une variable globale, qui n'est pas passée en paramètre de la fonction n'entraine pas le re-invoquation de la fonction.
Exemple :
/**
* @author Kaesar ALNIJRES
*/
function ajouterChiffres(n1:Integer,n2:Integer): Integer{
var r = (n1 + n2) * facteur;
}
var num1 = 5;
var num2 = 4;
var facteur = 10;
def result = bind ajouterChiffres(num1,num2);
println(result);
num1=1;//changement d'un paramètre
println(result);
facteur=100;
println(result);
Utiliser une fonction avec le mot clé "bound"
Une fonction qui a l'attribut "bound" dans sa déclaration et en combinaison avec "bind" est re-invoquer à chaque changement des ses paramètres ou des variables globales utilisées dans cette fonction. Une fonction "bound" doit obligatoirement retourner une valeur (Void n'est pas autorisée)
Note :
Le corps d'une fonction est un bloc d'expression. Pour une fonction avec "bound" des restrictions existent comme dans un bloc d'expression "bind".
- Dans les expressions avant l'expression finale. Les seuls opérations permises sont des définitions de variables.
- On ne peut pas utiliser l'incrémentation et décrémentation.
- while, insert, delete sont interdits (ces instructions retourne Void et ne sont pas des déclarations)
Exemple :
/**
* @author Kaesar ALNIJRES
*/
bound function ajouterChiffres(n1:Integer,n2:Integer): Integer{
var r = (n1 + n2) * facteur;
}
var num1 = 5;
var num2 = 4;
var facteur = 10;
def result = bind ajouterChiffres(num1,num2);
println(result);
num1=1;//changement d'un paramètre
println(result);
facteur=100;
println(result);
Bind des séquences
A l'instar des variables simples, il est possible de lier des séquences avec le mot clé "bind"
Exemple :
/**
* @author Kaesar ALNIJRES
*/
var seq1 = [1..10];
def seq2 = bind seq1;
println("{for(i in seq1) i}");
println("{for(i in seq2) i}");
insert 20 into seq1;
println("=====================================");
println("{for(i in seq1) i}");
println("{for(i in seq2) i}");
Bind avec for
Il est possible d'utiliser "bind" avec "for" pour combiner deux séquences
Exemple :
/**
* @author Kaesar ALNIJRES
*/
var seq1 = [1..5];
def seq2 = bind for (item in seq1) item*2;
println("{for(i in seq1) "\n{i}"}");
println("{for(i in seq2) "\n{i}"}");
insert 20 into seq1;
println("=====================================");
println("{for(i in seq1) "\n{i}"}");
println("{for(i in seq2) "\n{i}"}");
Lier une variable à une expression
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.text.Text;
import javafx.scene.text.Font;
import javafx.ext.swing.SwingLabel;
/**
* @author kas
*/
class Counter {
var value: Integer;
}
def count=Counter
{
value:0;
}
Stage {
title: "Application title"
width: 250
height: 80
scene: Scene {
content: [
Text {
font: Font {
size: 16
}
x: 10,
y: 30
content: "Application content"
},
SwingLabel {
text: bind count.value.toString();
}
]
}
}
Expression conditionnelles avec bind
Il est possible d'utiliser une clause conditionnelle pour lier une variable à une ou l'autre expression selon le résultat de la condition.
Exemple :
La variable "z" prend la valeur de "x" ou "y" selon la valeur de "x"
var y = 10;
var x = 10;
def z = bind if (x<=0) y else x;
println("y = {y} : x = {x} et z = {z}");
y=5;
println("y = {y} : x = {x} et z = {z}");
x=0;
println("y = {y} : x = {x} et z = {z}");
Utiliser un bloc d'expression bind
Il est possible d'assigner une bloc d'expression avec bind à une variable. La valeur de cette expression est l'expression finale.
Exemple :
/**
*@author : Kaesar ALNIJRES
*/
var y = 10;
var x = 10;
def z = bind {
def a = x;
def b = y;
a+b;
}
println("y = {y} : x = {x} et z = {z}");
y=5;
println("y = {y} : x = {x} et z = {z}");
x=0;
println("y = {y} : x = {x} et z = {z}");
Limitation :
Dans un bloc d'expression avec bind. Il est interdit d'utiliser
- Dans les expressions avant l'expression finale. Les seuls opérations permises sont des définitions de variables.
def a = x;
- Dans un bloc d'expression bind on ne peut pas utiliser l'incrémentation et décrémentation.
var y = 10;
var x = 10;
def z = bind {
def a = x;
def b = y;
a++;//ERREUR
}
- while, insert, delete sont interdits dans le bloc bind (ces instructions retourne Void et ne sont pas des déclarations)
var x = 10;
def z = bind {
def a = x;
def b = y;
while(a) b;//Erreur
}
Bind et les composants graphiques :
La possibilité de lier des variables avec bind est très intéressant, spécialement dans les développement de composants graphiques, permettant de modifier des composants avec le changement de leurs modèles (ou données). En Swing il faut utiliser des Listeners, nettement plus complexe à utiliser.
Exemple :
Deux zones de texte permettant de modifier les coordonnées x et y du center du cercle avec deux instructions "bind". Le source est commenté.
/*
* Hello.fx
*
* Created on Mar 1, 2009, 9:20:25 PM
*/
import javafx.ext.swing.SwingButton;
import javafx.ext.swing.SwingLabel;
import javafx.ext.swing.SwingTextField;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.Scene;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;
/**
* @author Kaesar ALNIJRES
*/
var w = 500;//largeur Stage
var h = 500; //hauteur Stage
var x = 100; //center X
var y= 100; //center y
var stfx = SwingTextField {
columns: 10
text: "100"
editable: true
}
var stfy = SwingTextField {
columns: 10
text: "100"
editable: true
}
Stage {
title: "Exemple de bind"
width: w;
height: h;
scene: Scene {
content: VBox{
content: [
SwingLabel {
//uniquement pour afficher x et y
text: bind "Center X, : {x},{y}";
},
stfx,//SwingTextField définie la-haut
stfy,
SwingButton {
text: "Bouger"
action: function() {
//Obtient le text dans SwingTextFiled, après conversion le metter dans x
x=java.lang.Integer.parseInt(stfx.getJTextField().getText());
//même chose pour y
y=java.lang.Integer.parseInt(stfy.getJTextField().getText());
}
},
Circle {
centerX: bind x;
centerY: bind y;
radius: 40
fill: Color.BLACK
}
]
}
}
}
Exemple graphique
Comment lier des composants graphiques avec des variables.
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.text.Text;
import javafx.scene.text.Font;
import javafx.ext.swing.SwingTextField;
/**
* @author Kaesar ALNIJRES
*/
var chaine="Hello World";
var tf=SwingTextField {
columns: 10
text: bind chaine with inverse;
editable: true
}
Stage {
title: bind chaine;
width: 250
height: 80
scene: Scene {
content:tf
}
}
Exemple :
Lier des variables d'instance ensemble.
/*
* Hello.fx
*
* Created on Mar 1, 2009, 9:20:25 PM
*/
import javafx.ext.swing.SwingButton;
import javafx.ext.swing.SwingLabel;
import javafx.ext.swing.SwingTextField;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.Scene;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;
/**
* @author Kaesar ALNIJRES
*/
var w = 500;//largeur Stage
var h = 500; //hauteur Stage
var x = 100; //center X
var y= 100; //center y
var stage;
var stfx = SwingTextField {
columns: 10
text: "100"
editable: true
}
var stfy = SwingTextField {
columns: 10
text: "100"
editable: true
}
var c0 = Circle {
centerX: bind x;
centerY: bind y;
radius: 40
fill: Color.BLACK
}
var c1 = Circle {
centerX: bind c0.centerX+100;
centerY: bind c0.centerY-100;
radius: 40
fill: Color.RED
}
Stage {
title: "Exemple de bind"
width: w;
height: h;
scene: Scene {
content: VBox{
content: [
SwingLabel {
text: bind "Center X, : {x},{y}";
},
stfx,//SwingTextField définie la-haut
stfy,//idem
SwingButton {
text: "Bouger"
action: function() {
//Obtient le text dans SwingTextFiled, après conversion le metter dans x
x=java.lang.Integer.parseInt(stfx.getJTextField().getText());
//même chose pour y
y=java.lang.Integer.parseInt(stfy.getJTextField().getText());
}
},c0,c1,
]
}
}
}
4 comments:
superbe article très clair et complet sur le binding merci
Merci :)
Bien expliqué.
Auriez-vous des infos sur un possible moyen de "unbinding"?
Je cherche à créer un éditeur où l'on peut glisser-déposer des liens entre deux objets qui peuvent aussi être déplacés.. Et il faudrait pouvoir aussi changer l'objet cible du lien (glisser d'un objet à un autre..).
Merci de m'apporter votre aide.
Bonjour et merci pour le message,
Excellente question.
Une réponse très rapide : Il me semble q'une instruction “unbind” n'existe pas de cette façon, néanmoins, j'essayerai d'approfondir la recherche.
Cordialement
Kaesar
Post a Comment