JavaFX Programmatic POJO Expression Bindings (Part III)

Update: If you need help, have comments or want to contribute to the ongoing effort please visit the JFXtras GitHub site and file an issue

Collection/Map Binding

In Part I and Part II of the JavaFX POJO bindings I introduced possible mechanisms for binding JavaFX control properties to POJOs without having to create all the boilerplate code for the fields of your POJOs. It offered a way to bind POJO fields to JavaFX controls, but excluded POJO fields that contain collections or maps. Since then I have refined the adapter to include bindings that support bindings to/from POJO fields that are of type List, Set, and Map to/from ObservableList, ObservableSet, and ObservableMap. That means you can now bind your POJO fields that carry one of these collection/map types to a ListView, TableView, etc. items and/or their corresponding selections. The source code for the BeanPathAdapter can be downloaded from the JFXtras.org website (or you can view the latest source: BeanPathAdapter/BeanPathAdapterTestApp).

Usage

One of the features of the BeanPathAdapter is that we can bind different JavaFX collection/map types (ObservableList/ObservableSet/ObservableMap) with a different POJO field collection/map types (List/Set/Map). To demo this we’ll stick with the same “Person” example that we used in Part I/II, but this time we’ll add a few fields to demonstrate collection binding. Our POJO will contain Sets that we will use to bind to/from a ListView‘s items (ObservableList) and it’s selectionModel (MultipleSelectionModel). We’ll use the allLanguages/allHobbies fields as the item values for a ListView and the languages/hobbies fields as the selection values of the ListView:

public static class Person {
	...
	private Set languages;
	private Set hobbies;
	private Set allLanguages;
	private Set allHobbies;
	...
	public Set getLanguages() {
		return languages;
	}
	public void setLanguages(Set languages) {
		this.languages = languages;
	}
	public Set getHobbies() {
		return hobbies;
	}
	public void setHobbies(Set hobbies) {
		this.hobbies = hobbies;
	}
	public Set getAllLanguages() {
		return allLanguages;
	}
	public void setAllLanguages(Set allLanguages) {
		this.allLanguages = allLanguages;
	}
	public Set getAllHobbies() {
		return allHobbies;
	}
	public void setAllHobbies(Set allHobbies) {
		this.allHobbies = allHobbies;
	}
}
public static class Hobby {
	private String name;
	private String description;

	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getDescription() {
		return description;
	}
	public void setDescription(String description) {
		this.description = description;
	}
}

The languages will contain simple String values and the hobbies will use a more complex Hobby POJO where we will supply a path to Hobby#name that the adapter will use as the display value of each item in the example ListView. As far as the JavaFX control is concerned it will always contain values of the same (or a coercible counterpart) type as the Hobby#name field.

Person person = new Person();
BeanPathAdapter<Person> personPA = new BeanPathAdapter<>(person);

// initial items will never be seen because the
// POJO bean allHobbies will take presidence
ListView llv = new ListView<>(
		FXCollections.observableArrayList(new String[]{
		"Language Item Never Shown"}));
// bind the items to/from Person#allLanguages
personPA.bindContentBidirectional("allLanguages", null, String.class,
		llv.getItems(), String.class, null);
// we need to pass in the SelectionModel of
// llv.getSelectionModel() so that updates to the
// ReadOnlyUnbackedObservableList can be updated
personPA.bindContentBidirectional("languages", null,
		String.class, llv.getSelectionModel().getSelectedItems(),
		String.class,
		llv.getSelectionModel());

// initial items will never be seen because the
// POJO bean allHobbies will take presidence
ListView hlv = new ListView<>(
		FXCollections.observableArrayList(new String[]{
		"Hobby Item Never Shown"}));
// bind the items to/from Person#allHobbies
personPA.bindContentBidirectional("allHobbies", "name", Hobby.class,
		hlv.getItems(), String.class, null);
// we need to pass in the SelectionModel of
// hlv.getSelectionModel() so that updates to the
// ReadOnlyUnbackedObservableList can be updated
personPA.bindContentBidirectional("hobbies", "name",
		Hobby.class, hlv.getSelectionModel().getSelectedItems(),
		String.class,
		hlv.getSelectionModel());

Now that the items in the ListView are bound to/from the Person#allLanguages/Person#allHobbies fields and the selections for those items are bound to the Person#languages/Person#hobbies fields changes in the controls will be reflected in the corresponding POJO fields. Due to the nature of POJO field change capture (as well as the collection/map) you may need to call personPA.setBean(person); after changes are made DIRECTLY to POJO collections/maps to see them within the JavaFX controls they are bound to. One of the nice things about the decoupled nature of collection/map bindings is that the JavaFX control will always have a primitive value (or a primitive boxed type). This allows you to add/put/remove a primitive value to/from the control while the adapter handles any instantiation/conversion needed to get the primitive value reflected in your POJO’s collection/map. For example, the sample application has a “Add Hobby” button. When clicked it takes the String value of the hobby name entered and calls listView.getItems().add(textField.getText());. The adapter handles the instantiation of a new Hobby, calls newHobby.setName(nameFromTextField), and ultimately calls person.getAllHobbies().add(newHobby). None of which requires any additional steps by the implementer.

Summary of Usage

Here are a few usage examples:

  1. Binding bean fields to multiple JavaFX control properties of different types:
    // Assuming "age" is a double field in person we can bind it to a 
    // Slider#valueProperty() of type double, but we can also bind it
    // to a TextField#valueProperty() of type String. 
    Person person = new Person();
    BeanPathAdapter<Person> personPA = new BeanPathAdapter<>(person);
    Slider sl = new Slider();
    TextField tf = new TextField();
    personPA.bindBidirectional("age", sl.valueProperty());
    personPA.bindBidirectional("age", tf.valueProperty());
    
  2. Binding beans within beans:
    // Binding a bean (Person) field called "address" that contains another 
    // bean (Address) that also contains a field called "location" with a 
    // bean (Location) field of "state" (the chain can be virtually endless
    // with all beans being instantiated along the way when null).
    Person person = new Person();
    BeanPathAdapter<Person> personPA = new BeanPathAdapter<>(person);
    TextField tf = new TextField();
    personPA.bindBidirectional("address.location.state", tf.valueProperty());
    
  3. Binding non-primitive bean paths to JavaFX control properties of the same non-primitive type:
    // Assuming "role" is a "Role" field in a "Person" class we can 
    // bind it to a ComboBox#valueProperty() of the same type. The "Role" 
    // class should override the "toString()" method in order to show a 
    // meaningful selection value in the example ComboBox.
     Role r1 = new Role();
     Role r2 = new Role();
     r1.setName("Administrator");
     r2.setName("User");
     ComboBox<Role> cb = new ComboBox<>();
     cb.getItems().addAll(r1, r2);
     Person person = new Person();
     BeanPathAdapter<Person> personPA = new BeanPathAdapter<>(person);
     personPA.bindBidirectional("role", cb.valueProperty(), Role.class);
    
  4. Binding collections/maps fields to/from observable collections/maps (i.e. items in a JavaFX control):
    // Assuming "allLanguages" is a collection/map field in person we can
    // bind it to a JavaFX observable collection/map
    Person person = new Person();
    BeanPathAdapter<Person> personPA = new BeanPathAdapter<>(person);
    ListView<String> lv = new ListView<>();
    personPA.bindContentBidirectional("allLanguages", null, String.class, lv.getItems(), String.class, null, null);
    
  5. Binding collections/maps fields to/from observable collections/maps selections (i.e. selections in a JavaFX control):
    // Assuming "languages" is a collection/map field in person we can
    // bind it to a JavaFX observable collection/map selections
    Person person = new Person();
    BeanPathAdapter<Person> personPA = new BeanPathAdapter<>(person);
    ListView<String> lv = new ListView<>();
    personPA.bindContentBidirectional("languages", null, String.class, lv.getSelectionModel().getSelectedItems(), String.class, lv.getSelectionModel(), null);
    
  6. Binding collection/map fields to/from observable collections/maps selections using an items from another observable collection/map as a reference (i.e. selections in a JavaFX control that contain the same instances as what are in the items being selected from):
    // Assuming "languages" and "allLanguages" are a collection/map 
    // fields in person we can bind "languages" to selections made from
    // the items in "allLanguages" to a JavaFX observable collection/map 
    // selection
    Person person = new Person();
    BeanPathAdapter<Person> personPA = new BeanPathAdapter<>(person);
    ListView<String> lv = new ListView<>();
    personPA.bindContentBidirectional("languages", null, String.class, lv.getSelectionModel().getSelectedItems(), String.class, lv.getSelectionModel(), "allLanguages");
    
  7. Binding complex bean collection/map fields to/from observable collections/maps selections and items (i.e. selections in a JavaFX control that contain the same bean instances as what are in the items being selected):
    // Assuming "hobbies" and "allHobbies" are a collection/map 
    // fields in person and each element within them contain an 
    // instance of Hobby that has it's own field called "name" 
    // we can bind "allHobbies" and "hobbies" to the Hobby "name"s 
    // for each Hobby in the items/selections (respectively) to/from
    // a ListView wich will only contain the String name of each Hobby
    // as it's items and selections
    Person person = new Person();
    BeanPathAdapter<Person> personPA = new BeanPathAdapter<>(person);
    ListView<String> lv = new ListView<>();
    // bind items
    personPA.bindContentBidirectional("allHobbies", "name", Hobby.class, lv.getItems(), String.class, null, null);
    // bind selections that reference the same instances within the items
    personPA.bindContentBidirectional("hobbies", "name", Hobby.class, lv.getSelectionModel().getSelectedItems(), String.class, lv.getSelectionModel(), "allHobbies");
    
  8. Binding bean collection/map fields to/from multiple JavaFX control observable collections/maps of the same type (via bean collection/map):
    Person person = new Person();
    Hobby hobby1 = new Hobby();
    hobby1.setName("Hobby 1");
    Hobby hobby2 = new Hobby();
    hobby2.setName("Hobby 2");
    person.setAllHobbies(new LinkedHashSet<Hobby>());
    person.getAllHobbies().add(hobby1);
    person.getAllHobbies().add(hobby2);
    BeanPathAdapter<Person> personPA = new BeanPathAdapter<>(person);
    ListView<String> lv = new ListView<>();
    personPA.bindContentBidirectional("allHobbies", "name", Hobby.class, 
    		lv.getItems(), String.class, null, null);
    ListView<String> lv2 = new ListView<>();
    personPA.bindContentBidirectional("allHobbies", "name", Hobby.class, 
    		lv2.getItems(), String.class, null, null);
    
  9. Binding bean collection/map fields to/from multiple JavaFX control observable collections/maps of the same type (via JavaFX control observable collection/map):
    // When the bean collection/map field is empty/null and it is
    // bound to a non-empty observable collection/map, the values
    // of the observable are used to instantiate each item bean
    // and set the item value (Hobby#setName in this case)
    Person person = new Person();
    final ObservableList<String> oc = FXCollections.observableArrayList("Hobby 1", "Hobby 2", "Hobby 3");
    BeanPathAdapter<Person> personPA = new BeanPathAdapter<>(person);
    ListView<String> lv = new ListView<>(oc);
    personPA.bindContentBidirectional("allHobbies", "name", Hobby.class, 
    		lv.getItems(), String.class, null, null);
    ListView<String> lv2 = new ListView<>(); // <-- notice that oc is not passed
    personPA.bindContentBidirectional("allHobbies", "name", Hobby.class, 
    		lv2.getItems(), String.class, null, null);
    
  10. Switching beans:
    // Assuming "age" is a double field in person...
    final Person person1 = new Person();
    person1.setAge(1D);
    final Person person2 = new Person();
    person2.setAge(2D);
    final BeanPathAdapter<Person> personPA = new BeanPathAdapter<>(person1);
    TextField tf = new TextField();
    personPA.bindBidirectional("age", tf.valueProperty());
    Button btn = new Button("Toggle People");
    btn.setOnMouseClicked(new EventHandler<MouseEvent>() {
    	@Override
    	public void handle(MouseEvent event) {
    		// all bindings will show relevant person data and changes made
    		// to the bound controls will be reflected in the bean that is 
    		// set at the time of the change
    		personPA.setBean(personPA.getBean() == person1 ? person2 : person1);
    	}
    });
    
  11. Date/Calendar binding:
    // Assuming "dob" is a java.util.Date or java.util.Calendar field 
    // in person it can be bound to a java.util.Date or 
    // java.util.Calendar JavaFX control property. Example uses a
    // jfxtras.labs.scene.control.CalendarPicker
    final Person person = new Person();
    final BeanPathAdapter<Person> personPA = new BeanPathAdapter<>(person);
    CalendarPicker calendarPicker = new CalendarPicker();
    personPA.bindBidirectional("dob", calendarPicker.calendarProperty(), Calendar.class);
    
  12. TableView items binding:
    // Assuming "name"/"description" are java.lang.String fields 
    // in Hobby and "hobbies" is a List/Set/Map in Person
    final Person person = new Person();
    final BeanPathAdapter<Person> personPA = new BeanPathAdapter<>(person);
    TableView<Hobby> table = new TableView<>();
    TableColumn<Hobby, String> nameCol = new TableColumn<>("Hobby Name");
    nameCol.setMinWidth(100);
    nameCol.setCellValueFactory(
            new PropertyValueFactory<Hobby, String>("name"));
    TableColumn<Hobby, String> descCol = new TableColumn<>("Hobby Desc");
    descCol.setMinWidth(100);
    descCol.setCellValueFactory(
            new PropertyValueFactory<Hobby, String>("description"));
    table.getColumns().addAll(nameCol, descCol);
    personPA.bindContentBidirectional("hobbies", null, String.class,
    	table.getItems(), Hobby.class, null, null);
    
  13. Listening for global changes:
    final Person person = new Person();
    final BeanPathAdapter<Person> personPA = new BeanPathAdapter<>(person);
    // use the following to eliminate unwanted notifications
    // personPA.removeFieldPathValueTypes(FieldPathValueType.BEAN_CHANGE, ...)
    personPA.fieldPathValueProperty().addListener(
     	new ChangeListener<FieldPathValue>() {
     	@Override
     	public void changed(
     		final ObservableValue<? extends FieldPathValue> observable,
     		final FieldPathValue oldValue, final FieldPathValue newValue) {
     			System.out.println("Value changed from: " + oldValue
     				+ " to: " + newValue);
     		}
     	});
    

In Action

Here is some screen shots of the demo application in action (can be downloaded from the JFXtras.org website or view the latest snapshot from: BeanPathAdapter/BeanPathAdapterTestApp):

About ugate
UGate is a fully open source solution for a do-it-yourself configurable indoor/outdoor security system. It's built with Arduino Uno32 (ChipKIT) + JavaFX and a number of other leading software/hardware technologies. Our goal is to provide individuals with access to the most popular hardware sensor technology used in industrial today. Our attempt is to bridge the gap between the hardware and software in an extendable and intuitive GUI interface that is free and open to the public. Visit our code page at: http://ugate.org

89 Responses to JavaFX Programmatic POJO Expression Bindings (Part III)

  1. St Clair Clarke says:

    Awesome implementation of JavaFX using POJO. I was searching for something like this.

    POGO (Groov) even reduced the boiler-plate more.

    What remains is the mechanism where when the model is set, the UI updates

    • ugate says:

      Thanks! I have been thinking about possible solutions regarding the capture of POJO field changes using something like ASM, Javassist, etc. Maybe a provider can be used for this so you could plugin a DI framework implementation (something like JPA’s @PostPersist, @PostLoad, etc.)? Any suggestions as far as implementation is concerned? I was hoping to keep it simple as possible to keep from degrading performance and memory usage while maintaining a high degree of flexibility. Any input would be much appreciated!

  2. tbeernot says:

    Very impressive!

    I agree with St Clair Clake that an indirection is needed to make this real world usable; if you have a screen which is wired (bind) to an entity, then replacing that entity (set model) should be easy. In your example this would mean replacing “Person 1” with “Person 2” with just one API call.

    • ugate says:

      I agree. The current workaround for updating the JavaFX display when changes are made to an underlying model/bean/pojo is to call personPA.setBean(person); This will cause all the bound JavaFX controls (including collections/maps) to update their values with values contained in your model/bean/pojo. So, in a sense it is one API call (in the example app you can test this by clicking the “Set Person’s Name” button and switching back and forth from the Person 1/Person 2/Person 3 selection which is using personPA.setBean(person)). It’s just a cumbersome task for someone to know all the scenarios in which to make the call. It’s also somewhat inefficient in respect that you are forcing a traversal of all bound method handles rather just the ones that pertain to the field(s) that actually have a changed model/bean/pojo value :/

  3. St Clair Clarke says:

    I am thinking about the implementation mechanism.

    In the meantime, it would be nice if we have snippets of code demonstrating each usage of BeanPathAdapter, as compared to the single test application provided. For someone learning how to use BeanPathAdapter, it is somewhat daunting to go through all the code.

    For example, a snippet demonstrating the usage of Map binding, listview binding etc.

    Let me know what you thing,

    • ugate says:

      St Clair Clarke, thanks for you input! I have updated the post to include some usage examples. Let me know if it makes sense or not.

      • St Clair Clarke says:

        Great UGate! I think everyone who finds this blog will appreciate it more for the snippets.

        Question: Do you think this is enterprise ready at the moment? Its simplicity is just marvelous. More than ever, this allows me to use my POJO/POGO (Groovy) objects for previous RCP applications to be used in my JavaFX port without any adjustment to my model!

    • ugate says:

      I’ve been testing it at least every few days since it’s inception as I’m integrating it into the ugate.org project. A few other people have been testing it as well. There could be a lot more testing conducted though. One good thing is that most of the inner workings are reused by each of the available binding options. So, there should only be slight variations between them. Also, all of the underlying binding operations are utilizing the existing Bindings API in JavaFX. There are currently no outstanding bugs reported and the only new feature requests have been 1. A dependency injection capability that will notify the adapter when any bound bean fields have changed (workaround is when any changes are made directly to the bound bean requires a call to setBean) 2. Have the BeanPathAdapter extend ReadOnlyObjectProperty so that changes to any bound property within the adapter can be globally observed for changes.

  4. Pingback: JavaFX links of the week, August 6 // JavaFX News, Demos and Insight // FX Experience

  5. Hi, it’s great that you’ve implemented support for Listish variables. Thanks. But, I have tried binding two controls to a single List (allHobbies from your example), and – unless I’m doing something wrong – the second control doesn’t get any beans, if you know what I mean. 😉 This is the line I’ve added into your start() method, just to try it:
    HBox anotherHobbyBox=beanTF("allHobbies", "hobbies", "name", Hobby.class, 0, ListView.class, null, HOBBY_OVERWRITE);

    • ugate says:

      Slavko, I think the demo may be a little convoluted. I just added some examples to the post. Can you try them out and let me know if you’re still experiencing issues? Also, make sure your getting the latest source code from the links provided in the post.

    • Yes, unfortunately I’m still experiencing the same issue when binding two ObservableLists (ListView.getItems()) to a single List. I’ve used the latest code you provided, and modified your example with this code:
      ListView<String> anotherHobbyBox=new ListView();
      personPA.bindContentBidirectional("allHobbies", "name", Hobby.class, anotherHobbyBox.getItems(), String.class, null, null);

      Naturally, I’ve also added this ListView into personBox. Still get only the “SHOULD NOT APPEAR” hobby in the second list. Could you please try it yourself? BTW, thanks for the examples, they’re really helpful.

      • ugate says:

        Slavko, I tried binding multiple ObservableLists to a single bean collection/map and experienced the same results that you are getting. I updated the BeanPathAdapter.java source to reflect the fix for this and tested it using the following example (I also updated the post to include the use case):

        public class ATest extends Application {
        	
        	public static void main(final String[] args) {
        		Application.launch(ATest.class, args);
        	}
        
        	@Override
        	public void start(Stage primaryStage) throws Exception {
        		VBox root = new VBox(10);
        		Person person = new Person();
        		Hobby hobby1 = new Hobby();
        		hobby1.setName("Hobby 1");
        		Hobby hobby2 = new Hobby();
        		hobby2.setName("Hobby 2");
        		person.setAllHobbies(new LinkedHashSet<Hobby>());
        		person.getAllHobbies().add(hobby1);
        		person.getAllHobbies().add(hobby2);
        		BeanPathAdapter<Person> personPA = new BeanPathAdapter<>(person);
        		ListView<String> lv = new ListView<>();
        		personPA.bindContentBidirectional("allHobbies", "name", Hobby.class, 
        				lv.getItems(), String.class, null, null);
        		ListView<String> lv2 = new ListView<>();
        		personPA.bindContentBidirectional("allHobbies", "name", Hobby.class, 
        				lv2.getItems(), String.class, null, null);
        		root.getChildren().addAll(lv, lv2);
        		primaryStage.setScene(new Scene(root));
        		primaryStage.show();
        	}
        
        }
        

        Let me know if this works for you. BTW, good catch!

      • Yes, It’s OK now. Thanks! I’m sorry I’m not able to help fixing problems, not just reporting them. 🙂

      • ugate says:

        If you really feel inspired to do so you can always contact the JFXtras group and contribute. Either way I’m glad to help! I also updated the BeanPathAdapter source again (same link in post) so it will allow you to bypass the Hobby instantiation in the example I gave you in favor a simpler one using the ObservableList on the control (see example usage #8).

      • Oh, I feel inspired, all right, but I’m just not that good of a programmer (yet 🙂 ). If I were, I would have created something like this long time ago.

        This latest addition is great, and it certainly has it’s use cases.

  6. Pingback: JavaFX Programmatic POJO Expression Bindings (Part II) « UGate

  7. Sorry to bother you again, but I have another problem. This time it’s the bindBidirectional(java.lang.String fieldPath, javafx.beans.property.Property property, java.lang.Class propertyType) method. Namely, I’m using some kind of DatePicker control (custom made, JFX doesn’t have one yet) that has selectedDateProperty property of ObjectProperty<java.util.Date> type.
    Now, if I understand this method correctly, this should work just fine:
    personPA.bindBidirectional("dateOfBirth", dateOfBirthDatePckr.selectedDateProperty(), java.util.Date.class); But it doesn’t. Any thoughts?

    • ugate says:

      Originally, I only accounted for primitive types (and their Java object counterparts) for the final bean field binding types, but Date/Calendar make sense as well. I just added support for them in the updated link to the BeanPathAdapter source. Can you give it a try and let me know if it works for you?

    • Yes, I think now it’s OK. For some reason, my DatePicker control is initially not showing the right date value (even thou its selectedDate property contains a proper value), but i suppose that’s because something’s wrong on my side, not yours. Thank you for great work on mitigating some of the JavaFX’s serious drawbacks.

    • Tom says:

      JFX won’t be having one for quite some time. To fill that gap, JFXtras has a CalendarPicker.

  8. Tom says:

    The popup issue has been fixed by now. It has been months ago. Seem we need to update the stuff on the website. But Christian’s picker is nice. From what I remember he had no interest in maintaining it; it was just a finger excercise.

  9. Tom says:

    Ah, I should have permission, but we haven’t gotten around to handing those out. It’s also a matter of roles; I code, someone else does the website, another the build process. Teamwork you know. It’s ok.

  10. Hello! It’s me again… 🙂

    Remember when I said that my DatePicker is not showing the correct Date value when bound via BeanPathAdapter, but that I thought it had something to do with the Control itself? Well, I couldn’t figure out why is this happening, so I tried with the JFXtras’ CalendarPicker control, because I didn’t want to bother you. But the problem persisted, so I arrived at the only logical conclusion: I was wrong, problem is not in the Control.

    As it turns out, setBean() method has no effect on the Date – typed values. Why – I don’t know, even thought I’ve stepped through the PA code line by line, via debugger, I couldn’t figure it out.

    When I do this:

    Person p2=new Person();
    p2.setName("Emilly Rose");
    p2.setPassword("passKey");
    SimpleDateFormat sdf=new SimpleDateFormat("dd.MM.yyyy.");
    p2.setDateOfBirth(sdf.parse("30.09.1990."));

    personBPA= new BeanPathAdapter(p2);
    personBPA.bindBidirectional("name", nameTxt.textProperty());
    personBPA.bindBidirectional("password", passwordPwd.textProperty());
    personBPA.bindBidirectional("dateOfBirth", dobDtp.selectedDateProperty(), Date.class);
    //note that dobDtp Control is of type DatePicker

    all is good, the correct date is displayed. But if I try to change the bean afterwards via setBean(), nothing happens in the DatePicker control.

    I figured you’d want to know about this (plus, I desperately need PA). 🙂 If you need a messy ad-hock NetBeans project created to test this use case, you can find it here: https://dl.dropbox.com/u/94867347/BeanPathAdapter_DatePicker_test.zip

    Cheers!

    • ugate says:

      Hey Slavko, I fixed the issue. All the other “human readable” primitive types can easily be converted to a String and back, but a Calendar/Date cannot be easily converted from a String value. So, I had to handle this case separately. When I did this, I should have had the same check for “isDirty” when setting the Object value on the FieldProperty. I’ve since add the check and tested your same application and it seems to be working now using the latest BeanPathAdapter from the link in the post (as well as the source in JFXtras). Let me know if you encounter any other issues. Thanks for your help in testing it!

    • Well, I’m glad if you see it that way. The way I see it, I’m using something that’s entirely your creation, and than bitching when something’s not working the way I need it to work. 😆 But thanks anyway, it’s my pleasure. You’re doing a great job.

  11. Pingback: Convert POJO bean property to a JavaFX 2 Property | scekics

  12. Hi,

    First of all I’d like to thank you A LOT for saving me hours of work.

    I have a question (which might be stupid, as I don’t really know all the concepts of the JavaFX beans yet).

    I have a TableView embedded in a class. Whenever I select an item in this TableView, it opens a new Scene where I can edit the info contained in the selected POJO. How can I use your PathAdapter so any change in the external Scene will be reflected in the TableView? My main problem compared to your example is that my object displayed in the TableView is not a primitive type.

    I can provide more info if you are willing to help!

    Cheers

    • ugate says:

      Hi Gabriel,
      I’m glad to hear that the adapter is of use to you! Can you provide me with more details of what you’re trying to accomplish? Maybe a code snippet? I’m a little confused by your comment about an object being displayed in the TableView not being a primitive (or the Java Object counterpart). Doesn’t the item that’s displayed need to be human readable? If you’re referring to the Observable collection that the TableView uses it should get resolved to a primitive using the collection item path you provide to the adapter. I have not thoroughly tested the use of a TableView using the adapter. There may be some changes that I need to make to get it working for what you’re trying to do.

      • Hi,

        I was confused with the way TableView and ListView works with data, I’ve found a workaround by extending the class, it had nothing to do with your BeanAdapter – which is sick btw!

        I still feel something is missing to make it perfect. Do you think adding a SimpleObjectProperty for the bean inside the PathAdapter would make sense, so that the bean inside it can be changed dynamically too! (use case: select an object (which does not have any SimpleProperty binding and is serializable) in a TableView, then in another view everything will be refreshed using the freshly assigned bean in the PathAdapter).

      • Sorry, I couldn’t edit my previous comment. Is there a way to bind the selected item of a ComboBox (which is a custom object) to a field with the same type in another class (the bean of the PA) – like this: bindBidirectional(“brand”, brandComboBox.getSelectionModel().selectedItemProperty(), Brand.class);

      • ugate says:

        Hi Gabriel,

        I added an example using TableView. Care to show what you did to extend the adapter to get the TableView to work the way you wanted to? If there is something missing from the adapter that others could benefit from I would like to add it.

        You should be able to bind your ComboBox selection like this:

        personPA.bindBidirectional("brand", brandComboBox.valueProperty(), Brand.class);
        
      • I don’t know why, but binding the valueProperty of a ComboBox does not work. Whenever the bean changes, the changes of the Brand object are not reflected in the PathAdapter. I also try to bind the Brand field to a SimpleObjectProperty, and it didn’t work. I’ll try to find what’s going on.

      • ugate says:

        Hi Gabriel,
        Can you post sample code/app for your Brand ComboBox?

      • Here you go: http://dl.dropbox.com/u/6740409/BrandComboBoxTest.zip

        I suspect I’m doing things wrong. BeanPathAdapter has been renamed OldBeanPathAdapter because I don’t really know which version I have (depending if you modified it in JFXtras, but I can’t find it in the git repo on Github… seems like it was in an old branch but it’s not in the master tree anymore). I also added a few tweaks to get passed the problem I encountered (having a beanProperty to notify when the bean is changed for instance).

    • ugate says:

      Hi Gabriel,

      It looks like there was a bug that prevented the use of a POJO as the final binding path instead of a typical String, Number, Date, etc. I overlooked the possibility of overriding “toString” on the POJO and using that as the display value in the control. I just updated the BeanPathAdapter to include support for this and tested it against your example app. It seems to be working now. I added an example as the 3rd item in the “Summary of Usage” section of this post. Let me know if this works for you.

      Ironically, I had another feature request to add a global listener to listen for any changes made on the bean. This has also been included in the updated BeanPathAdapter with an example as the 13th item in the “Summary of Usage” section of this post.

      I’m working toward getting the adapter back into the master branch of the JFXtras project. It had to be taken out due to it’s dependency on Java 7, but I’m in the process of back porting it to Java 6 until JFXtras requires Java 7. I will respond to this post once it’s back in the master branch.

    • ugate says:

      The BeanPathAdapter is back in the JFXtras labs project version 2.2r5+. Sorry for the delay.

  13. Pingback: Confluence: Miniprosjekter

  14. Mark says:

    Thank you so much for making this available. I’m a newbie when it comes to javafx and having some trouble getting it to work with a TableView. Could you perhaps provide an example of how to bind a List to a TableView.using your library?

    • Mark says:

      Some characters were removed. I meant to ask how to bind a List of Person to a TableView.

      • ugate says:

        Mark,

        I added an example that demonstrates using the adapter to bind to TableView items. Let me know if you encounter any issues or see any missing features.

  15. John says:

    Hello,

    This looks brilliant, although I have just got the jfxtras 2.2-r3 and your BeanPathAdapter doesnt seem to be in the labs project. When do you think you will be adding it to JFXtras, or any other project, as I would prefer to use it within Maven?

    • ugate says:

      We had to temporarily remove it from jfxtras because it requires java 7 method handles. I will make a port for it to use the older reflection api until jfxtras requires java 7. I will do that within the next week or so and reply to this post once it’s back in the repo.

    • ugate says:

      The BeanPathAdapter is back in the JFXtras labs project version 2.2r5+. Sorry for the delay.

  16. Pingback: Confluence: Fritt om fag

  17. pablo says:

    Hi!
    I have the same problem of Javier but with ChoiceBox:

    Gabriel Féron says:
    October 18, 2012 at 9:04 am
    I don’t know why, but binding the valueProperty of a ComboBox does not work. Whenever the bean changes, the changes of the Brand object are not reflected in the PathAdapter. I also try to bind the Brand field to a SimpleObjectProperty, and it didn’t work. I’ll try to find what’s going on.

    I’m follow your suggestion:
    personPA.bindBidirectional(“brand”, brandComboBox.valueProperty(), Brand.class);
    but does not work for me

    Is there any problem with this kind of elements?

    regards,
    Pablo

    • ugate says:

      Hi Pablo, Can you post an example code (preferably zipped app) where the ChoiceBox/ComboBox is not working? Also, can you compare it to the demo app to make sure you are binding the value property correctly?

      • pablo says:

        Hi!
        Thanks for the answer.
        The code I’m using is very simple.
        The relevant parts are below:
        @FXML // fx:id=”temaPub1″
        private ChoiceBox temaPub1; // Value injected by FXMLLoader
        revistaPA = new BeanPathAdapter(publicacionPer);
        revistaPA.bindBidirectional(“tema1”,temaPub1.valueProperty(),TemaPublicacion.class);

        In the cases where I bind with a String field all works fine, like in this case
        revistaPA.bindBidirectional(“nombreRevista”,nombreRevista.valueProperty(),String.class);

        but the problem is when the fields references a POJO.

        Do you know where is the ploblem?

        Regards,
        Pablo

    • ugate says:

      In your example is “tema1” a TemaPublication instance? “tempa1” should be a human readable type like String, Number, Date, etc.

      • Pablo says:

        Thanks again for your response.
        Yes, tema1 is an instance of TemaPublicacion. Overriding the toString() method we use this type in the ChoicheBox without problems.
        This is an unsupported feature in the path adapter?
        Regards,
        Pablo

    • ugate says:

      Hi Pablo,

      I just updated the BeanPathAdapter to include support for using an actual POJO as the final path value (versus human readable types like String, Number, Date, etc.). Obviously it’s a good idea to override the toString method of the POJO to show a meaningful selection value to the end user. I added an example in the “Summary of Usage” of this post (3rd example). Let me know if this works for you.

      • Nice, thanks! It’s funny I just implemented an invalidation mechanism in the PathAdapter myself yesterday, waiting for your (obviously better) implementation.

      • I’d suggest adding a fieldPathValue.set(new FieldPathValue(fullPath, col.size())); in syncCollectionValues so that you propagate change when synchronizing the collections. Otherwise, you are stil a life savior.

      • ugate says:

        Hi Gabriel,

        Great suggestion! I updated the BeanPathAdapter with a new constructor to the FieldPathValue:

        FieldPathValue(final String path, final Object bean,
        				final Object value,
        				final ListChangeListener.Change listChange,
        				final SetChangeListener.Change setChange,
        				final MapChangeListener.Change mapChange)
        

        … and a call in the FieldProperty#syncCollectionValues(Object, boolean, javafx.collections.ListChangeListener.Change, javafx.collections.SetChangeListener.Change, javafx.collections.MapChangeListener.Change):

        fieldPathValue.set(new FieldPathValue(fullPath, getBean(),
        						getDirty(), listChange, setChange, mapChange));
        

        Let me know if this works for you (I updated the demo app to include this feature while dumping the POJO data to the TextArea).

  18. pablo says:

    Thanks!
    Now is working for me…

    Regards,
    Pablo

  19. Amar says:

    Nice work!

    Is it feasible to enhance this implementation to work with POJOs that already do provide propertyChangeListener notification?

    Regards,
    Amar

  20. Gabriel says:

    Hi ugate,

    Is there a way to unbind the bounded collections previously linked with bindContentBidirectionnal ?

    Thanks.

  21. Atif Hussain says:

    Hi,

    Thanks for the great work. I am new to JavaFx and am implementing a game using the technology. This post was of great interest to me.

    I have a simple “Player” POJO and I have bound the String property to a JavaFX textProperty of the control text. It seems to pick up the initial value from the Pojo, but upon setting the pojo fields like player1.setName(“Tom”), the GUI nevers picks up on this change.

    I might be missing something here. I set it like this.

    p.bindBidirectional(“test”, playerStakesDisplay.textProperty());

    where
    Text playerStakesDisplay = TextBuilder.create()
    .x(x_coord)
    .y(y_coord + 30)
    .build();

    Many thanks. Also, where can i find the latest jar. I have looked at the website, but I might not have the latest version.

    • ugate says:

      Hi Atif,

      At the moment setting bound POJO fields directly (such as player1.setName(“Tom”)) requires one additional step to refresh the GUI. The easiest of which is to call setBean(player1) on the adapter. The reason for this additional step is due to the lack of native a Java dependency injection instrumentation, java agent, or byte-code manipulation (like raw ASM, Javassist, CGlib, etc.). So, in other words, there is no way to intercept the call made in your POJO setters/getters in order to notify the adapter/JavaFX of the changes. An alternative is to only set values in the JavaFX controls or call the adapters setBean method when you want to refresh the control values. I have been contemplating adding a pluggable interface to the adapter that will allow you to use a 3rd party library that will do the DI, but recently performance optimizations have been on the forefront. As always, suggestions, patches, etc. are welcome at anytime 🙂

      The latest jar can be found here (version 2.2-r5 and greater, currently 2.2r5-SNAPSHOT).

  22. Julian. says:

    Hi ugate,
    I’m having trouble trying to bind a field with fieldpath like “fooOther.foo.etc”.
    I think there is a bug when you try to progress to the next child field/bean in the path chain.

    In line 1277:
    // progress to the next child field/bean in the path chain
    final String nextFieldPath = fieldPath.substring(fieldPath.indexOf(fieldNames[1]));

    If a field name is a substring of a previous fieldn ame in the path chain you get the wrong nextFieldPath (and you will probably get an IllegalArgumentException at some point).

    • ugate says:

      Hi Julian,
      Good catch! I updated the repo with the following fix. Let me know if it works for you:

      final String nextFieldPath = fieldPath.replaceFirst(fieldNames[0] + PATH_SEPARATOR, "");
      
  23. marcus says:

    Hi, first of all: very nice work.
    I have a question: I need to display a List of (JPA) beans, but how to bind them to a TableView, as they’re not part of a bean?
    In your example I retrieve all possible hobbies, without relationship to persons.
    I could do that with a dummy bean containing a List property and set the retrieved List to that instance.
    That would be a workaround, but it’s not beautiful.
    Further on: and as far as I can see the List, if using the workaround, is displayed, but modifications in the TableView are not written back to the List’s beans.
    Do I have to write a TableCell implementation, or is there an other, more simple way?

  24. ugate says:

    Hi Marcus,
    Correct me if I’m wrong, but It looks like there is two parts to the issue you’re experiencing…

    The first part is that in order to initialize the adapter you need a root bean instead of a Collection/Map of beans. My initial thought to solve this is to create a new static method in the adapter that accepts a Collection/Map that will return a BeanPathAdapter<BeanReference> where BeanReference is a new class that wraps the Collection/Map and binds the BeanReference#getSource()/BeanReference#setSource(java.util.Collection). That way you would not have to create the root bean yourself. Would that satisfy your needs? If you know of a better solution I’m open for suggestions/patches.

    I think the second part is related to the scenario described in the PropertyValueFactory which is looking for hobbyNameProperty() (in our example). Because it’s not generated via the adapter it in turn falls back on the use of a ReadOnlyObjectWrapper which does not propagate value changes. Maybe the adapter can have a method and/or feature that can generate it’s own implementation of PropertyValueFactory that will return the path to the internally generated FieldProperty? Other suggestions?

  25. marcus says:

    Sorry for not yet replying to your reply on my comment in update Part III. It will follow.
    Until then there’s another issue I would like to address: localization.
    The actual version formats Dates hardcoded in an English format (“yyyy-MM-dd’T’HH:mm:ssz”) which is not acceptable in my country.
    My suggestion: add a customizable a “DateFormat” property.
    Something like this:

    private static DateFormat dateFormat;

    public static DateFormat getDateFormat() {

    if(BeanPathAdapter.dateFormat==null)
    BeanPathAdapter.dateFormat = DateFormat.getDateInstance(DateFormat.SHORT, Locale.getDefault());;

    return dateFormat;
    }

    public static void setDateFormat(DateFormat dateFormat) {

    BeanPathAdapter.dateFormat = dateFormat;
    }

    After adding this property in the BeanPathAdapter and replacing the constant SDF in the nested class FieldStringConverter’s coerceToString it seems to work fine.

    The same procedure should be implemented for Number formats.

    Question: does the FieldStringConverter has to be static? The suggested properties should be able to vary from instance to instance.

    Greets, Marcus

  26. dani says:

    Hi,

    How would you show a list of Persons (not the part III person but the part II person which has fildes name, address and age) in a TableView which has the columns name, country, age, and the country column data would be bound to the “person.address.location.country” path?

    If in our root bean I have a List persons, with bindContentBidirectional I can:

    show the name and the age columns using
    bindContentBidirectional(“persons”, null, …..)
    and the PropertyValueFactory for both columns

    or show the country column using
    bindContentBidirectional(“persons”, “address.location”, …..)
    and the PropertyValueFactory for the country column

    But I don’t know how use the bindContentBidirectional to show all the columns. Is that possible?

    —-

    Regarding the fact that it isn’t possible to listen to changes in POJO fields, I have implemented in each POJO a mechanism that will notify changes to a list of Listeners. One of those listeners would be the BeanPathAdapter who, thanks to your contribution, will notify JavaFX as well. I modified your BeanPathAdapter to automatically add himself as a listener to the POJO in the setBean Method. Furthermore, as a Listener, I have had to add an OnChange Method to the Adapter that is called when the POJO changes. Here is not really clear for me how to follow to tell the Adapter that a field of the POJO has changed. Now, to do that, I am using

    getRoot().setBean(getBean());

    And it works, but this means “the entire POJO has changed” instead of a “field in the POJO has changed”, and all the bound JavaFX properties will be updated instead of the property that has changed.

    Another doubt I have is if this worth using the JavaFX Properties rather than this mechanism I have implemented in terms of performance.

    Thanks a lot in advance and thanks for your contribution.

    Dani

    • ugate says:

      Hi Dani,

      Sorry, but I’m not sure I’m understanding your issue with binding to a TableView. Can you elaborate on what is not working? Maybe provide an example? Are you using the technique described in bullet point #12?

      That is one of the disadvantages of the Java language. In order to make the binding more dynamic when changes are made in the POJO requires some extra help via byte-code instrumentation or some other comparable mechanism. I’ve been contemplating adding an extension for use with some common frameworks like Spring/standard DI/ASM/etc. but I’d hate to tie those type of dependencies back to the adapter itself :/ If you have some ideas for a more viable (non-intrusive) solution let me know.

  27. Julian says:

    I’m getting an IllegalArgumentException almost randomly.
    When I’m selecting entities from a TableView and binding them using BeanPathAdapter, from time to time I get this exception (is very strange, I can’t reproduce the exception doing the same steps, sometime happens an others not).
    Someone has gotten the same issue?


    Exception in thread "JavaFX Application Thread" java.lang.IllegalArgumentException: Unable to set object value: 929 on numero
    at presentacion.formularios.commons.BeanPathAdapter$FieldProperty.set(BeanPathAdapter.java:1602)
    at presentacion.formularios.commons.BeanPathAdapter$FieldProperty.setDerived(BeanPathAdapter.java:1563)
    at presentacion.formularios.commons.BeanPathAdapter$FieldProperty.setTarget(BeanPathAdapter.java:2242)
    at presentacion.formularios.commons.BeanPathAdapter$FieldBean.setBean(BeanPathAdapter.java:910)
    at presentacion.formularios.commons.BeanPathAdapter$FieldBean.setParentBean(BeanPathAdapter.java:930)
    at presentacion.formularios.commons.BeanPathAdapter$FieldBean.setBean(BeanPathAdapter.java:906)
    at presentacion.formularios.commons.BeanPathAdapter$FieldBean.setParentBean(BeanPathAdapter.java:930)
    at presentacion.formularios.commons.BeanPathAdapter$FieldBean.setBean(BeanPathAdapter.java:906)
    at presentacion.formularios.commons.BeanPathAdapter.setBean(BeanPathAdapter.java:604)
    at presentacion.formularios.commons.FormControllerABMC.elementoSelecionado(FormControllerABMC.java:173)
    at presentacion.formularios.commons.FormControllerABMCSubClass.elementoSelecionado(FormControllerABMCSubClass.java:137)
    at presentacion.formularios.secretaria.tramites.FormABMCSolicitudPrestamo.elementoSelecionado(FormABMCSolicitudPrestamo.java:222)
    at presentacion.formularios.commons.FormControllerBusqueda$1.changed(FormControllerBusqueda.java:92)
    at com.sun.javafx.binding.ExpressionHelper$SingleChange.fireValueChangedEvent(ExpressionHelper.java:196)
    at com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(ExpressionHelper.java:100)
    at javafx.beans.property.ReadOnlyObjectWrapper$ReadOnlyPropertyImpl.fireValueChangedEvent(ReadOnlyObjectWrapper.java:195)
    at javafx.beans.property.ReadOnlyObjectWrapper.fireValueChangedEvent(ReadOnlyObjectWrapper.java:161)
    at javafx.beans.property.ObjectPropertyBase.markInvalid(ObjectPropertyBase.java:130)
    at javafx.beans.property.ObjectPropertyBase.set(ObjectPropertyBase.java:163)
    at javafx.scene.control.SelectionModel.setSelectedItem(SelectionModel.java:101)
    at javafx.scene.control.TableView$TableViewSelectionModel$1.invalidated(TableView.java:1561)
    at com.sun.javafx.binding.ExpressionHelper$SingleInvalidation.fireValueChangedEvent(ExpressionHelper.java:155)
    at com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(ExpressionHelper.java:100)
    at javafx.beans.property.ReadOnlyIntegerWrapper$ReadOnlyPropertyImpl.fireValueChangedEvent(ReadOnlyIntegerWrapper.java:195)
    at javafx.beans.property.ReadOnlyIntegerWrapper.fireValueChangedEvent(ReadOnlyIntegerWrapper.java:161)
    at javafx.beans.property.IntegerPropertyBase.markInvalid(IntegerPropertyBase.java:130)
    at javafx.beans.property.IntegerPropertyBase.set(IntegerPropertyBase.java:163)
    at javafx.scene.control.SelectionModel.setSelectedIndex(SelectionModel.java:67)
    at javafx.scene.control.TableView$TableViewArrayListSelectionModel.updateSelectedIndex(TableView.java:2324)
    at javafx.scene.control.TableView$TableViewArrayListSelectionModel.select(TableView.java:1999)
    at javafx.scene.control.TableView$TableViewArrayListSelectionModel.clearAndSelect(TableView.java:1970)
    at com.sun.javafx.scene.control.behavior.TableCellBehavior.simpleSelect(TableCellBehavior.java:247)
    at com.sun.javafx.scene.control.behavior.TableCellBehavior.doSelect(TableCellBehavior.java:196)
    at com.sun.javafx.scene.control.behavior.TableCellBehavior.mousePressed(TableCellBehavior.java:118)
    at com.sun.javafx.scene.control.skin.SkinBase$4.handle(SkinBase.java:335)
    at com.sun.javafx.scene.control.skin.SkinBase$4.handle(SkinBase.java:329)
    at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:64)
    at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:217)
    at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:170)
    at com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:38)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:37)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:92)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:35)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:92)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:35)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:92)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:35)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:92)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:35)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:92)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:35)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:92)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:35)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:92)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:35)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:92)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:35)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:92)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:35)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:92)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:35)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:92)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:35)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:92)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:35)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:92)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:35)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:92)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:35)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:92)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:35)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:92)
    at com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:53)
    at com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:33)
    at javafx.event.Event.fireEvent(Event.java:171)
    at javafx.scene.Scene$MouseHandler.process(Scene.java:3328)
    at javafx.scene.Scene$MouseHandler.process(Scene.java:3168)
    at javafx.scene.Scene$MouseHandler.access$1900(Scene.java:3123)
    at javafx.scene.Scene.impl_processMouseEvent(Scene.java:1563)
    at javafx.scene.Scene$ScenePeerListener.mouseEvent(Scene.java:2265)
    at com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:250)
    at com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:173)
    at java.security.AccessController.doPrivileged(Native Method)
    at com.sun.javafx.tk.quantum.GlassViewEventHandler.handleMouseEvent(GlassViewEventHandler.java:292)
    at com.sun.glass.ui.View.handleMouseEvent(View.java:528)
    at com.sun.glass.ui.View.notifyMouse(View.java:922)
    at com.sun.glass.ui.gtk.GtkApplication._runLoop(Native Method)
    at com.sun.glass.ui.gtk.GtkApplication$3$1.run(GtkApplication.java:82)
    at java.lang.Thread.run(Thread.java:722)
    Caused by: java.lang.NullPointerException
    at com.sun.javafx.binding.ExpressionHelper$Generic.fireValueChangedEvent(ExpressionHelper.java:367)
    at com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(ExpressionHelper.java:100)
    at javafx.beans.property.ObjectPropertyBase.fireValueChangedEvent(ObjectPropertyBase.java:123)
    at javafx.beans.property.ObjectPropertyBase.markInvalid(ObjectPropertyBase.java:130)
    at javafx.beans.property.ObjectPropertyBase.set(ObjectPropertyBase.java:163)
    at javafx.beans.property.ObjectProperty.setValue(ObjectProperty.java:84)
    at com.sun.javafx.binding.BidirectionalBinding$TypedGenericBidirectionalBinding.changed(BidirectionalBinding.java:466)
    at com.sun.javafx.binding.ExpressionHelper$SingleChange.fireValueChangedEvent(ExpressionHelper.java:196)
    at com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(ExpressionHelper.java:100)
    at javafx.beans.property.ObjectPropertyBase.fireValueChangedEvent(ObjectPropertyBase.java:123)
    at presentacion.formularios.commons.BeanPathAdapter$FieldProperty.postSet(BeanPathAdapter.java:1619)
    at presentacion.formularios.commons.BeanPathAdapter$FieldProperty.set(BeanPathAdapter.java:1598)
    ... 88 more

  28. Jose Figueroa says:

    i have the following problem:
    My pojo has a field of type Long called bankId, an my combobox has a value of type Bank. Bank is a class with id:Long, and Name:String. i want to bind bakId field of the pojo with combobox.valueProperty().getId(). The code could be as follow.

    aspAdapter.bindBidirectional(“bankId”, Bindings.when(Bindings.isNull(textEntidadDeposito.valueProperty())).then(0)
    .otherwise(textEntidadDeposito.getValue().getId()), Long.class);
    or

    aspAdapter.bindBidirectional(“bankId”, Bindings.select(textEntidadDeposito.valueProperty(), “id”));

    or

    aspAdapter.bindBidirectional(“bankId”, textEntidadDeposito.valueProperty(), “id”);

    Can you add support for this??

    • ugate says:

      Hi Jose,

      Unfortunately, adding Expression support is a tricky endeavor that will take quite a bit of effort from what I can tell. I’ll see what I can do, but I can’t promise anything :/ In the meantime, if you find a way to add it that works you can submit a patch or we can see about setting you up as a JFXtras contributor so you can make the updates as needed.

  29. Ryan says:

    I added an issue to the GitHub project. Would you be able to have a look at it?

    https://github.com/JFXtras/jfxtras-labs/issues/63

  30. Marco says:

    Hi,
    i’m studying the code but need some advices. I don’t find on the net anything to answer this question. How can I use BeanPathAdapter if the filed is not primitive? I mean if the POJO field is Double, can i override a specific method to implement a conversion to primitive?

    Thanks in advance

    MArco

  31. Hi there,
    Thanks for the great work! I was wondering if the BeanPathAdapter would be able to handle the following scenario:
    Often times my POJO’s will have some form of validation when setting properties. The set method will either throw an exception, or return some form of a report indicating whether the property change was allowed. For example, I might not allow a Person to have the same id as someone else in my address book. Lets say I have a table with an id column. I will bind the id field of my Person to this column. When the id column is edited, my Person’s setId() method might reject this change.I would like to react to this rejection by showing a message to the user. I can obviously check whether the input is valid before on edit commit, but often times my validation is much more complex than this simple example, and this would require duplication of my validation for every property. Could you maybe recommend any way in which this could be handled? Any help would be appreciated!

    • ugate says:

      Hi Johann,

      Validation seems like a good enhancement idea. The best thing you can do at the moment add it here (or if you’d like to contribute you can join the JFXtras group and add the feature yourself).

      In the meantime, a work around would to be to use the example in this blog titled “Listening for global changes”. This will notify you when any change is made to the underlying bean. You can filter it for validation using the observable FieldPathValue which contains the path that changed, the bean where the change was made, the current value of the change and the type (FieldPathValueType). There is one for the old value and another for the new value. So, if the new value is invalid you can revert to the old value that’s provided.

  32. Ugate, I been trying to make heads and tails of this.

    https://gist.github.com/casz/06137cf272734637003d

    So I have a TableView
    With an observableList sellers
    Which have 3 attributes I’d like to be editable.
    Name,Phone,Money.
    My thinking something along the lines is to
    for (Seller seller : sellers) {
    final BeanPathAdapter sellerPA = new BeanPathAdapter(seller);

    }

    This is the same from some of my other JavaFX TableViews and some even include editable combobox with Seller.

    • Finally got it to work… Must have been a brain block after spending so much time trying to figure out how to get this to work…

      Anyway
      Having trouble getting localDate to work.
      Caused by: java.lang.IllegalArgumentException: Unable to get accessor return instance for MethodHandle()LocalDate using class java.time.LocalDate.
      at jfxtras.labs.scene.control.BeanPathAdapter$FieldHandle.deriveValueFromAccessor(BeanPathAdapter.java:3223)
      at jfxtras.labs.scene.control.BeanPathAdapter$FieldProperty.setDerived(BeanPathAdapter.java:1777)
      at jfxtras.labs.scene.control.BeanPathAdapter$FieldProperty.(BeanPathAdapter.java:1753)
      at jfxtras.labs.scene.control.BeanPathAdapter$FieldBean.performOperation(BeanPathAdapter.java:1316)
      at jfxtras.labs.scene.control.BeanPathAdapter$FieldBean.performOperation(BeanPathAdapter.java:1186)
      at jfxtras.labs.scene.control.BeanPathAdapter.bindContentBidirectional(BeanPathAdapter.java:451)

  33. Michael says:

    I just wanted to say, FANTASTIC WORK! This is a great library.

  34. Tom D says:

    The link to github seems not to work?
    Thanks for the very interesting post!
    Tom

  35. SourceCode says:

    Hey, I have been trying to use this adapter for a while and it has been pretty great so far.

    But i need some help. I have a class say A, and it has a list of element of class B. Each of this B classes has objects of class C and D. Now both C and D has a string, which i want to bind to a TableView. I cant find a way to do this.