JavaFX - Binding & Co

Diskutiere JavaFX - Binding & Co im AWT, Swing, JavaFX & SWT Bereich.
J

JustNobody

Hallo zusammen,

ich bin in erster Linie Backend Entwickler, aber man bekommt ja einiges mit und nun möchte ich bezüglich JavaFX mal meine Sichtweise etwas validieren lassen (Ich beschäftige mich da derzeit etwas mit):

Was zumindest ich regelmäßig finde ist:
- fxml mit fx:id
- Im Controller dann alle Controls mit @FXML ControllType fxId
- Dann sehr viel im Code. Wenn Binding, dann wird das im Code gesetzt

Ist das nicht extrem Kontraproduktiv?

Was die Technologie ja bietet, ist das ganze Binding per fxml.

Somit kann ein Software Entwickler dann alles bauen, was fachlich benötigt wird:
- Controller mit diversen Actions - also z.B. sowas wie quitApplicationAction was klar Funktionsbasiert ist (statt sowas wie ein handleQuitButtonAction, was dann Button orientiert ist.)
- Model, welches im Controller referenziert wird und die Daten hält. Der Controller hält diese Daten nur in Form der Modell-Instanz.

Das kann dann aus meiner Sicht gut getestet werden in Unit Tests.
Und die Abhängigkeiten sind dann so aufgebaut, dass der UI Verantwortliche die UI so bauen kann, wie er möchte. Das Beenden der Applikation geht nicht über einen Button sondern dafür gibt es ein eigenes, spezielles ButtonControl? Kein Thema.Oder es gibt gar kein Button? Und das Schließen kann nur/zusätzlich über ein Menü-Punkt erfolgen? Alles kein Thema. Bindings erfolgen im @FXML und können jederzeit (ohne Code neu zu übersetzen) verändert werden.
Ich sehe das als deutlich besseres Design an aber dazu finden sich kaum Seiten und Beschreibungen (oder ich war zu blind, da was zu finden). Und wenn, dann wird es nur leicht angekratzt und es wird fast nur über andere Dinge geredet....

Ursache kann ggf. sein, dass der SceneBuilder mit sowas nicht umgehen kann. Sobald ich da also z.B. in einem Label ein
text="${controller.model.greeting}"
habe, steigt der SceneBuilder mehr oder weniger aus (Text Element wird leer angezeigt aber zumindest bei mir gab es ganz seltsame Verhaltensweisen ... der "Element-Katalog" auf der linken Seite wurde nicht mehr richtig angezeigt und so ... ) Die Code Elemente funktionieren sehr gut, sogar incl. Auswertung des Controllers so dass das Drop Down gefüllt ist.

Somit stellen sich mir die folgenden Fragen:
- Habe ich das mit dem vernünftigen Aufbau einer JavaFX Applikation so erst einmal richtig verstanden? Oder habe ich da jetzt etwas übersehen und es sprechen gute Gründe gegen diese strikte Aufteilung?
- Gibt es bessere GUI Tools anstelle des Scene Builders? Eine Applikation, die ein graphisches Layout incl. Bindings erlaubt?

Vielen Dank schon einmal für eure Sicht / Meinung zu diesen Punkten.
 
L

lam_tr

Hallo zusammen,

ich bin in erster Linie Backend Entwickler, aber man bekommt ja einiges mit und nun möchte ich bezüglich JavaFX mal meine Sichtweise etwas validieren lassen (Ich beschäftige mich da derzeit etwas mit):

Was zumindest ich regelmäßig finde ist:
- fxml mit fx:id
- Im Controller dann alle Controls mit @FXML ControllType fxId
- Dann sehr viel im Code. Wenn Binding, dann wird das im Code gesetzt

Ist das nicht extrem Kontraproduktiv?

Was die Technologie ja bietet, ist das ganze Binding per fxml.

Somit kann ein Software Entwickler dann alles bauen, was fachlich benötigt wird:
- Controller mit diversen Actions - also z.B. sowas wie quitApplicationAction was klar Funktionsbasiert ist (statt sowas wie ein handleQuitButtonAction, was dann Button orientiert ist.)
- Model, welches im Controller referenziert wird und die Daten hält. Der Controller hält diese Daten nur in Form der Modell-Instanz.

Das kann dann aus meiner Sicht gut getestet werden in Unit Tests.
Und die Abhängigkeiten sind dann so aufgebaut, dass der UI Verantwortliche die UI so bauen kann, wie er möchte. Das Beenden der Applikation geht nicht über einen Button sondern dafür gibt es ein eigenes, spezielles ButtonControl? Kein Thema.Oder es gibt gar kein Button? Und das Schließen kann nur/zusätzlich über ein Menü-Punkt erfolgen? Alles kein Thema. Bindings erfolgen im @FXML und können jederzeit (ohne Code neu zu übersetzen) verändert werden.
Ich sehe das als deutlich besseres Design an aber dazu finden sich kaum Seiten und Beschreibungen (oder ich war zu blind, da was zu finden). Und wenn, dann wird es nur leicht angekratzt und es wird fast nur über andere Dinge geredet....

Ursache kann ggf. sein, dass der SceneBuilder mit sowas nicht umgehen kann. Sobald ich da also z.B. in einem Label ein
text="${controller.model.greeting}"
habe, steigt der SceneBuilder mehr oder weniger aus (Text Element wird leer angezeigt aber zumindest bei mir gab es ganz seltsame Verhaltensweisen ... der "Element-Katalog" auf der linken Seite wurde nicht mehr richtig angezeigt und so ... ) Die Code Elemente funktionieren sehr gut, sogar incl. Auswertung des Controllers so dass das Drop Down gefüllt ist.

Somit stellen sich mir die folgenden Fragen:
- Habe ich das mit dem vernünftigen Aufbau einer JavaFX Applikation so erst einmal richtig verstanden? Oder habe ich da jetzt etwas übersehen und es sprechen gute Gründe gegen diese strikte Aufteilung?
- Gibt es bessere GUI Tools anstelle des Scene Builders? Eine Applikation, die ein graphisches Layout incl. Bindings erlaubt?

Vielen Dank schon einmal für eure Sicht / Meinung zu diesen Punkten.
Hi @JustNobody ,

an sich würde mich das auch interessieren. Bisher habe ich leider auch noch nicht viel über das Binding im FXML gelesen, was eigentlich sehr schade ist. Ich komme ursprünglich von SWT/XWT, dort kann man UIs auch mit xml deklarieren und direkt im xml data binding benutzen was ziemlich cool ist.

Aber jetzt zurück zu deiner Frage, ich denke diesen Schritt im FXML haben die Gluon Entwickler noch nicht wirklich Zeit darein gesteckt oder auch darüber nachgedacht. Vielleicht kannst du ein request hier machen https://github.com/gluonhq/scenebuilder und auf Glück warten dass sie es implementieren :)

Zur zweiten Frage, ich glaube nicht dass sowas schon gibt. Scene Builder ist schon so ziemlich das einzige UI Builder für JavaFX das ich kenne.

Grüße
lam
 
J

JustNobody

Danke. Die Problematik ist im SceneBuilder bekannt und wohl nicht so einfach änderbar oder so ... Ich hatte da bereits einen Link dazu. Werde ich noch heraus suchen und bringen.

Aber dann wird es wohl ohne Grafisches Tool gehen müssen. So man entsprechende Panes nutzt, die die Gliederung für einen machen, sollte es kein Problem sein. Bisher hatte ich einfach nur ein paar ganz triviale Dinge gemacht und da habe ich Controls halt mit dem Editor hart plaziert und die Größe gesetzt. Das ist ja eh nicht das Gelbe vom Ei.

Ansonsten nutze ich die Gelegenheit, das auch etwas im Blog nieder zu schreiben um das etwas zu teilen, was ich da so mache und für richtig halte.
Das Binding klappt mit fxml auch super. Meine ersten Tests waren sehr erfolgreich und ich finde es bisher durchaus gelungen. Aber ich will mich da auch nicht zu sehr blamieren - daher frage ich hier auch noch vorsichtshalber nach, ehe ich zu großen Stuss im Blog erzähle ... Wobei auch gilt: Am Ende kann ja auch rauskommen, dass etwas keine gute Idee ist / war :) Das ist im Blog auch nicht anders wie bei wissenschaftlichen Arbeiten ;)

Und was man auch nicht vergessen darf wenn man etwas erstellen möchte: Für viele Bereiche gibt es auch noch Erweiterungen, also z.B. FormsFX, wenn es um Formulare geht. Da wird es dann auch relativ einfach, gewisse Oberflächen zu erstellen.
 
L

lam_tr

Danke. Die Problematik ist im SceneBuilder bekannt und wohl nicht so einfach änderbar oder so ... Ich hatte da bereits einen Link dazu. Werde ich noch heraus suchen und bringen.

Aber dann wird es wohl ohne Grafisches Tool gehen müssen. So man entsprechende Panes nutzt, die die Gliederung für einen machen, sollte es kein Problem sein. Bisher hatte ich einfach nur ein paar ganz triviale Dinge gemacht und da habe ich Controls halt mit dem Editor hart plaziert und die Größe gesetzt. Das ist ja eh nicht das Gelbe vom Ei.

Ansonsten nutze ich die Gelegenheit, das auch etwas im Blog nieder zu schreiben um das etwas zu teilen, was ich da so mache und für richtig halte.
Das Binding klappt mit fxml auch super. Meine ersten Tests waren sehr erfolgreich und ich finde es bisher durchaus gelungen. Aber ich will mich da auch nicht zu sehr blamieren - daher frage ich hier auch noch vorsichtshalber nach, ehe ich zu großen Stuss im Blog erzähle ... Wobei auch gilt: Am Ende kann ja auch rauskommen, dass etwas keine gute Idee ist / war :) Das ist im Blog auch nicht anders wie bei wissenschaftlichen Arbeiten ;)

Und was man auch nicht vergessen darf wenn man etwas erstellen möchte: Für viele Bereiche gibt es auch noch Erweiterungen, also z.B. FormsFX, wenn es um Formulare geht. Da wird es dann auch relativ einfach, gewisse Oberflächen zu erstellen.
Generell ist Databinding schon ein richtig coole Sache, aber es ist immer morz ein Aufwand das alles einzurichten für die einzelnen Controls, es ist einfach nur übelst Fleißarbeit.

Deshalb habe ich mithilfe von SceneBuilder API ein DatabindingModel -Controller Generator geschrieben, womit ich zum Beispiel alle:
* Labels, Textfields automatisch mit textProperty gebinded wird
* TableView mit ObjectProperty, ObservableList, ...
etc....

Damit spare ich mir sehr viel Tipparbeit.

Ich freue mich schon auf dein Blog Post. Sag einfach Bescheid wenn du durch bist, blamieren wirst du dich da bestimmt nicht, alleine der Aufwand was du da rein steckst ist schon viel Wert! *daumenhoch*
 
J

JustNobody

Du findest da eine kleine Serie unter http://blog.kneitzel.de

Ein großes Problem ist aus meiner Sicht, dass man Bidirectional Bindings nicht im XML definieren kann so ich das richtig verstanden habe. Das geht wohl nur in Code.

Da ist meine Lösung ein fx:ccript welches dann javafx.beans.binding.Bindings.bindBidirectional aufruft. Habe noch nicht getestet, ob mit JavaFX 14 das Binding per # statt $ funktioniert oder ob das noch nicht implementiert wurde. Über das bin ich in der Vergangenheit mal gestolpert.
 
J

JustNobody

Deshalb habe ich mithilfe von SceneBuilder API ein DatabindingModel -Controller Generator geschrieben, womit ich zum Beispiel alle:
* Labels, Textfields automatisch mit textProperty gebinded wird
* TableView mit ObjectProperty, ObservableList, ...
etc....

Damit spare ich mir sehr viel Tipparbeit.
Hast / willst / kannst Du das einmal teilen? Das hört sich erst einmal toll an und das würde ich mir gerne einmal anschauen.
 
L

lam_tr

Hast / willst / kannst Du das einmal teilen? Das hört sich erst einmal toll an und das würde ich mir gerne einmal anschauen.
Nachdem ich dein Post gelesen habe, war ich so motiviert, dass ich jetzt dabei bin, mir ein Eclipse IDE Plugin für das FXML mache. Bin mal gespannt ob das was wird. Ich kanns das Plug-in auf Github verteilen wenn es soweit ist.

Ich hab mal einen kleinen Blick auf deine Seite gemacht, ich schätze deine Arbeit, es macht einfach Spaß sowas zu lesen. Zwei Sachen sind mir da aufgefallen, an sich ist FXML je nachdem wie es aufgebaut ist schon sehr komplex und langsam, wenn ich das noch mit fx:script erweitere, dann wird die FXML unendlich groß, ist das noch gut zu managen? Und wie du schon sagtest, der SceneBuilder kann das dann auch nicht mehr richtig parsen wenn im FXML fx:script erweitert wird oder? ich rede hier von Performanz, weil ich auch sehr an JavaFX Substrate / Jfxmobile mich interessiere und wenn da etwas ausgebremst wird.

Einer der Hauptgründe warum ich SceneBuilder benutze, ist das ich alles schön und schnell per Editor layouten kann. Wenn ich aber im graphischen Editor das DataBinding nicht machen kann und umgekehrt noch extra Texteditor benötige zum erweitern, schreckt mich das etwas ab. Zumal der TextEditor kein Autocompletion and Syntax Highlighting kann. Und vor allem wird das Debuggen auch viel mehr erschwert sage ich mal.

Ich hatte das früher in XWT sehr bequem und schön Databinden können. Deswegen werde ich das jetzt versuchen für FXML umzusetzen, natürlich nur von der Bedienung her.

Bitte betrachte es nicht als Kritik, das ist nur meine Meinung zur Databinding im FXML. Und generell liebe ich über jede JavaFX Blog Einträge, leider gibt es zu wenig davon, deswegen würde ich mich noch mehr über deine freuen.

Grüße
lam
 
J

JustNobody

Ob das am Ende tauglich ist, weiß ich selbst noch nicht. Kann durchaus sein, dass am Ende das Fazit stehen wird: taugt nur sehr begrenzt etwas.

Ob man ohne SceneBuilder auskommen kann, wird sich für mich später zeigen. Ich schaue halt gerade selbst, wie man das mit hbox/vbox und den ganzen Panes gut hin bekommen kann. Klar - am Anfang ist eine GUI einfacher, aber auch bei TeX war meine Erfahrung, dass man da viele Standard Dinge einfacher eben schnell von Hand gemacht hat. War zwar einmal mühsam, sich das zu erarbeiten, aber wenn man das einmal drauf hatte, dann ging es sehr schnell. (Da hatte ich mal die Schnaps-Idee, in TeX Dinge aufzubereiten um dann PDF und HTML zu generieren. Aber das HTML hat sich als annähernd unmöglich herausgestellt. Das war noch zu Zeiten, als ich hier als kneitzel unterwegs war und ich dann so bei einzelnen Fragen das halt generischer ausbauen wollte incl. Blog Eintrag und Post der Antwort hier im Forum. Aber Satz mit X und nach zwei Wochen Fluchen habe ich dann beschlossen, dem TeX doch keine Zeit mehr zu widmen :) )

Aber da lernt man schnell, was da alles an Arbeit drinnen steckt. :)

Und bei JavaFX fehlt es an Dokumentation / guten Seiten. Ich probiere jetzt derzeit, ob ich nicht einer Property irgendwie eine Property zuweisen kann.
Also ein eigenes Control mit Namen "BidirectionalBinding" und das hat zwei Properties:

Code:
    ObjectProperty<Property<? extends Object>> first = new SimpleObjectProperty<>();
    ObjectProperty<Property<? extends Object>> second = new SimpleObjectProperty<>();
Nur wie kann ich dann da die Properties zuweisen? Ich habe also:

XML:
<TextField fx:id="nameTextField" ... />
<BidirectionalBinding first="..." second="..." />
und first soll nun werden $nameTextField.texProperty() und second ${controller.model.nameProperty()}
nameTextField sieht man ja und controller hat ein model welches dann name als StringProperty hat mit get/set name und nameProperty.

Wenn ich das gesetzt bekomme, dann wäre der Aufruf statt in einem JavaScript in meinem Control (das im Konstruktor die Werte ausliest und dann den Aufruf für das Binding macht...

War das verständlich erläutert?
 
L

lam_tr

Ob das am Ende tauglich ist, weiß ich selbst noch nicht. Kann durchaus sein, dass am Ende das Fazit stehen wird: taugt nur sehr begrenzt etwas.

Ob man ohne SceneBuilder auskommen kann, wird sich für mich später zeigen. Ich schaue halt gerade selbst, wie man das mit hbox/vbox und den ganzen Panes gut hin bekommen kann. Klar - am Anfang ist eine GUI einfacher, aber auch bei TeX war meine Erfahrung, dass man da viele Standard Dinge einfacher eben schnell von Hand gemacht hat. War zwar einmal mühsam, sich das zu erarbeiten, aber wenn man das einmal drauf hatte, dann ging es sehr schnell. (Da hatte ich mal die Schnaps-Idee, in TeX Dinge aufzubereiten um dann PDF und HTML zu generieren. Aber das HTML hat sich als annähernd unmöglich herausgestellt. Das war noch zu Zeiten, als ich hier als kneitzel unterwegs war und ich dann so bei einzelnen Fragen das halt generischer ausbauen wollte incl. Blog Eintrag und Post der Antwort hier im Forum. Aber Satz mit X und nach zwei Wochen Fluchen habe ich dann beschlossen, dem TeX doch keine Zeit mehr zu widmen :) )

Aber da lernt man schnell, was da alles an Arbeit drinnen steckt. :)

Und bei JavaFX fehlt es an Dokumentation / guten Seiten. Ich probiere jetzt derzeit, ob ich nicht einer Property irgendwie eine Property zuweisen kann.
Also ein eigenes Control mit Namen "BidirectionalBinding" und das hat zwei Properties:

Code:
    ObjectProperty<Property<? extends Object>> first = new SimpleObjectProperty<>();
    ObjectProperty<Property<? extends Object>> second = new SimpleObjectProperty<>();
Nur wie kann ich dann da die Properties zuweisen? Ich habe also:

XML:
<TextField fx:id="nameTextField" ... />
<BidirectionalBinding first="..." second="..." />
und first soll nun werden $nameTextField.texProperty() und second ${controller.model.nameProperty()}
nameTextField sieht man ja und controller hat ein model welches dann name als StringProperty hat mit get/set name und nameProperty.

Wenn ich das gesetzt bekomme, dann wäre der Aufruf statt in einem JavaScript in meinem Control (das im Konstruktor die Werte ausliest und dann den Aufruf für das Binding macht...

War das verständlich erläutert?
Ich bin mir nicht ganz sicher ob ich das was du vorhast, verstanden habe. Du willst ein eigenes Control mit zwei Properties haben und die sollen dann über FXML gebindet werden, aber bidirectionalBinding ist doch wenn du am Control oder Model Änderung machst, wird es auf dem anderen aktualisiert und umgekehrt, oder?
 
J

JustNobody

Sorry, komme jetzt erst dazu, das im Detail zu erläutern.

Derzeit baue ich das Binding ja über ein fx:script Element auf:
XML:
<TextField fx:id="nameTextField" layoutX="92.0" layoutY="65.0" text="Bitte Namen eingeben." />
      <fx:script>
         javafx.beans.binding.Bindings.bindBidirectional(
               controller.model.nameProperty(),
               nameTextField.textProperty()
         );
      </fx:script>
Sprich: Ich habe ein TextField und im Model habe ich dann dazu eine StringProperty.
Und das soll in beide Richtungen gebunden sein.

Meine Überlegung wäre jetzt, dass ich dieses fx:script ersetzen kann über ein eigenes Tag. Ich könnte z.B. ein eigenes "Control" erstellen, welches BidrectionalBinding heisst. Dem müsste ich aber dann ja angeben, welche Properties ich binden möchte.

Also das Ergebnis sollte dann etwas sein wie:

XML:
<TextField fx:id="nameTextField" layoutX="92.0" layoutY="65.0" text="Bitte Namen eingeben." />
<BidirectionalBinding first="$controller.model.nameProperty" second="$nameTextField.textProperty" />
first / second hatte ich dann als ObjectProperty<Property<String>> definiert und so. (Erst einmal konkret für String Properties. Das wäre dann ggf. später erweiterbar auf Property<? extends Object> oder so.

Aber egal, was ich da dann im fxml so angegeben habe: Das hat alles nicht funktioniert. Ich war nicht in der Lage, da eine Property zuzuweisen.

Das BidirectonalBinding ist natürlich ein triviales Control. Es fügt nichts hinzu und ist somit nicht sichtbar. Aber es ruft dann halt
Bindings.bindBidirectional(first.get(), second.get());
auf.

Die Kernidee wäre halt, dass man über diesen Umweg auch ohne Scripts im fxml ein Bidirectionales Binding definieren könnte.

Evtl. geht es so auch nicht. Ich war schon etwas am überlegen, ob ich statt dessen einfach 4 Angaben machen könnte:
Control / PropertyName das gebunden werden soll.
Model / PropertyName, das gebunden werden soll.

Aber da beißt sich die Katze in den Schwanz fürchte ich, denn wie gebe ich das Model an? Das könnte natürlich auch wieder eine ObjectProperty sein, aber die müsste ich ja erneut auch angeben ... wenn ich die angegeben bekomme, dann bekomme ich eigentlich auch die konkrete Property im model angegeben... oder die Property des Controls.
 
L

lam_tr

Sorry, komme jetzt erst dazu, das im Detail zu erläutern.

Derzeit baue ich das Binding ja über ein fx:script Element auf:
XML:
<TextField fx:id="nameTextField" layoutX="92.0" layoutY="65.0" text="Bitte Namen eingeben." />
      <fx:script>
         javafx.beans.binding.Bindings.bindBidirectional(
               controller.model.nameProperty(),
               nameTextField.textProperty()
         );
      </fx:script>
Sprich: Ich habe ein TextField und im Model habe ich dann dazu eine StringProperty.
Und das soll in beide Richtungen gebunden sein.

Meine Überlegung wäre jetzt, dass ich dieses fx:script ersetzen kann über ein eigenes Tag. Ich könnte z.B. ein eigenes "Control" erstellen, welches BidrectionalBinding heisst. Dem müsste ich aber dann ja angeben, welche Properties ich binden möchte.

Also das Ergebnis sollte dann etwas sein wie:

XML:
<TextField fx:id="nameTextField" layoutX="92.0" layoutY="65.0" text="Bitte Namen eingeben." />
<BidirectionalBinding first="$controller.model.nameProperty" second="$nameTextField.textProperty" />
first / second hatte ich dann als ObjectProperty<Property<String>> definiert und so. (Erst einmal konkret für String Properties. Das wäre dann ggf. später erweiterbar auf Property<? extends Object> oder so.

Aber egal, was ich da dann im fxml so angegeben habe: Das hat alles nicht funktioniert. Ich war nicht in der Lage, da eine Property zuzuweisen.

Das BidirectonalBinding ist natürlich ein triviales Control. Es fügt nichts hinzu und ist somit nicht sichtbar. Aber es ruft dann halt
Bindings.bindBidirectional(first.get(), second.get());
auf.

Die Kernidee wäre halt, dass man über diesen Umweg auch ohne Scripts im fxml ein Bidirectionales Binding definieren könnte.

Evtl. geht es so auch nicht. Ich war schon etwas am überlegen, ob ich statt dessen einfach 4 Angaben machen könnte:
Control / PropertyName das gebunden werden soll.
Model / PropertyName, das gebunden werden soll.

Aber da beißt sich die Katze in den Schwanz fürchte ich, denn wie gebe ich das Model an? Das könnte natürlich auch wieder eine ObjectProperty sein, aber die müsste ich ja erneut auch angeben ... wenn ich die angegeben bekomme, dann bekomme ich eigentlich auch die konkrete Property im model angegeben... oder die Property des Controls.
Okay nach einer ausführlichere Erklärung habe ich dein Vorhaben verstanden. Die Idee ist Klasse, hoffentlich klappt das bei deinem test auch. Dieses Binding würde für einfache StringProperties gehen, aber bei custom Objects, LocalDate, LocalDateTime, etc. würdest du noch einen dritten Parameter benötigen zum Konvertieren.

Eine Sache gefällt mir hier nicht ganz, jedes mal wenn BidirectionalBinding hinzugefügt wird, wird beim Laden der FXML ein separated Object erstellt nur um ein statisches Binding zu machen. Ich finde es irgendwie schöner wenn man für alle <BidirectionalBinding> ein zentrale Klasse hätt. Was meinst du dazu?

Grüße
lam
 
J

JustNobody

Mein Problem ist, dass ich irgendwie das mit dem Control schon gar nicht erst hin bekomme. Ich habe dazu einmal folgendes gemacht:

Code:
package de.kneitzel;

import javafx.beans.NamedArg;
import javafx.beans.binding.Bindings;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.fxml.Initializable;
import javafx.scene.control.Control;

import java.net.URL;
import java.util.ResourceBundle;

public class BidirectionalBinding extends Control implements Initializable {

    private ObjectProperty<StringProperty> first = new SimpleObjectProperty<>();
    public StringProperty getFist() { return first.getValue(); }
    public void setFirst(final StringProperty value) {
        first.setValue(value);
    }
    public ObjectProperty<StringProperty> firstProperty() { return first; }

    private ObjectProperty<StringProperty> second  = new SimpleObjectProperty<>();
    public StringProperty getSecond() { return second.getValue(); }
    public void setSecond(final StringProperty value) {
        second.setValue(value);
    }
    public ObjectProperty<StringProperty> secondProperty() { return second; }

    public BidirectionalBinding() {}

    public BidirectionalBinding(@NamedArg("first") final StringProperty first, @NamedArg("first") final StringProperty second) {
        setFirst(first);
        setSecond(second);
    }

    @Override
    public void initialize(URL location, ResourceBundle resources) {
        if (first.getValue() == null || second.getValue() == null) {
            System.err.println("A value is null!");
        }
        first.getValue().bindBidirectional(second.getValue());
    }
}
Also mal davon abgesehen, dass initialize nicht aufgerufen wird und dass ich die folgende Meldung bekomme:
Code:
Juni 29, 2020 9:17:06 VORM. javafx.scene.control.Control doProcessCSS
SCHWERWIEGEND: The -fx-skin property has not been defined in CSS for [email protected] and createDefaultSkin() returned null.
Habe ich generell das Problem, dass setFist und setSecond zwar aufgerufen werden aber das nur mit null Werten oder ich kriege eine Exception.

Was habe ich alles probiert:

${controller.model.name} => javafx.fxml.LoadException: Cannot bind to untyped object.
${controller.model.nameProperty} => javafx.fxml.LoadException: Cannot bind to untyped object.
${controller.model.nameProperty()} => javafx.fxml.LoadException: Cannot bind to untyped object.

$controller.model.nameProperty() => null im Aufruf
$controller.model.nameProperty => null im Aufruf
$controller.model.name => java.lang.IllegalArgumentException: Unable to coerce to class javafx.beans.property.StringProperty.

Also irgendwie habe ich hier gewisse Probleme, so eine Property überhaupt anzugeben.

(Die anderen Probleme weiter oben dürften daher kommen, dass ich das einfach zu sehr vereinfacht habe. Andere CustomControls sind ja mit Controller und so aufgebaut. Aber da ich etwas hatte, das erst einmal geladen werden kann, wollte ich da erst einmal die Angabe von Properties testen.)
 
T

temi

Ich habe das hier nur überflogen, aber vielleicht kannst du aus diesem Projekt ja etwas Hilfreiches mitnehmen. Falls nicht, so sei es ungeschrieben...
 
J

JustNobody

Ja, darüber bin ich auch schon gestolpert alleine um zu sehen, ob dies evtl. ein brauchbarer Ansatz zur sauberen Trennung ist. Da werde ich auf jeden Fall noch tiefer eintauchen müssen. Danke auf jeden Fall für den Hinweis!
 
T

temi

Unter C# hatte ich viel mit XAML und Caliburn Micro gearbeitet. Das war wirklich sehr angenehm. Aber dies nur am Rande. Ich hoffe du findest eine gute Lösung!
 
J

JustNobody

Ja, C# mit XAML (WPF) habe ich Erfahrungen mit gesammelt. Da ist halt deutlich mehr möglich mit z.B.:
Text="{Binding Path=SomeSource.SomeValue, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"

Aber das fxml ist da halt deutlich eingeschränkter. Da scheint es tatsächlich nur den Weg über den Code zu geben.

So wie ich dieses mvvmFX verstehe, hat man da dann im Java Part der View den Code mit den Bindings. Statt also alles in einer fxml Datei zu halten (Die dann nicht mehr mit Tools wie SceneBuilder bearbeitet werden kann), wurden die notwendigen Teile in den "Controller" verschoben, der nun Part der View ist (und auch als View bezeichnet wird).

Mögliche Logik und Co liegt im ViewModel - ist also aus dem Controller raus gewandert, so dass der Controller sozusagen in View umbenannt werden konnte (er kontrolliert nichts mehr! Ich mag es, wenn deutlich wird, dass die Benennung auch direkt Sinn ergibt!).
Hat auf jeden Fall gewisse Vorteile. Aber die View (fxml Part) ist nicht abgegrenzt vom Code editierbar. Wobei das auch bei mir nicht der Fall war - ich hatte den Code ja auch nur in das fxml integriert. Somit sieht das auf jeden Fall wie eine sehr interessante Sache aus.

Und ich kratze ja erst noch an der Oberfläche. Die haben da auch DI verwendet und so (Google Guice, Spring und andere DI Frameworks werden unterstützt wie ich es gesehen habe) und dazu bin ich noch nicht vorgedrungen. Da bin ich mal gespannt, was die da noch für nette Einfälle hatten...
 
T

temi

So wie ich dieses mvvmFX verstehe, hat man da dann im Java Part der View den Code mit den Bindings. Statt also alles in einer fxml Datei zu halten
Das ist wohl so eine Art "code-behind". Gibt es ja, so ich mich richtig erinnere, auch bei WPF.

EDIT: Aus dem "How-to" von mvvmFX:
Bei FXML-Basierten Ansichten besteht die konzeptionelle "View" aus 2 Dateien:

  1. Die FXML-Datei ansich enthält die Struktur und das Layout der Oberfläche. Sie definiert, welche UI-Elemente verwendet werden.
  2. Eine so genannte "CodeBehind" Klasse oder "View Klasse". Dabei handelt es sich um eine Java classe, die Referenzen auf die UI-Controls der FXML-Datei enthält. Sie stellt also die Verbindung von der FXML-Datei zum Java-Code dar.
Beide Dateien zusammen bilden "die View". MvvmFX benutzt einen Convention-Over-Configuration-Ansatz mit einigen Namenskonventionen. Diese besagen, dass sowohl die FXML-Datei und die CodeBehind-Klasse den gleichen Namen (ohne Dateiendung) besitzen sollen und sich im gleichen Verzeichnis/Package befinden müssen.
 
Zuletzt bearbeitet:
mihe7

mihe7

Habe ich generell das Problem, dass setFist und setSecond zwar aufgerufen werden aber das nur mit null Werten oder ich kriege eine Exception.
Keine Ahnung, ob es damit zu tun hat, nur weil es mir gerade auffällt: der Setter ist mit r, der Getter ohne: setFirst vs. getFist

EDIT: kann es sein, dass Getter und Setter hier mit String und nicht StringProperty arbeiten müssen?
 
J

JustNobody

Ach, Tippfehler. Werde ich morgen noch mal schauen.

Wobei das Problem schon vorab lag, denn der Setter wurde ja nicht korrekt aufgerufen. Binding scheint nur für Werte zu gehen aber nicht für die eigentlichen Properties wie es scheint.

Der erste Blick auf mvvmFX ist aber sehr vielversprechend finde ich. Das vertiefe ich noch weiter ...

Bin aber generell gespannt, was da noch alles geht. War da ja immer sehr skeptisch, wenn es um Themen wie Multi Plattform Lösungen und so ging. Aber das muss man sich alles mal im Detail ansehen :)
 
B

Bela B.

Das hier, ist auch für mich extrem interessant. Bin ebenfalls am Verstehen von mvvmFX und wie man es "richtig" verwendet. Mir fehlt da allerdings einiges an Wissen und so geht es nur schleppend voran bei mir.

Daher @JustNobody bitte dringend mit dem Blog weitermachen und das ganze auch für Einsteiger in JavaFX verständlich erklären, denn genau so etwas fehlt bei JavaFX.

Wie du anfangs schon sagtest, gibt es viele Guides, Tutorials, Videos und Bücher, die JavaFX zum Teil sehr oberflächlich und auch noch sehr "falsch" (damit meine ich sehr unsauber bezüglich Trennung der Zuständigkeiten) zeigen. Da wird oft sehr viel hingerotzt, ohne schönen Code zu produzieren.

Bitte dran bleiben🤩
 
Thema: 

JavaFX - Binding & Co

Passende Stellenanzeigen aus deiner Region:
Anzeige

Neue Themen

Anzeige

Anzeige
Oben