how to share data between two stages in javafx - javafx

I have two fxml window login and main window.Their respective controllers are given below:
public class MainwindowController extends Stage implements Initializable {
#FXML private Button Send;
#FXML private TextField txtBcast;
#FXML private ListView listviewUsers;
#FXML Label lblDisplayName;
/**
* Initializes the controller class.
* #param url
* #param rb
*/
#Override
public void initialize(URL url, ResourceBundle rb) {
ObservableList<String> chat =FXCollections.observableArrayList ("default");
listviewUsers.setItems(chat);
}
public void setLblName(String msg){
lblDisplayName.setText(msg);
}
#FXML public void ActionSend(ActionEvent e){
send();
txtBcast.setText("");
}
private void send() {
if (txtBcast.getText().isEmpty())
return;
// chatManager.sendPublicMsg(format,txtBcast.getText());
}
/**
*
* #param e
* #throws Exception
*/
#FXML public void ActionUserSelected( MouseEvent e) throws Exception{
// String lineRest = e.getActionCommand();
if(e.getClickCount()==2)
{
if(!listviewUsers.getSelectionModel().isEmpty())
{
String str=(String)listviewUsers.getSelectionModel().getSelectedItem();
Parent main= FXMLLoader.load(getClass().getResource("/letschat/fxwindows/Usertab.fxml"));
Scene scene = new Scene(main);
Stage stage = new Stage();
stage.setTitle(str);
stage.setScene(scene);
stage.show();
}
else { JOptionPane.showMessageDialog(null, "Oops! it seems you are trying to click the list view"); }
}
//Stage pstage = (Stage)listUsers.getScene().getWindow();
//pstage.close();
}
}
And
public class LoginwindowController extends Stage implements Initializable {
#FXML private LoginwindowController loginwindowController;
#FXML private MainwindowController mainwindowController;
#FXML private Button btnSignIn;
#FXML private TextField txtDisplayName;
#FXML private ToggleGroup Gender;
#FXML private ComboBox comboStatus;
/**
* Initializes the controller class.
* #param url
* #param rb
*/
#Override
public void initialize(URL url, ResourceBundle rb) {
ObservableList<String> items =FXCollections.observableArrayList ("Online","Offline");
comboStatus.setItems(items);
writeToTextField();
}
public void writeToTextField() {
String username = System.getProperty("user.name");
txtDisplayName.setText(""+ username);
}
#FXML protected void ActionSignIn(ActionEvent event) throws Exception {
mainwindowController.setLblName(txtDisplayName.getText());
InetAddress addr = InetAddress.getLocalHost();
if(addr.isLoopbackAddress())
{
Dialogs.create().message("Oops! It seems you are not connected to any network..\n :(").showError();
}
else{
start(txtDisplayName.getText());// start chat manager
Parent root= FXMLLoader.load(getClass().getResource("/letschat/fxwindows/Mainwindow.fxml"));
Scene scene = new Scene(root);
Stage stage = new Stage();
stage.setTitle("LetsChat-Welcome "+ txtDisplayName.getText());
// Context.getInstance().setDisplayName(txtDisplayName.getText());
stage.setScene(scene);
stage.getIcons().add(new Image("/letschat/images/logo.png"));
Stage pstage = (Stage)btnSignIn.getScene().getWindow();
stage.show();
pstage.close();
}
}
private void start(String name) {
try {
ChatManager ic = new ChatManager(name);
ic.start();
} catch (Exception ex) {
Dialogs.create().message( "Could not start the chat session\nCheck that there no other instances running :(").showError();
}
}
}
I want the label lblDisplayName in main window updated with text from txtDisplay Name in login window when user clicks signin button.can someone help how to do so..soon plz

There are various ways to do this, in your case Login will create the other stage so an easy way is to create a new FXML loader (variable name: myLoader) and, if you want to pass the username of the user as constructor argument you can use myLoader.setControllerFactory and as return:
return clazz == MyController.class ? new MyController(userName) : null;
MyController is the name of the Controller where you want to read the username
If you want to use set methods, with getController you get the controller instance and call the set method (e.g, myController.setUsername());
To create a custom FXML
FXMLLoader myLoader = new FXMLLoader(<if you use relative paths, here you should pass the position);
remember to call the load() because the URI overload is static. (i.e, use getResourceAsStream).
If your application is large and complex, you could use EventBus (which I prefer everywhere..)

I'm not sure I completely understand the relationship between the two controllers, and which are the FXML files that correspond to the controllers, but it looks like the LoginWindowController loads MainWindow.fxml, and I'm guessing that MainWindowController is the controller for MainWindow.fxml.
In that case, you can just do
#FXML protected void ActionSignIn(ActionEvent event) throws Exception {
InetAddress addr = InetAddress.getLocalHost();
if(addr.isLoopbackAddress())
{
Dialogs.create().message("Oops! It seems you are not connected to any network..\n :(").showError();
}
else{
start(txtDisplayName.getText());// start chat manager
FXMLLoader loader = new FXMLLoader(getClass().getResource("/letschat/fxwindows/Mainwindow.fxml"));
Parent root= loader.load();
MainWindowController mainWindowController = loader.getController();
mainWindowController.setLblName(txtDisplayName.getText());
Scene scene = new Scene(root);
Stage stage = new Stage();
stage.setTitle("LetsChat-Welcome "+ txtDisplayName.getText());
// Context.getInstance().setDisplayName(txtDisplayName.getText());
stage.setScene(scene);
stage.getIcons().add(new Image("/letschat/images/logo.png"));
Stage pstage = (Stage)btnSignIn.getScene().getWindow();
stage.show();
pstage.close();
}
}

Related

Modify an object in a parent controller in JavaFX

here is my problem :
I have 3 FXML files with each controller assigned.
ExplorerPage.FXML : layout used to show a gridpane with undefined object number inside.
ExplorerNoteController.FXML : the object (StackPane) inserted in the gridpane.
NoteHoverMenuController.FXML : When the user moves the mouse on the previous object it shows options to update or delete the object.
To resume :
ExplorerPage.FXML -> ExplorerNoteController.FXML -> NoteHoverMenuController.FXML
Gridpane -> Stackpane -> Borderpane(2)
What I want to do is when I click on the "delete" button in the NoteHoverMenuController.FXML, this remove the current object from the gridpane (located in ExplorerPage.FXML). But I have no clue to do this. I tried binding with properties, static methods, .. but no satisfaying results.
This is my code :
ExplorerPage controller
public class ExplorerPage implements Initializable {
private Connection conn;
// LOAD ALL NOTES
private List<Note> gridNotes = new LinkedList<Note>();
JFXRadioButton alertButton = new JFXRadioButton(); // TEMP
StackPane spaneExpNote; // StackPane used to switch front/back pane (overlay)
private byte j = 0;
private byte k = 0;
#FXML
private GridPane FXMLNotesPane = new GridPane();
#Override
public void initialize(URL fxmlFileLocation, ResourceBundle resources) {
try {
// LOAD DATAS FROM DB W/ PARAM FOLDER
conn = DBInitialize.getInstance();
DAO<Note> findAllNotes = new NoteDAO(conn);
gridNotes = findAllNotes.findAll("Orange");
// STACKPANES CONSTRUCTION
for(int i = 0; i < gridNotes.size(); i++)
{
if (j>2) {
j=0;
k++;
}
FXMLLoader loader = new FXMLLoader(getClass().getResource("/fr/cryption/view/ExplorerNote.fxml"));
spaneExpNote = (StackPane) loader.load();
ExplorerNoteController controller = loader.getController();
controller.setData(gridNotes.get(i).getTitleNote(), gridNotes.get(i).getContentNote(), gridNotes.get(i).getDateNote());
FXMLNotesPane.add(spaneExpNote,j,k);
j++;
}
ExplorerNoteController
public class ExplorerNoteController implements Initializable {
#FXML
StackPane rootNotePane;
#FXML
private Label labPaneTitle1;
#FXML
private Label labPaneDate1;
#FXML
private Label labPaneContent1;
BorderPane pane = null;
String ok = "ok";
/**
* Initializes the controller class.
*/
#Override
public void initialize(URL url, ResourceBundle rb) {
}
public void setData(String title, String content, String date) {
labPaneTitle1.setText(title);
labPaneContent1.setText(content);
labPaneDate1.setText(date);
}
public void showHoverMenu() throws IOException {
FXMLLoader loader = new FXMLLoader(getClass().getResource("/fr/cryption/view/NoteHoverMenu.fxml"));
pane = (BorderPane) loader.load();
NoteHoverMenuController controller = loader.getController();
rootNotePane.getChildren().add(pane);
}
public void hideHoverMenu() throws IOException {
rootNotePane.getChildren().remove(pane);
}
....
NoteHoverMenuController
public class NoteHoverMenuController implements Initializable {
String idNote = "";
#FXML
BorderPane hovernode;
#Override
public void initialize(URL url, ResourceBundle rb) {
// TODO
}
public void setID(String id) {
idNote = id;
}
public void deleteNoteExplorer(ActionEvent event) {
System.out.println(hovernode.getParent().lookup("stckpane")); // DELETE THE NOTE
}
}
I will update this post if you want FXML(s) as well.
Thanks.
SCREENS :
Screen Gridpane
Screen Hover StackPane

JavaFX change ComboBox items (outside of initialize() method)

I am playing around with SceneBuilder and come across a few questions about the intialize() method and how to change ComboBox items after it's already been initialized in said method. So basically, after I set the items in initialize, I am not able to change them anymore from another method in the controller.
Here is my code:
public class AppController implements Initializable {
private ObservableList<String> list = FXCollections.observableArrayList();
private MainModel model;
#FXML
private ComboBox<String> cobUsers = new ComboBox<String>();
#Override
public void initialize(URL url, ResourceBundle rb) {
list.add("name1");
list.add("name2");
cobUsers.setItems(list); // this works!
}
public void initModel(MainModel model) {
this.model = model;
}
public void addItems(){
list.add("name3");
list.add("name4");
cobUsers.setItems(list); // this does not work. ComboBox items remain "name1" and "name2"
}
}
public class App extends Application {
private Stage primaryStage;
private AnchorPane rootLayout;
private AppController appController = new AppController();
MainModel model = new MainModel();
#Override
public void start(Stage primaryStage) {
appController.initModel(model);
this.primaryStage = primaryStage;
this.primaryStage.setTitle("App");
initRootLayout();
appController.addItems();
}
/**
* Initializes the root layout.
*/
public void initRootLayout() {
try {
// Load root layout from fxml file.
FXMLLoader loader = new FXMLLoader();
loader.setLocation(getClass().getResource("FXMLDocument.fxml"));
rootLayout = (AnchorPane) loader.load();
// Show the scene containing the root layout.
Scene scene = new Scene(rootLayout);
primaryStage.setScene(scene);
primaryStage.show();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
}
So guess my question is, how can I access/change my ComboBox later on, after it's been initialized in intialize()?
Thanks! :)
UPDATE 1:
I have changed the initRootLayout() in the App class (see below) and it WORKS now. list now contains 4 items and all of them show up in the ComboBox after calling addItems(). Thanks everyone!
public void initRootLayout() {
try {
// Load root layout from fxml file.
FXMLLoader loader = new FXMLLoader(); loader.setLocation(getClass().getResource("FXMLDocument.fxml"));
rootLayout = (AnchorPane) loader.load();
AppController controller = loader.<AppController>getController();
controller.addItems();
// Show the scene containing the root layout.
Scene scene = new Scene(rootLayout);
primaryStage.setScene(scene);
primaryStage.show();
} catch (IOException e) {
e.printStackTrace();
}
}

How to prepare FXML Controller to be GC

I have an issue in my application. Any Controllers are garbage collected.
I was prepared one, pretty simple with clear function, but it is no removed from memory.
#Log4j2
public class DialogOpenProjectController extends DialogPane implements Initializable, FXMLController, FXMLPane, FXMLDialogController {
#FXML
private ObjectProperty<ResourceBundle> languageBundle = new SimpleObjectProperty<>();
#FXML
private JFXTabPane TAB_PANE_OPEN_PROJECT;
#FXML
private JFXButton BUTTON_CONFIRM;
private Tab tabOpenNewProject;
private Tab tabOpenExistingProject;
private Stage stage;
private ChangeListener<? super ResourceBundle> languageListener = this::languageChange;
private ChangeListener<? super Tab> selectedTabListener = this::selectedTabChanged;
{
tabOpenExistingProject = new Tab();
tabOpenNewProject = new Tab();
}
/**
* Initializes the controller class.
*/
#Override
public void initialize(URL url, ResourceBundle rb) {
log.trace(LogMessages.MSG_CTRL_INITIALIZATION);
this.languageBundle.setValue(rb);
initTabs();
addSelectedTabListener();
addDisableButtonListener();
addLanguageListener();
log.trace(LogMessages.MSG_CTRL_INITIALIZED);
}
#FXML
public void cancel() {
this.stage.close();
clean();
}
#FXML
public void confirm() {
if (TAB_PANE_OPEN_PROJECT.getSelectionModel().getSelectedItem().equals(tabOpenNewProject)) {
actionNewProject();
} else if (TAB_PANE_OPEN_PROJECT.getSelectionModel().getSelectedItem().equals(tabOpenExistingProject)) {
actionOpenProject();
} else {
//To to show error
}
this.stage.close();
clean();
}
private void initTabs() {
TAB_PANE_OPEN_PROJECT.getSelectionModel().select(tabOpenNewProject);
}
private void addSelectedTabListener() {
TAB_PANE_OPEN_PROJECT.getSelectionModel().selectedItemProperty().addListener(selectedTabListener);
}
private void addDisableButtonListener() {
//nothing to do temporary
}
private void clean() {
this.languageBundle.removeListener(languageListener);
languageBundle.unbind();
languageBundle.setValue(null);
TAB_PANE_OPEN_PROJECT.getSelectionModel().selectedItemProperty().removeListener(selectedTabListener);
TAB_PANE_OPEN_PROJECT.getSelectionModel().clearSelection();
TAB_PANE_OPEN_PROJECT.getTabs().clear();
BUTTON_CONFIRM.disableProperty().unbind();
selectedTabListener = null;
languageListener = null;
tabOpenNewProject = null;
tabOpenExistingProject = null;
stage = null;
getChildren().clear();
}
private void addLanguageListener() {
this.languageBundle.addListener(languageListener);
}
private void languageChange(ObservableValue<? extends ResourceBundle> observable, ResourceBundle oldValue, ResourceBundle newValue) {
reloadElements();
}
private String getValueFromKey(String key) {
return this.languageBundle.getValue().getString(key);
}
private void reloadElements() {
// Nothing to do
}
public void setStage(Stage stage) {
this.stage = stage;
}
private void selectedTabChanged(ObservableValue<? extends Tab> observable, Tab oldValue, Tab newValue) {
if (newValue.equals(tabOpenNewProject)) {
BUTTON_CONFIRM.setText(getValueFromKey(Keys.CREATE));
} else if (newValue.equals(tabOpenExistingProject)) {
BUTTON_CONFIRM.setText(getValueFromKey(Keys.OPEN));
}
}
}
For loading FXML files I use Singleton Class ScreenManager. This method is called to load this Dialog :
public void showNewDialog( FilesFXML fxml) {
FXMLLoader loader = new FXMLLoader(getClass().getResource(fxml.toString()), context.getBundleValue());
try {
Stage dialogStage = new Stage();
AnchorPane dialogwindow = (AnchorPane) loader.load();
FXMLDialogController controller = loader.getController();
dialogStage.initModality(Modality.WINDOW_MODAL);
dialogStage.initOwner(this.getStage());
Scene scene = new Scene(dialogwindow);
dialogStage.setScene(scene);
dialogStage.setResizable(false);
controller.setStage(dialogStage);
dialogStage.showAndWait();
} catch (Exception ex) {
log.error(ex.getMessage());
log.error(ex.getCause());
ex.printStackTrace();
}
}
I was checked it in VisualVM and I see one 1 of this controller and 2 lambdas ( I suppose it consiste initialization of 2 listener)
But event clear function is called this dialog is still in memory and cannot be garbage collected. I have no more idea how to force removing it. It is reallegrave, because it consist all of my Controllers.
I'm pretty confused what you are asking and what you are talking about. Nevertheless, if that dialog window is open, there is no way it can be garbage collected.
The JavaFX main API is holding a strong reference of the stage (maybe the scene too).
The stage/scene is holding strong reference of all the nodes specified in your FXML file.
Some of those nodes are holding strong reference of the controller because you have specified action event handlers in the controller.
In order to make the controller eligible for garbage collection, you need to break this chain somewhere.
The easiest way is to close the dialog window. When the window is closed, because the JavaFX engine does not need to manage render pulses anymore, it will release its reference to the stage. At that point, the whole thing under that stage is eligible for garbage collection, if nothing else (that itself is not eligible for garbage collection) is holding strong references of them.

How to get reference to another controller - JavaFX

Let's say I've got 3 views and 3 controllers:
LogInController, LogInView
MainMenuController, MainMenuView
ListOfPatientsInternalMedicineController, ListOfPatientsInternalMedicineView.
An internalMedicineButtonClicked method change my scene to another (with some other content) but in this new scene, I want to have a button which allows me to go back to MainMenu (goBacktoMainMenuButtonClicked() method). And here occures my problem. How am I able to get reference to MainMenuController (the one which is corresponding with fxml file, created in LogInController) to fill setController() method.
public class LogInController {
MainMenuController mainMenuController = new MainMenuController();
#FXML
private JFXTextField logInTextField;
#FXML
private JFXButton logInButton;
#FXML
private JFXPasswordField passwordTextField;
#FXML
void logInButtonClicked(ActionEvent event) throws IOException {
LogInDAO logInDAO = new LogInDAO();
if(logInDAO.checkIfLoginAndPasswordIsCorrect(logInTextField.getText(),passwordTextField.getText()))
{
FXMLLoader loader = new FXMLLoader(getClass().getResource("/fxml/MainMenu.fxml"));
Window window = logInButton.getScene().getWindow();
Stage stage = (Stage) window;
loader.setController(mainMenuController); // here i'm passing original controller corresponding with fmxl
stage.setScene(new Scene(loader.load()));
}
else
{
(...)
}
}
}
MainMenuCotroller class:
public class MainMenuController {
ContentOfPatientTableView patientTableViewModel = new ContentOfPatientTableView();
(..)
#FXML
void internalMedicineButtonClicked(ActionEvent event) throws IOException {
FXMLLoader loader = new FXMLLoader(getClass().getResource("/fxml/ListOfPatientsInternalMedicineView.fxml"));
Button button = (Button) event.getSource();
Scene scene = button.getScene();
Stage stage = (Stage) scene.getWindow();
loader.setController(new ListOfPatientsInternalMedicineController(patientTableViewModel));
stage.setScene(new Scene(loader.load()));
}
And ListOfPatientsInternalMedicineController class;
public class ListOfPatientsInternalMedicineController {
IPatientDAO patientDAO = new PatientDAO();
ContentOfPatientTableView patientTableViewModel;
public ListOfPatientsInternalMedicineController(ContentOfPatientTableView content) {
patientTableViewModel=content;
}
#FXML
public void goBacktoMainMenuButtonClicked(ActionEvent event)
{
FXMLLoader loader = new FXMLLoader(MainMenuController.class.getResource("/fxml/MainMenuView.fxml");
loader.setController(?????????); // Here if I will pass new MainController() i will create new instance, not this which is corresponding with fxml file. How am I able to refer to instance MainController created in LogInController ?
}
}
Consider using another model to represent the current view. You could implement this along the following lines:
public class ViewState {
private final ContentOfPatientTableView patientTableViewModel ;
private final ReadOnlyObjectWrapper<Parent> currentView = new ReadOnlyObjectWrapper<>();
private Parent logInView ;
private Parent mainMenuView ;
private Parent listOfPatientsMainMedicineView ;
public ViewState(ContentOfPatientTableView patientTableViewModel) {
this.patientTableViewModel = patientTableViewModel ;
}
public ReadOnlyObjectProperty<Parent> currentViewProperty() {
return currentView.getReadOnlyProperty();
}
public void showLogIn() {
if (logInView == null) {
try {
FXMLLoader loader = new FXMLLoader("/fxml/LogIn.fxml");
loader.setController(new LogInController(this));
logInView = loader.load();
} catch (IOException exc) {
// fatal...
throw new UncheckedIOException(exc);
}
}
currentView.set(logInView);
}
public void showMainMenu() {
// similarly...
}
public void showListOfPatientsMainMedicineView() {
// ...
}
}
Now your LogInController can do:
public class LogInController {
private final ViewState viewState ;
#FXML
private JFXTextField logInTextField;
#FXML
private JFXButton logInButton;
#FXML
private JFXPasswordField passwordTextField;
public LogInController(ViewState viewState) {
this.viewState = viewState ;
}
#FXML
void logInButtonClicked(ActionEvent event) {
LogInDAO logInDAO = new LogInDAO();
if(logInDAO.checkIfLoginAndPasswordIsCorrect(logInTextField.getText(),passwordTextField.getText()))
{
viewState.showMainMenu();
}
else
{
(...)
}
}
}
Similarly,
public class MainMenuController {
private final ViewState viewState ;
public MainMenuController(ViewState viewState) {
this.viewState = viewState ;
}
#FXML
void internalMedicineButtonClicked(ActionEvent event) throws IOException {
viewState.showListOfPatientsMainMedicineView();
}
}
and similarly for the other controller.
Note that you are instantiating each controller in ViewState, so just give that class access to each of the other models it may need.
Finally, you boot all this up with
public class MyApp extends Application {
#Override
public void start(Stage primaryStage) {
ViewState viewState = new ViewState(/* pass models here...*/);
viewState.showLogIn();
Scene scene = new Scene(viewState.currentViewProperty().get());
scene.rootProperty().bind(viewState.currentViewProperty());
primaryStage.setScene(scene);
primaryStage.show();
}
}

accessing a Pane from another class in javafx

I am unable to access from another class, I have created two pane with different controllers in one FXML File, second pane has to move around the first pane, on Click Action, and I have loaded an FXML file in second pane, also successfully moved the pane one time, but the second time I want to move it from second controller, but it is giving me an NullPointerException.
this is my main controller where I have moved the pane:
public class MainController implements Initializable {
#FXML
private Pane searchPane;
#FXML
private Pane secondPane;
private TranslateTransition nextTransition;
public Pane getSecondPane() {
return secondPane; // Accessing Pane with this getter method
}
/**
* Initializes the controller class.
*/
#Override
public void initialize(URL url, ResourceBundle rb) {
}
#FXML
private void nextBtnAction(ActionEvent event) {
try {
Parent pessFxml = FXMLLoader.load(getClass().getResource("firstPane.fxml"));
secondPane.getChildren().add(pessFxml);
nextTransition = new TranslateTransition(Duration.millis(300), secondPane);
nextTransition.setToX(searchPane.getLayoutX()-secondPane.getLayoutX());
nextTransition.play();
} catch (IOException ex) {
JOptionPane.showMessageDialog(null, "Form does not found" ,ex.toString(),0);
}
}
}
this is SecondController where I am accessing the pane to move it back smoothly, but its throwing NullPointerException:
please tell me how to solve this
public class SecondController implements Initializable {
MainController mainControll;
/**
* Initializes the controller class.
*/
#Override
public void initialize(URL url, ResourceBundle rb) {
}
#FXML
private void backBtnPessAction(ActionEvent event) {
//here i am putting the second pane to move back
TranslateTransition back = new TranslateTransition(Duration.millis(300), mainControll.getSecondPane());
back.setToX(mainControll.getSecondPane().getLayoutX()-750);
back.play();
}
}
MainController is never initialized in SecondController. You should pass it when creating second controller in nextBtnAction.
MainController code:
private void nextBtnAction(ActionEvent event) {
try {
FXMLLoader loader = new FXMLLoader(getClass().getResource("firstPane.fxml"));
Parent pessFxml = loader.load();
SecondController controller = (SecondController)loader.getController();
controller.setMainController(this);
secondPane.getChildren().add(pessFxml);
nextTransition = new TranslateTransition(Duration.millis(300), secondPane);
nextTransition.setToX(searchPane.getLayoutX()-secondPane.getLayoutX());
nextTransition.play();
} catch (IOException ex) {
JOptionPane.showMessageDialog(null, "Form does not found" ,ex.toString(),0);
}
}
SecondController:
public void setMainController(MainController controller) {
this.mainControll = controller;
}

Resources