ADDITIONAL SYSTEM INFORMATION :
Tested with:
java version "1.8.0_73"
Java(TM) SE Runtime Environment (build 1.8.0_73-b02)
Java HotSpot(TM) Client VM (build 25.73-b02, mixed mode)
and
java version "1.8.0_202"
Java(TM) SE Runtime Environment (build 1.8.0_202-b08)
Java HotSpot(TM) Client VM (build 25.202-b08, mixed mode, sharing)
Betriebssystemname: Microsoft Windows 10 Enterprise
Betriebssystemversion: 10.0.15063 Nicht zutreffend Build 15063
A DESCRIPTION OF THE PROBLEM :
When you have a property with a single listener attached to it, this listener will initially not be triggered when the implementation of the invalidated method of this property adds a new listener for this property.
Sounds a little bit unreal but it is a real use-case when e.g. using the showingProperty of a javafx stage in combination with the ProgressIndicator.
Since the implementation of the invalidated method of the showingProperty in the javafx window class will force the creation of a new ProgressIndicatorSkin. Due to a memory leak with the progress indicator a fix introduced a new listener for the showing property to make sure that the animator is GB-Collected. So while the showingProperty gets invalidated another component adds a new listener to the showingProperty which will lead to a Generic n the property framework. The Generic gets initialized with the observables value which has actually change to true. After the invalidated method was executed the method fireValueChangedEvent is called. But since the current and the old value are the same the frameworks thinks that the value did not change.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Look at the two sample codes provided.
# Execute the Sample class
# Press "Open Stage" the button
# Close the opened stage
The Sample2 class shows the same problem a little bit simplified without any UI but only the properties. It looks weird to do so but this is what actually happens in the first sample just without the overhead of using the ProgressIndicator.
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
As i have a listener on the showing property, i would expect two lines in my console window:
(testing fails listener) oldValue=<false> newValue=<true>
(testing fails listener) oldValue=<true> newValue=<false>
ACTUAL -
The console window contains just a sinle line
(testing fails listener) oldValue=<true> newValue=<false>
So for changing the property value from false to true the listener was obviously not called.
---------- BEGIN SOURCE ----------
package de.sick.sopas.one.toolkit.fx.samples.apps;
import javafx.application.Application;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.event.ActionEvent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ProgressIndicator;
import javafx.scene.paint.Color;
import javafx.stage.Modality;
import javafx.stage.Stage;
public class Sample extends Application
{
private Stage owner;
private class MyStage extends Stage
{
public MyStage()
{
initModality(Modality.WINDOW_MODAL);
initOwner(owner);
setTitle("Test");
setWidth(100);
setHeight(100);
setMinWidth(100);
setMinHeight(100);
setScene(new Scene(new ProgressIndicator(), Color.WHITE));
}
}
@Override
public void start(Stage primaryStage) throws Exception
{
this.owner = primaryStage;
final Button openButton = new Button("Open Stage");
openButton.setOnAction(this::onOpenStage);
primaryStage.setScene(new Scene(openButton));
primaryStage.show();
}
private void onOpenStage(final ActionEvent e)
{
final MyStage stage = new MyStage();
stage.showingProperty().addListener(new ChangeListener<Boolean>()
{
@Override
public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue)
{
System.out.println("(testing fails listener) oldValue=<" + oldValue + "> newValue=<" + newValue + ">");
}
});
stage.showAndWait();
}
public static void main(String[] args)
{
launch(args);
}
}
package de.sick.sopas.one.toolkit.fx.samples.apps;
import javafx.beans.property.ReadOnlyBooleanProperty;
import javafx.beans.property.ReadOnlyBooleanWrapper;
public class Sample2
{
private final ReadOnlyBooleanWrapper showingProperty = new ReadOnlyBooleanWrapper(false)
{
protected void invalidated()
{
System.out.println("Invalidated");
showingProperty().addListener((observable, oldValue, newValue) -> {
System.out.println("oldValue=<" + oldValue + "> newValue=<" + newValue + ">");
});
};
};
public void setShowing(boolean value)
{
showingProperty.set(value);
}
public ReadOnlyBooleanProperty showingProperty()
{
return showingProperty.getReadOnlyProperty();
}
public static void main(String[] args)
{
final Sample2 s = new Sample2();
s.showingProperty().addListener((observable, oldValue, newValue) -> {
System.out.println("(testing fails listener) oldValue=<" + oldValue + "> newValue=<" + newValue + ">");
});
s.setShowing(true);
}
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
You can either use an InvalidationListener or adding a second ChangeListener to the property before the first change will also solve this issue. This will force the creation of a Generic in the ExpressionHelper instead of a SingleChange and the value will not be overridden anymore.
FREQUENCY : always
Tested with:
java version "1.8.0_73"
Java(TM) SE Runtime Environment (build 1.8.0_73-b02)
Java HotSpot(TM) Client VM (build 25.73-b02, mixed mode)
and
java version "1.8.0_202"
Java(TM) SE Runtime Environment (build 1.8.0_202-b08)
Java HotSpot(TM) Client VM (build 25.202-b08, mixed mode, sharing)
Betriebssystemname: Microsoft Windows 10 Enterprise
Betriebssystemversion: 10.0.15063 Nicht zutreffend Build 15063
A DESCRIPTION OF THE PROBLEM :
When you have a property with a single listener attached to it, this listener will initially not be triggered when the implementation of the invalidated method of this property adds a new listener for this property.
Sounds a little bit unreal but it is a real use-case when e.g. using the showingProperty of a javafx stage in combination with the ProgressIndicator.
Since the implementation of the invalidated method of the showingProperty in the javafx window class will force the creation of a new ProgressIndicatorSkin. Due to a memory leak with the progress indicator a fix introduced a new listener for the showing property to make sure that the animator is GB-Collected. So while the showingProperty gets invalidated another component adds a new listener to the showingProperty which will lead to a Generic n the property framework. The Generic gets initialized with the observables value which has actually change to true. After the invalidated method was executed the method fireValueChangedEvent is called. But since the current and the old value are the same the frameworks thinks that the value did not change.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Look at the two sample codes provided.
# Execute the Sample class
# Press "Open Stage" the button
# Close the opened stage
The Sample2 class shows the same problem a little bit simplified without any UI but only the properties. It looks weird to do so but this is what actually happens in the first sample just without the overhead of using the ProgressIndicator.
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
As i have a listener on the showing property, i would expect two lines in my console window:
(testing fails listener) oldValue=<false> newValue=<true>
(testing fails listener) oldValue=<true> newValue=<false>
ACTUAL -
The console window contains just a sinle line
(testing fails listener) oldValue=<true> newValue=<false>
So for changing the property value from false to true the listener was obviously not called.
---------- BEGIN SOURCE ----------
package de.sick.sopas.one.toolkit.fx.samples.apps;
import javafx.application.Application;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.event.ActionEvent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ProgressIndicator;
import javafx.scene.paint.Color;
import javafx.stage.Modality;
import javafx.stage.Stage;
public class Sample extends Application
{
private Stage owner;
private class MyStage extends Stage
{
public MyStage()
{
initModality(Modality.WINDOW_MODAL);
initOwner(owner);
setTitle("Test");
setWidth(100);
setHeight(100);
setMinWidth(100);
setMinHeight(100);
setScene(new Scene(new ProgressIndicator(), Color.WHITE));
}
}
@Override
public void start(Stage primaryStage) throws Exception
{
this.owner = primaryStage;
final Button openButton = new Button("Open Stage");
openButton.setOnAction(this::onOpenStage);
primaryStage.setScene(new Scene(openButton));
primaryStage.show();
}
private void onOpenStage(final ActionEvent e)
{
final MyStage stage = new MyStage();
stage.showingProperty().addListener(new ChangeListener<Boolean>()
{
@Override
public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue)
{
System.out.println("(testing fails listener) oldValue=<" + oldValue + "> newValue=<" + newValue + ">");
}
});
stage.showAndWait();
}
public static void main(String[] args)
{
launch(args);
}
}
package de.sick.sopas.one.toolkit.fx.samples.apps;
import javafx.beans.property.ReadOnlyBooleanProperty;
import javafx.beans.property.ReadOnlyBooleanWrapper;
public class Sample2
{
private final ReadOnlyBooleanWrapper showingProperty = new ReadOnlyBooleanWrapper(false)
{
protected void invalidated()
{
System.out.println("Invalidated");
showingProperty().addListener((observable, oldValue, newValue) -> {
System.out.println("oldValue=<" + oldValue + "> newValue=<" + newValue + ">");
});
};
};
public void setShowing(boolean value)
{
showingProperty.set(value);
}
public ReadOnlyBooleanProperty showingProperty()
{
return showingProperty.getReadOnlyProperty();
}
public static void main(String[] args)
{
final Sample2 s = new Sample2();
s.showingProperty().addListener((observable, oldValue, newValue) -> {
System.out.println("(testing fails listener) oldValue=<" + oldValue + "> newValue=<" + newValue + ">");
});
s.setShowing(true);
}
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
You can either use an InvalidationListener or adding a second ChangeListener to the property before the first change will also solve this issue. This will force the creation of a Generic in the ExpressionHelper instead of a SingleChange and the value will not be overridden anymore.
FREQUENCY : always
- duplicates
-
JDK-8295342 ChangeListener not triggered when adding a new listener in invalidated method - samples fail again on openjfx 17+
- Closed
- relates to
-
JDK-8295342 ChangeListener not triggered when adding a new listener in invalidated method - samples fail again on openjfx 17+
- Closed