Search code examples
javajavafx-8property-binding

JavaFX 8 MapProperty unbind does not work?


Can anyone explain me if this is a bug or am I missing something in JavaFX MapProperty binding?

Scenario: Two MapProperty instances - master and child.

  1. At first we bind child to master
  2. Then we store some values in master
  3. Unbind child from master
  4. Clear child
  5. both instances are empty - why?
  6. Store some values in child
  7. both instances contain the same values - why?

Code:

public static void main(String[] args) {

    MapProperty<String, Object> master = new SimpleMapProperty<String, Object>(
            FXCollections.observableMap(new HashMap<String, Object>()));
    MapProperty<String, Object> child = new SimpleMapProperty<String, Object>(
            FXCollections.observableMap(new HashMap<String, Object>()));

    child.bind(master);

    master.put("k1", "v1");

    System.out.println("Java version: " + System.getProperty("java.version"));
    System.out.println("OS version  : " + System.getProperty("os.name") + " - " + System.getProperty("os.arch"));
    System.out.println("------------");
    System.out.println("master: " + master);
    System.out.println("child : " + child);

    // Isn't this supposed to stop change listener ?????
    child.unbind();
    child.clear();

    System.out.println("------------");
    System.out.println("master: " + master);
    System.out.println("child : " + child);

    child.put("k2", "v2");

    System.out.println("------------");
    System.out.println("master: " + master);
    System.out.println("child : " + child);

}

Output:

run:
Java version: 1.8.0_45
OS version  : Windows 7 - amd64
------------
master: MapProperty [value: {k1=v1}]
child : MapProperty [bound, invalid]
------------
master: MapProperty [value: {}]
child : MapProperty [value: {}]
------------
master: MapProperty [value: {k2=v2}]
child : MapProperty [value: {k2=v2}]
BUILD SUCCESSFUL (total time: 0 seconds)

Solution

  • The value of a MapProperty is an ObservableMap, not the content of the ObservableMap.

    Executing this code

    MapProperty<String, Object> master = new SimpleMapProperty<String, Object>(
            FXCollections.observableMap(new HashMap<String, Object>()));
    MapProperty<String, Object> child = new SimpleMapProperty<String, Object>(
            FXCollections.observableMap(new HashMap<String, Object>()));
    
    ObservableMap<String, Object> childMap = child.get();
    ObservableMap<String, Object> masterMap = master.get();
    
    System.out.println("before binding: " + ((childMap == masterMap) ? "childMap == masterMap" : "childMap != masterMap"));
    
    child.bind(master);
    
    childMap = child.get();
    masterMap = master.get();
    
    System.out.println("after binding: " + ((childMap == masterMap) ? "childMap == masterMap" : "childMap != masterMap"));
    
    child.unbind();
    System.out.println("after unbinding: " + ((childMap == masterMap) ? "childMap == masterMap" : "childMap != masterMap"));   
    

    shows that after binding, the ObservableMap in both child and main is the same object, because the property wraps the map and not its content:

    before binding: childMap != masterMap
    after binding: childMap == masterMap
    after unbinding: childMap == masterMap
    

    To bind the content of the map, use bindContent instead. Executing

    MapProperty<String, Object> master = new SimpleMapProperty<String, Object>(
            FXCollections.observableMap(new HashMap<String, Object>()));
    MapProperty<String, Object> child = new SimpleMapProperty<String, Object>(
            FXCollections.observableMap(new HashMap<String, Object>()));
    
    child.bindContent(master);
    
    master.put("k1", "v1");
    
    System.out.println("Java version: " + System.getProperty("java.version"));
    System.out.println("OS version  : " + System.getProperty("os.name") + " - " + System.getProperty("os.arch"));
    System.out.println("------------");
    System.out.println("master: " + master);
    System.out.println("child : " + child);
    
    child.unbindContent(master);
    child.clear();
    
    System.out.println("------------");
    System.out.println("master: " + master);
    System.out.println("child : " + child);
    
    child.put("k2", "v2");
    
    System.out.println("------------");
    System.out.println("master: " + master);
    System.out.println("child : " + child);
    

    gives the following result:

    Java version: 1.8.0_45
    OS version  : Windows 7 - amd64
    ------------
    master: MapProperty [value: {k1=v1}]
    child : MapProperty [value: {k1=v1}]
    ------------
    master: MapProperty [value: {k1=v1}]
    child : MapProperty [value: {}]
    ------------
    master: MapProperty [value: {k1=v1}]
    child : MapProperty [value: {k2=v2}]