Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JavaBean wrapping with JavaFX Properties

Tags:

I want to use JavaFX properties for UI binding, but I don't want them in my model classes (see Using javafx.beans properties in model classes). My model classes have getters and setters, and I want to create properties based on those. For example, assuming an instance bean with methods String getName() and setName(String name), I would write

 SimpleStringProperty property = new SimpleStringProperty(bean, "name") 

expecting that property.set("Foobar") would trigger a call to bean.setName. But this doesn't seem to work. What am I missing?

like image 254
warakawa Avatar asked May 07 '14 15:05

warakawa


People also ask

Which JavaBean method is use to set the properties of bean?

This method is called accessor. For example, if property name is firstName, your method name would be setFirstName() to write that property. This method is called mutator.

Are JavaFX properties thread safe?

The JavaFX scene graph, which represents the graphical user interface of a JavaFX application, is not thread-safe and can only be accessed and modified from the UI thread also known as the JavaFX Application thread.

What are binding properties used for in JavaFX?

JavaFX properties are often used in conjunction with binding, a powerful mechanism for expressing direct relationships between variables. When objects participate in bindings, changes made to one object will automatically be reflected in another object. This can be useful in a variety of applications.


1 Answers

The Simple*Property classes are full, standalone implementations of their corresponding Property abstract classes, and do not rely on any other object. So, for example, SimpleStringProperty contains a (private) String field itself which holds the current value of the property.

The parameters to the constructor you showed:

new SimpleStringProperty(bean, "name") 

are:

  • bean: the bean to which the property belongs, if any
  • name: the name of the property

The bean can be useful in a ChangeListener's changed(...) method as you can retrieve the "owning bean" of the property that changed from the property itself. The name can be used similarly (if you have the same listener registered with multiple properties, you can figure out which property changed: though I never use this pattern).

So a typical use of a SimpleStringProperty as an observable property of an object looks like:

public class Person {     private final StringProperty firstName          = new SimpleStringProperty(this, "firstName");      public final String getFirstName() {         return firstName.get();     }      public final void setFirstName(String firstName) {         this.firstName.set(firstName);     }      public StringProperty firstNameProperty() {         return firstName ;     }      // ... other properties, etc } 

The functionality you are looking for: to wrap an existing Java Bean style property in a JavaFX observable property is implemented by classes in the javafx.beans.property.adapter package. So, for example, you could do

StringProperty nameProperty = new JavaBeanStringPropertyBuilder()         .bean(bean)         .name("name")         .build(); 

Calling

nameProperty.set("James"); 

with this setup will effectively cause a call to

bean.setName("James"); 

If the bean supports PropertyChangeListeners, the JavaBeanStringProperty will register a PropertyChangeListener with the bean. Any changes to the name property of the Java Bean will be translated by the JavaBeanStringProperty into JavaFX property changes. Consequently, if the underlying JavaBean supports PropertyChangeListeners, then changes to the bean via

bean.setName(...); 

will result in any ChangeListeners (or InvalidationListeners) registered with the JavaBeanStringProperty being notified of the change.

So, for example, if the Bean class is

import java.beans.PropertyChangeListener; import java.beans.PropertyChangeSupport;  public class Bean {      private String name ;     private final PropertyChangeSupport propertySupport ;      public Bean(String name) {         this.name = name ;         this.propertySupport = new PropertyChangeSupport(this);     }      public Bean() {         this("");     }      public String getName() {         return name ;     }      public String setName(String name) {         String oldName = this.name ;         this.name = name ;         propertySupport.firePropertyChange("name", oldName, name);     }      public void addPropertyChangeListener(PropertyChangeListener listener) {         propertySupport.addPropertyChangeListener(listener);     } } 

Then the following code:

Bean bean = new Bean(); StringProperty nameProperty() = new JavaBeanStringPropertyBuilder()         .bean(bean)         .name("name")         .build(); nameProperty().addListener((obs, oldName, newName) -> System.out.println("name changed from "+oldName+" to "+newName)); bean.setName("James"); System.out.println(nameProperty().get()); 

will produce the output:

name changed from to James  James 

If the JavaBean does not support PropertyChangeListeners, then changes to the bean via bean.setName(...) will not propagate to ChangeListeners or InvalidationListeners registered with the JavaBeanStringProperty.

So if the bean is simply

public class Bean {      public Bean() {         this("");     }      public Bean(String name) {         this.name = name ;     }      private String name ;      public String getName() {         return name ;     }      public void setName(String name) {         this.name = name ;     } } 

The JavaBeanStringProperty would have no way to observe the change, so the change listener would never be invoked by a call to bean.setName(). So the test code above would simply output

James 
like image 156
James_D Avatar answered Sep 18 '22 13:09

James_D