I just came across a strange case when these two are not equivalent. Maybe that is no surprise to you, but it was to me. The case I stumbled upon most likely has to do with ListView internals.
Substitute a := ListView.widthProperty() b := ListCell.prefWidthProperty() and the code that demonstrates the difference: public class Test extends Application { private static class MyCell extends ListCell<String> { public MyCell(ListView<String> lv) { setMaxWidth(Region.USE_PREF_SIZE); // !!! comment out exactly one of the following // !!! two lines to demonstrate the difference prefWidthProperty().bind(lv.widthProperty()); lv.widthProperty().addListener(o -> setPrefWidth(lv.getWidth())); } @Override protected void updateItem(String item, boolean empty) { super.updateItem(item, empty); setGraphic(empty ? null : new TextFlow(new Text(item))); } } @Override public void start(Stage stage) { ListView<String> listView = new ListView<>(); listView.setCellFactory(lv -> new MyCell(lv)); listView.getItems().add("This is a very long line that needs to be wrapped"); StackPane stack = new StackPane(); stack.getChildren().add(listView); Scene scene = new Scene(stack, 200, 100); stage.setScene(scene); stage.show(); } public static void main(String[] args) { launch(args); } } When I run the "bind" version, the text shows up wrapped. When I run the "addListener" version, the text shows up in one long line. Do you think this is a bug in ListView, or is there a reasonable explanation? Just for the record, I consider the "wrapping" behavior to be the correct one. My *speculation* about the possible cause follows: The only case I could come up with when A) b.bind(a) and B) a.addListener(o -> b.set(a.get())) are effectively different is when both of these conditions hold: 1) there is an invalidation listener set on b that causes b to become valid (e.g. calls b.get()); and 2) a is invalidated twice in a row, but recomputes to the same value each time. Now the scenarios for A) and B) differ: Scenario A (b.bind(a)): - a is invalidated for the first time - b's invalidation listener is called for the first time - b is now valid - a is invalidated for the second time - b's invalidation listener is called for the *SECOND* time Scenario B (a.addListener(o -> b.set(a.get()))): - a is invalidated for the first time - b is set to a.get() = x and b's invalidation listener is called for the first time - b is now valid - a is invalidated for the second time - b is set to a.get() = x and b's invalidation listener is *NOT* called for the second time, because b's value did not change In scenario A, b's invalidation listener is called twice, while in scenario B just once. If "weird things" are happening in this invalidation listener, this can result in different behavior. Now, if b is ListCell.prefWidthProperty(), scenario A and scenario B cause it to be invalidated different number of times, which, my *speculation*, could yield different behavior. This is how far I got. Cheers, Tomas