将JavaFX标签绑定到StringProperty

时间:2018-06-20 17:05:32

标签: javafx bind

在我的JavaFX应用程序中,有一个标签,我想使用另一个定义服务器功能的类的StringProperty更新。

在服务器类中,我定义了一个StringProperty,如下所示:

public class eol_server {

     private StringProperty serverMessagesProperty;
     etc..

我有一个根据要求返回StringProperty的方法:

    public StringProperty serverMessagesProperty() {
    if (serverMessagesProperty == null) {
        serverMessagesProperty = new SimpleStringProperty();
    }
    return serverMessagesProperty;
}

在主gui类中,使用Start()方法构建Scene,然后实例化一个新的服务器对象。之后,通过将其绑定到服务器对象的StringProperty来更新场景图中的标签之一:

public void start(Stage primaryStage) {
    ...set up all the gui components
     primaryStage.show();
     dss = new eol_server();
     lbl_dssMessage.textProperty().bind(dss.getServerMessagesProperty());
}

当我运行应用程序时,将按预期呈现场景,并且将lbl_dssMessage文本设置为在eol_server的构造函数中设置的值。但是,从那时起,绑定就无法正常工作,尽管我有一些操作会更新dss对象的StringProperty,但它们并没有更新GUI中的标签。

以下是生成场景的精简版本的完整文件:

public class eol_gui extends Application {

private static eol_server dss = null;
private static Stage primaryStage;


/**
 * Application Main Function
 * @param args
 */
public static void main(String[] args) {
    launch(args);
}

/**
 * @param stage
 */
private void setPrimaryStage(Stage stage) {
    eol_gui.primaryStage = stage;
}

/**
 * @return Stage for GUI
 */
static public Stage getPrimaryStage() {
    return eol_gui.primaryStage;
}

/* (non-Javadoc)
 * @see javafx.application.Application#start(javafx.stage.Stage)
 */
@Override
public void start(Stage primaryStage) {

    setPrimaryStage(primaryStage);

    BorderPane root = new BorderPane();
    Label lbl_dssMessage = new Label("initialized");
    HBox topMenu = new HBox();
    topMenu.getChildren().add(lbl_dssMessage);

    eol_supervisor_I config1 = new eol_supervisor_I();

    // Build Scene
    root.setStyle("-fx-background-color: #FFFFFF;");
    root.setTop(topMenu);
    primaryStage.setScene(new Scene(root, 300, 50));
    primaryStage.show();

    dss = new eol_server();
    dss.getServerMessagesProperty().addListener(new ChangeListener<Object>() {
                        @Override 
                        public void changed(ObservableValue<?> o,Object oldVal,Object newVal)
                        {
                            //Option 1: lbl_dssMessage.setText(newVal.toString());
                        }
    });
    //Option 2
    lbl_dssMessage.textProperty().bind(dss.serverMessagesProperty);
}

}

如您所见,我已经尝试了bind方法和一个更改侦听器。在所有情况下,除了在服务器服务线程中运行的那些情况外,似乎bind和侦听器都在工作。由于不在JavaFX应用程序主线程上,因此它们抛出IllegalStateException。如何安全正确地从服务向主线程交换消息?

服务器在以下类中定义,该类旨在运行独立于JavaFX主线程的服务。但是我想在线程之间交换信息以显示状态。我正在尝试避免在进行服务器连接和数据交换时GUI挂起。

public class eol_server {

public StringProperty serverMessagesProperty;

public eol_server() {
    /* Create Scripting Environment */

    serverMessagesProperty().set("Establishing Debug Server Environment");
    connect();

}
public boolean connect() {  
    serverMessagesProperty().set("Creating Server");
    ConnectService connectService = new ConnectService();
    connectService.start();
    return false;
}

public StringProperty serverMessagesProperty() {
    if (serverMessagesProperty == null) {
        serverMessagesProperty = new SimpleStringProperty();
    }
    return serverMessagesProperty;
}

public StringProperty getServerMessagesProperty() {
    return serverMessagesProperty;
}

private class ConnectService extends Service<Void> {

    @Override
    protected void succeeded() {

    }

    @Override
    protected void failed() {

    }

    @Override
    protected void cancelled() {

    }

    @Override
    protected Task<Void> createTask() {
        return new Task<Void>() {
            @Override
            protected Void call() throws Exception {
                serverMessagesProperty().set("Connecting....");
                Thread.sleep(5000);
                // DEMO: uncomment to provoke "Not on FX application thread"-Exception:
                // connectButton.setVisible(false);
                serverMessagesProperty().set("Waiting for server feedback");
                Thread.sleep(5000);
                return null;
            }
        };
    }
}

private class DisconnectService extends Service<Void> {

    @Override
    protected void succeeded() {

    }

    @Override
    protected void cancelled() {

    }

    @Override
    protected Task<Void> createTask() {
        return new Task<Void>() {
            @Override
            protected Void call() throws Exception {
                updateMessage("Disconnecting....");
                serverMessagesProperty().set("Disconnecting....");
                Thread.sleep(5000);
                updateMessage("Waiting for server feedback.");
                serverMessagesProperty().set("Waiting for server feedback.");
                Thread.sleep(5000);

                return null;
            }
        };
    }

}

}

预先感谢

JW

2 个答案:

答案 0 :(得分:0)

我似乎有一个解决方案,该解决方案源于我在runlater()Platform方法上阅读的内容。另请参阅此处的讨论

Multi-Threading error when binding a StringProperty

更改我的侦听器以通过runLater调用消息更新似乎可以解决多线程问题。是的,我希望它不是立竿见影的,但它将非常接近立竿见影且足够好。我理解JavaFX要求您不要弄乱场景图的Application线程值/节点等,但这是JavaFX库中相当复杂的部分。

这是为我工作的听众

// Get new DSS session active
    dss = new eol_server();

    dss.serverMessagesProperty.addListener(new ChangeListener<Object>() {
        @Override
        public void changed (ObservableValue<?> observable, Object oldValue, Object newValue) {
            Platform.runLater(() -> lbl_dssMessage.setText(newValue.toString()));
        }
    });
}

很高兴进一步讨论在其他选项不起作用的情况下为何可行,这也欢迎其他更优雅的建议。

答案 1 :(得分:0)

JavaFX Node文档中阅读以下段落。

  

Node对象可以在任何线程上构造和修改,只要   它们尚未附加到正在显示的窗口中的场景。一个   应用程序必须将节点附加到这样的场景或在   JavaFX应用程序线程。

解决问题的正确且实用的方法是

dss.serverMessagesProperty.addListener((observable, oldValue, newValue) -> Platform.runLater(() -> lbl_dssMessage.setText(newValue));