Updating after data binding does not update content in UI - vaadin7

Sorry, but I must have a mental lapsus right now, because I don't see where the problem is, and should be trivial. I've prepared a simple scenario where I bind a field to a bean property using the BeanFieldGroup, and when I click the Change and Reset buttons, the model is set with the correct values, but the textfield in the UI is not being updated.
I'm using Vaadin4Spring, but should not be the issue.
import com.vaadin.data.fieldgroup.BeanFieldGroup;
import com.vaadin.navigator.View;
import com.vaadin.navigator.ViewChangeListener;
import com.vaadin.spring.annotation.SpringView;
import com.vaadin.ui.Button;
import com.vaadin.ui.Notification;
import com.vaadin.ui.TextField;
import com.vaadin.ui.VerticalLayout;
import java.io.Serializable;
#SpringView(name = "test")
public class TestView extends VerticalLayout implements View {
private TextField txtTest = new TextField("Test");
private Button btnChange = new Button("Click!");
private Button btnReset = new Button("Reset");
private TestBean testBean = new TestBean();
public TestView() {
txtTest.setImmediate(true);
addComponent(txtTest);
addComponent(btnChange);
addComponent(btnReset);
BeanFieldGroup<TestBean> binder = new BeanFieldGroup<>(TestBean.class);
binder.setItemDataSource(testBean);
binder.setBuffered(false);
binder.bind(txtTest, "text");
initComponents();
}
private void initComponents() {
btnChange.addClickListener(new Button.ClickListener() {
#Override
public void buttonClick(Button.ClickEvent event) {
testBean.setText("Hello world!");
}
});
btnReset.addClickListener(new Button.ClickListener() {
#Override
public void buttonClick(Button.ClickEvent event) {
testBean.setText("");
}
});
}
#Override
public void enter(ViewChangeListener.ViewChangeEvent event) {
Notification.show("Test");
}
public class TestBean implements Serializable {
private String text;
public TestBean() {
text = "";
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
}
}

The closest thing I have found is binder.discard(), which forces all bound fields to re-read its value from the bean. Yes, it still has to be called manually, but is still far less painful than getItemDataSource().getItemProperty(...).setValue(...). If there are any concerns with this brute-force approach then of course one can call Field.discard() directly on the fields that should be affected.

You are calling a bean setter directly and because Java doesn't provide any way to listen that kind of changes, the Vaadin property (or a TextField) doesn't know that the value has been changed. If you change the value through a Vaadin property by saying
binder.getItemDataSource().getItemProperty("text").setValue("new value");
then you see "new value" on the TextField, and because buffering is disabled, testBean.getText() also returns "new value".

Related

How to attach event handlers to controller in MVC | JavaFX

I am having trouble understanding how to apply the mvc pattern with JavaFX.
Here are my questions with respect to the code below, since I need to follow the pattern given in the code:
a) How can I attach an event handler of the button which is present in my ViewA to the code in my ControllerA (specifically, attachEventHandlers() method). For example, I want my button to populate the comboBox in ViewA with the results of getModelItems() method from controller.
Note that the method getModelItems() is private.
b) I would have multiple buttons and event handlers in my view. How will I bind each one of them uniquely to the controller?
c) I want to invoke setName(String name) on my model in the controller, and the parameter I want to pass is the selected value on the comboBox in viewA. How can I achieve this?
Thank you so much for any help!
Below is the code referred in the description.
Controller:
import model.ModelA;
import view.ViewA;
public class ControllerA {
private ViewA view;
private ModelA model;
public ControllerA(ViewA view, ModelA model) {
//initialise model and view fields
this.model = model;
this.view = view;
//populate combobox in ViewB, e.g. if viewB represented your ViewB you could invoke the line below
//viewB.populateComboBoxWithCourses(setupAndRetrieveCourses());
this.attachEventHandlers();
}
private void attachEventHandlers() {
}
private String[] getModelItems() {
String[] it = new String[2];
it[0] = "0";
it[1] = "1";
return it;
}
}
Model:
public class ModelA {
private String name;
public Name() {
name = "";
}
public void setName(String name) {
this.name = name;
}
#Override
public String toString() {
return "Name = " + name;
}
}
View:
import javafx.scene.layout.BorderPane;
//You may change this class to extend another type if you wish
public class ViewA extends BorderPane {
public BorderPane bp;
public ViewA(){
this.bp = new BorderPane();
ComboBox comboBox = new ComboBox();
Button button1 = new Button("Populate");
bp.setTop(button1);
bp.setBottom(comboBox);
}
}
Loader:
public class ApplicationLoader extends Application {
private ViewA view;
#Override
public void init() {
//create model and view and pass their references to the controller
ModelA model = new ModelA();
view = new ViewA();
new ControllerA(view, model);
}
#Override
public void start(Stage stage) throws Exception {
//whilst you can set a min width and height (example shown below) for the stage window,
//you should not set a max width or height and the application should
//be able to be maximised to fill the screen and ideally behave sensibly when resized
stage.setMinWidth(530);
stage.setMinHeight(500);
stage.setTitle("Final Year Module Chooser Tool");
stage.setScene(new Scene(view));
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
You add delegates to your ViewA to allow for access:
public class ViewA extends BorderPane {
ComboBox comboBox;
Button button1;
public ViewA(){
comboBox = new ComboBox();
button1 = new Button("Populate");
setTop(button1);
setBottom(comboBox);
}
// Add delegates for all functionality you want to make available through ViewA
public ObservableList<String> getItems() { return comboBox.getItems(); }
public void setOnButton1Action(...) { ... }
public void setOnButton2Action(...) { ... }
...
}
You can go as broad or as narrow as you like, based on how much you want to manage through ViewA.

javafx checkBoxTableCell callback

I would like to ask what is the most elegant way to capture value change of CheckBoxTableCell in my TableView.
My goal is to save new value in DB which my example shows:
printedColumn.setCellValueFactory(f -> f.getValue().getPrintedProperty());
printedColumn.setCellFactory(CheckBoxTableCell.forTableColumn(new Callback<Integer, ObservableValue<Boolean>>() {
#Override
public ObservableValue<Boolean> call(Integer param) {
ProductFx productFx = addProductModel.getProductFxObservableList().get(param);
updateInDatabase(productFx);
return productFx.getPrintedProperty();
}
}));
This works fine, but I don't feel like it's the best way to achieve that. For other columns I follow this way:
#FXML
public void onEditPrice(TableColumn.CellEditEvent<ProductFx, Number> e) {
ProductFx productFx = e.getRowValue();
productFx.setPrice(e.getNewValue().doubleValue());
updateInDatabase(productFx);
}
fxml:
<TableColumn fx:id="priceColumn" onEditCommit="#onEditPrice" prefWidth="75.0" text="%addProductTable.price" />
Is it possible to do it in similar way with #FXML annotated method and fxml configuration? Maybe some other ideas?
I really find it hard to get the idea behind your question. I publish the code how I think it should be done. If it does not meet your requirements please elaborate on what you are exactly trying to achive.
import javafx.application.Application;
import javafx.beans.property.LongProperty;
import javafx.beans.property.SimpleLongProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.TextFieldTableCell;
import javafx.stage.Stage;
public class TableViewApp extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage stage) throws Exception {
ObservableList<Product> products = FXCollections.observableArrayList(product -> new ObservableValue[] {product.nameProperty()});
products.add(new Product(1l, "Machine1"));
products.add(new Product(2l, "Machine2"));
products.addListener((ListChangeListener<Product>) change -> {
while (change.next()) {
if (change.wasUpdated()) {
for (int i = change.getFrom(); i < change.getTo(); i++) {
updateInDb(change.getList().get(i));
}
}
}
});
TableView<Product> tableView = new TableView<>(products);
tableView.setEditable(true);
TableColumn<Product, Long> idColumn = new TableColumn<>("Id");
idColumn.setCellValueFactory(cellData -> cellData.getValue().idProperty().asObject());
TableColumn<Product, String> nameColumn = new TableColumn<>("Name");
nameColumn.setCellValueFactory(cellData -> cellData.getValue().nameProperty());
nameColumn.setCellFactory(TextFieldTableCell.forTableColumn());
nameColumn.setEditable(true);
tableView.getColumns().add(idColumn);
tableView.getColumns().add(nameColumn);
stage.setScene(new Scene(tableView));
stage.show();
}
private void updateInDb(Product product) {
System.out.println("Update " + product + " in db");
}
}
class Product {
private LongProperty id = new SimpleLongProperty();
private StringProperty name = new SimpleStringProperty();
public Product(long id, String name) {
this.id.set(id);
this.name.set(name);
}
public LongProperty idProperty() {
return id;
}
public long getId() {
return id.get();
}
public StringProperty nameProperty() {
return name;
}
public String getName() {
return name.get();
}
#Override
public String toString() {
return "Product[id=" + getId() + ", name=" + getName() + "]";
}
}
I do not understand in detail how you got your code working. I guess it is not working as intended by the design of the API. I can definitely answer if it is possible to do it with a simple FXML attribute of CheckBoxTableCell: No.
In case of CheckBoxTableCell
... it is not necessary that the cell enter its editing state (...). A side-effect of this is that the usual editing callbacks (such as on edit commit) will not be called. If you want to be notified of changes,
it is recommended to directly observe the boolean properties that are
manipulated by the CheckBox.
as stated by the javadoc.
If the class of printedProperty implements ObservableValue<Boolean>(as stated by your code) you should follow the cited doc and add a ChangeListener to it like
printedColumn.setCellValueFactory(f -> f.getValue().getPrintedProperty());
printedColumn.setCellFactory(CheckBoxTableCell.forTableColumn(new Callback<Integer, ObservableValue<Boolean>>() {
#Override
public ObservableValue<Boolean> call(Integer param) {
ProductFx productFx = addProductModel.getProductFxObservableList().get(param);
return productFx.getPrintedProperty();
}
}));
ObservableList<ProductFx> obs = addProductModel.getProductFxObservableList();
obs.addListener(new ListChangeListener<ProductFx>(){
#Override
public void onChanged(Change<? extends ProductFx> c) {
if(c.wasAdded()) {
for (ProductFx s:c.getAddedSubList()) {
s.getPrintedProperty().addListener(new ChangeListener<Boolean>() {
ProductFx localProductFx=s;
#Override
public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
updateInDatabase(localProductFx);
}
});
}
}
}
});
But this is not elegant at all.
To your approach to solve the problem:
The Callback<Integer, ObservableValue<Boolean>>() you used is called each time when the displayed cell is updated. This happens especially when you are scrolling through a huge list, because TableView only keeps as many Cell instances as necessary to fill its view-port (doc). They are simply updated during scrolling and your code updates the database each time this happens, so you might run into performance problems for large datasets or slow databases.
PS: As far as I understand your code you do not follow the usual naming conventions for properties. This might lead to problems using reflecting classes like PropertyValueFactory.

How to identify the source of JavaFX Property changes?

how can the source of a JavaFX property change be identified, i.e. who changed the property?
--
Example: the "selected" state of a CheckBox must be synchronized with another objects state, and the other object is not supporting Properties or Bindings. The obvious approach would be to register a ChangeHandler at the selectedProperty of the CheckBox in order to update the other objects state. In the opposite direction, another notification facility is used to call setSelected() or selectedProperty.set() of the CheckBox. This leads to a problem: the registered ChangeHandler is called not only when the user clicks the CheckBox in the UI, but also when the other object changes its state.
In the latter case, we dont want to propagate the change back to the object, of course. Surprisingly there does not seem to be anything on which the ChangeHandler could decide whether the Property was changed by the UI control itself or from outside. The first argument of the handler function only refers to the Property/Observabe which is changed, not who changed it. The oldValue and newValue arguments can be used to break an infinite notification cycle, but they are not sufficient to prevent the first unneccessary, possibly harmful notification.
--
The above description should be sufficient, but if you prefer this question in form of a minimal working example, the following code demonstrates the problem:
The "other object" with one boolean flag as "state" (a database in the real world):
package sample;
public class SomeDatabaseEntry {
private boolean someFlag = false;
public boolean getSomeFlag()
{
return someFlag;
}
public void setSomeFlag(boolean state)
{
someFlag = state;
}
public void toggleSomeFlag()
{
someFlag = !someFlag;
}
}
Main:
package sample;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class Main extends Application {
/*
* dummy object representing the "other side", e.g. a database
*/
public final SomeDatabaseEntry _someEntry = new SomeDatabaseEntry();
private Controller _controller;
#Override
public void start(Stage primaryStage) throws Exception{
FXMLLoader fxmlLoader = new FXMLLoader();
Parent root = fxmlLoader.load(getClass().getResource("sample.fxml").openStream());
primaryStage.setTitle("Hello World");
primaryStage.setScene(new Scene(root, 300, 275));
primaryStage.show();
_controller = fxmlLoader.getController();
_controller.init(_someEntry);
startSomeDummyDatabaseUpdater();
}
/*
* dummy logic that emulates external changes (e.g. database updates)
* in the real world there would be a function that is called by the
* database with a description of the changes that occured.
* as said: this part is not under my control
*/
public void startSomeDummyDatabaseUpdater()
{
new Thread(() -> {
while (true)
{
_someEntry.toggleSomeFlag();
_controller.updateUIFromDatabase();
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
public static void main(String[] args) {
launch(args);
}
}
The controller handling user input and the other objects state (database):
package sample;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.fxml.FXML;
import javafx.scene.control.CheckBox;
public class Controller {
private SomeDatabaseEntry _someEntry;
#FXML
private CheckBox myCheckBox;
public void init(SomeDatabaseEntry entry)
{
_someEntry = entry;
myCheckBox.selectedProperty().addListener(new ChangeListener<Boolean>() {
#Override
public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
// If the user toggled the CheckBox in the UI, update the state of the database
// PROBLEM: this handler is also called in the context of a database update (function below)
// in this case the database must not be updated (e. g. because the update is expensive)
_someEntry.setSomeFlag(newValue);
System.out.println("Database was updated: " + newValue);
}
});
}
public void updateUIFromDatabase()
{
myCheckBox.selectedProperty().setValue(_someEntry.getSomeFlag());
}
}
FXML:
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.control.CheckBox?>
<GridPane fx:id="root" fx:controller="sample.Controller"
xmlns:fx="http://javafx.com/fxml" alignment="center" hgap="10" vgap="10">
<CheckBox fx:id="myCheckBox" text="someFlag"></CheckBox>
</GridPane>
Just introduce an additional property. Typically this would be part of the model.
public class UIModel {
private final BooleanProperty value = new SimpleBooleanProperty();
public BooleanProperty valueProperty() {
return value ;
}
public final boolean getValue() {
return valueProperty().get();
}
public final void setValue(boolean value) {
valueProperty().set(value);
}
// other properties, etc...
}
Now you create an instance of your model and share that instance with interested parties, e.g. using dependency injection, etc.
Your controller should do something like
public class Controller {
#Inject // or inject it by hand, just imagining DI here for simplicity
private UIModel model ;
#Inject
private DAO dao ;
#FXML
private CheckBox myCheckBox ;
public void initialize() {
model.valueProperty().addListener((obs, oldValue, newValue) ->
myCheckBox.setSelected(newValue));
myCheckBox.selectedProperty().addListener((obs, wasSelected, isNowSelected) -> {
if (model.getValue() != isNowSelected) {
updateDatabase(isNowSelected);
model.setValue(isNowSelected);
}
});
}
private void updateDatabase(boolean value) {
Task<Void> task = new Task<Void>() {
#Override
protected Void call() throws Exception {
dao.update(value);
return null ;
}
};
task.setOnFailed(e -> { /* handle errors */ });
new Thread(task).start() ; // IRL hand to executor, etc.
}
}
Now your "updates from database" (which I assume represent changes that have already occurred to the external database) look like
UIModel model = ... ; // from DI or wherever.
while (! Thread.currentThread().isInterrupted()) {
Platform.runLater(() -> model.setValue(! model.getValue()));
try {
Thread.sleep(2000);
} catch (InterruptedException exc) {
Thread.currentThread().interrupt();
}
}
The point here is that you update the model, bringing it into sync with the external resource. The listener on the model updates the check box, so those are now consistent. That fires the listener on the check box's selected state, but that listener doesn't update the database if the model and check box are already in sync, which is the case if the check box was changed because of a change in the model.
On the other hand, if the user checks the check box, the model will not be in sync, so the listener updates the database and then brings the model in sync with the UI and database.
In any real application, it's going to be desirable to define a UI model (which may be more than one class) anyway, as you want to separate the UI state from the views and controllers. How you manage the exact interface of model to back-end services might vary somewhat, and you may need to modify where things sit in relation to that interface, but this separation should provide the means to do what you need, as in this example.

Accessing the OK button from inside the dialog controller [javaFX]

I am trying to disable the OK button in a javaFX dialog untill all of the text fields have content.
Due to the ButtonType not having FXML support it has to be added to the Dialog in the Controller class of the main Window. due to this I'm unable to (cant find a way) to link the button to a variable inside the dialog controller.
I have tried handling the process in the main Controller class as follows:
FXMLLoader loader = new FXMLLoader();
loader.setLocation(getClass().getResource("addDialog.fxml"));
try {
dialog.getDialogPane().setContent(loader.load());
} catch(IOException e) {
e.getStackTrace();
}
dialog.getDialogPane().getButtonTypes().add(ButtonType.OK);
dialog.getDialogPane().getButtonTypes().add(ButtonType.CANCEL);
dialog.getDialogPane().lookupButton(ButtonType.OK).setDisable(true);
AddDialogController controller = loader.getController();
// below calls on a getter from the addDialogController.java file to check if the input fields are full
if (controller.getInputsFull()) {
dialog.getDialogPane().lookupButton(ButtonType.OK).setDisable(false);
}
unfortunately this didn't work, the above code can only be run once before or after the window is called and cant run during.
so is there a way to access the OK ButtonType that comes with javaFX inside the dialog controller if it has been declared outside?
Or is there another way to disable the button based of information from the dialog controller that is being updated by the user?
thanks for any help
Edit 1:
As requested the addDialogController, this is very bare bones and incomplete, hopefully it helps:
import data.Contact;
import data.ContactData;
import javafx.fxml.FXML;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
public class AddDialogController {
#FXML
private TextField firstNameField;
#FXML
private TextField lastNameField;
#FXML
private TextField numberField;
#FXML
private TextArea notesArea;
private boolean inputsFull;
public void processResults() {
String first = firstNameField.getText().trim();
String last = lastNameField.getText().trim();
String number = numberField.getText().trim();
String notes = notesArea.getText().trim();
Contact contact = new Contact(first, last, number, notes);
// ContactData.add(contact);
}
#FXML
public void handleKeyRelease() {
boolean firstEmpty = firstNameField.getText().trim().isEmpty() && firstNameField.getText().isEmpty();
boolean lastEmpty = lastNameField.getText().trim().isEmpty() && lastNameField.getText().isEmpty();
boolean numberEmpty = numberField.getText().trim().isEmpty() && numberField.getText().isEmpty();
boolean notesEmpty = notesArea.getText().trim().isEmpty() && notesArea.getText().isEmpty();
inputsFull = !firstEmpty && !lastEmpty && !numberEmpty && !notesEmpty;
System.out.println(firstEmpty);
System.out.println(lastEmpty);
System.out.println(numberEmpty);
System.out.println(notesEmpty);
System.out.println(inputsFull);
System.out.println();
}
public boolean isInputsFull() {
return this.inputsFull;
}
First, delete your handleKeyRelease method. Never use key event handlers on text input components: for one thing they will not work if the user copies and pastes text into the text field with the mouse. Just register listeners with the textProperty() instead, if you need. Also, isn't (for example)
firstNameField.getText().trim().isEmpty() && firstNameField.getText().isEmpty()
true if and only if
firstNameField.getText().isEmpty();
is true? It's not clear what logic you are trying to implement there.
You should simply expose inputsFull as a JavaFX property:
public class AddDialogController {
#FXML
private TextField firstNameField;
#FXML
private TextField lastNameField;
#FXML
private TextField numberField;
#FXML
private TextArea notesArea;
private BooleanBinding inputsFull ;
public BooleanBinding inputsFullBinding() {
return inputsFull ;
}
public final boolean getInputsFull() {
return inputsFull.get();
}
public void initialize() {
inputsFull = new BooleanBinding() {
{
bind(firstNameField.textProperty(),
lastNameField.textProperty(),
numberField.textProperty(),
notesArea.textProperty());
}
#Override
protected boolean computeValue() {
return ! (firstNameTextField.getText().trim().isEmpty()
|| lastNameTextField.getText().trim().isEmpty()
|| numberField.getText().trim().isEmpty()
|| notesArea.getText().trim().isEmpty());
}
};
}
public void processResults() {
String first = firstNameField.getText().trim();
String last = lastNameField.getText().trim();
String number = numberField.getText().trim();
String notes = notesArea.getText().trim();
Contact contact = new Contact(first, last, number, notes);
// ContactData.add(contact);
}
}
and then all you need is
dialog.getDialogPane().getButtonTypes().add(ButtonType.OK);
dialog.getDialogPane().getButtonTypes().add(ButtonType.CANCEL);
AddDialogController controller = loader.getController();
dialog.getDialogPane().lookupButton(ButtonType.OK)
.disableProperty()
.bind(controller.inputsFullBinding().not());

Binding an enum's toString() to a Label

I have set up my application to change its function based on an enum. The value of a variable linked to this enum will determine how the program interprets certain actions like mouse clicks and so on. I would like a Label (perhaps in the status area in the bottom left) to reflect what the current "mode" the application is in, and display a readable message for the user to see.
Here's my enum:
enum Mode {
defaultMode, // Example states that will determine
alternativeMode; // how the program interprets mouse clicks
// My attempt at making a property that a label could bind to
private SimpleStringProperty property = new SimpleStringProperty(this, "myEnumProp", "Initial Text");
public SimpleStringProperty getProperty() {return property;}
// Override of the toString() method to display prettier text
#Override
public String toString()
{
switch(this) {
case defaultMode:
return "Default mode";
default:
return "Alternative mode";
}
}
}
From what I've gathered, what I'm looking for is a way to bind an enum's toString() property (which I overrode into more digestable form) to this label. The binding would be so that whenever I set something like
applicationState = Mode.alternativeMode;
the label will display the toString() results automatically, without me needing to place a leftStatus.setText(applicationState.toString()) every time I do that.
Here's what I've tried: (in my main controller class):
leftStatus.textProperty().bind(applicationState.getProperty());
That sets the label to the initial text, but won't update when I update the applicationState enum.
What am I doing wrong?
Instead of adding a property to the enum class, why not use a ObjectProperty for the application state? Have a look at this MCVE:
import javafx.application.Application;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.FlowPane;
import javafx.stage.Stage;
public class Example extends Application {
private ObjectProperty<Mode> appState = new SimpleObjectProperty<>(Mode.DEFAULT);
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) throws Exception {
Button btn = new Button("Toggle mode");
btn.setOnMouseClicked((event) -> appState.setValue(appState.get() == Mode.DEFAULT ? Mode.ALTERNATIVE : Mode.DEFAULT));
Label lbl = new Label();
lbl.textProperty().bind(appState.asString());
FlowPane pane = new FlowPane();
pane.getChildren().addAll(btn, lbl);
primaryStage.setScene(new Scene(pane));
primaryStage.show();
}
public enum Mode {
DEFAULT("Default mode"),
ALTERNATIVE("Alternative mode");
private String description;
private Mode(String description) {
this.description = description;
}
#Override
public String toString() {
return description;
}
}
}
Use asString to get a StringBinding from a Property<Mode> containing the value of the property converted to String using the object's toString method.
Example:
#Override
public void start(Stage primaryStage) {
ComboBox<Mode> combo = new ComboBox<>();
combo.getItems().setAll(Mode.values());
Label label = new Label();
// use "state" property from combo box
// (you could replace combo.valueProperty() with your own property)
label.textProperty().bind(combo.valueProperty().asString());
Scene scene = new Scene(new VBox(combo, label), 200, 200);
primaryStage.setScene(scene);
primaryStage.show();
}
Otherwise, if you want the property value contained in the enum, you could use Bindings.selectString, provided you rename the getProperty() method to propertyProperty() to adhere the naming conventions:
enum Mode {
...
public StringProperty propertyProperty() {return property;}
...
}
private final Random random = new Random();
#Override
public void start(Stage primaryStage) {
ComboBox<Mode> combo = new ComboBox<>();
combo.getItems().setAll(Mode.values());
Label label = new Label();
// use "state" property from combo box
// (you could replace combo.valueProperty() with your own property)
label.textProperty().bind(Bindings.selectString(combo.valueProperty(), "property"));
Scene scene = new Scene(new VBox(combo, label), 200, 200);
scene.setOnMouseClicked(evt -> {
// change property values at random
Mode.defaultMode.propertyProperty().set(random.nextBoolean() ? "a" : "b");
Mode.alternativeMode.propertyProperty().set(random.nextBoolean() ? "c" : "d");
});
primaryStage.setScene(scene);
primaryStage.show();
}

Resources