I have two controller class Fxml1.java and Fxml2.java. In Fxml1.java I hava label Label l1, and in Fxml2.java I have text field t1. I have some text in text field t1 and I want to set this text to Label l1. i want to do some thing like below. Below is a just dummy code for understanding consider both class have separate fxml files. Here i receive NullPointerException.
//class Fxml1
public class Fxml1{
public label l1;
l1.setText("hello");
}
//class Fxml2
public class Fxml2{
public TextField t1;
public Button b1;
public Fxml1 ob;
public void onButtonSubmit(ActionEvent event){
ob = new Fxml1();
ob.l1.setText(t1.getText());
}
public void initialize(URL url, ResourceBundle rb){
t1.setText("This is textfield text");
}
}
In your Fxml2 controller you are creating a new, completely separate instance of Fxml1 that is not linked to your fxml graph file, so it cannot update your required label since it has no access to it or any knowledge that it exists.
You should handle this through the use of events. A basic tutorial is can be found on Vogella's site.
Inject the event broker into both controllers:
#Inject IEventBroker broker;
In your Fxml2 controller you should post this string to the event broker when the text is updated:
public void onButtonSubmit(ActionEvent event) {
broker.post("SomeSharedConstant", t1.getText());
}
In your Fxml1 controller, you should listen for this event and update your label when it is fired.
#Inject #Optional
public void getEvent(#UIEventTopic("SomeSharedConstant") String text) {
// text1 is a SWT Text field
l1.setText(text);
}
Related
I am trying to make it such that I can create a new tab for my TabPane from within another tab but I am having some difficulty. Currently I have the TabPane set up in the "main-window.fxml" with the corresponding MainWindowController. I have a tab within this TabPane which, via fx:include, displays "mainTab.fxml" to the scene graph, controlled by MainTabController. Now from within the "mainTab" I want a button to be able to add an additional tab to the TabPane, but since this is requires a reference to the TabPane in "main-window", I have created a static method in "main-window". When the run the code below I get a NullPointerException on this line in the MainWindowController:
mainTabPane.getTabs().add(new Tab(team.getTeamName()));
Could someone please tell me as to why it is giving this exception and how I can begin to work around it?
main-window.fxml:
<TabPane fx:id="mainTabPane">
<tabs>
<Tab fx:id="mainTab" text="Main" closable="false">
<fx:include source="mainTab.fxml" fx:id="mainWindowTab" alignment="CENTER"/>
</Tab>
</tabs>
</TabPane>
mainTab.fxml (the event handler for the button):
#FXML
public void handleSubmit() {
String teamName = teamNameTextField.getText();
Roster roster = rosterComboBox.getValue();
int startWeek = spinner.getValue();
Team newTeam = new Team(teamName, startWeek, roster);
TeamData.addTeam(newTeam);
MainWindowController controller = new MainWindowController();
controller.createTeamTab(newTeam);
}
MainWindowController:
public class MainWindowController {
#FXML
private TabPane mainTabPane;
public void createTeamTab(Team team) {
mainTabPane.getTabs().add(new Tab(team.getTeamName()));
}
}
Your code doesn't work because you are not calling createTeamTab(...) on the controller: you are calling it on another instance of MainWindowController that you created. (The fields annotated #FXML are initialized in the controller instance by the FXMLLoader when the FXML is loaded: for fairly obvious reasons they will not be set to the same values in arbitrary other instances of the same class.) You need to get a reference to the controller you are using for the main tab, and pass it a reference to the main controller.
You didn't tell us the class name for the controller of mainTab.fxml: I will assume it is MainTabController (so just change it to whatever class name you actually use).
In MainWindowController, do:
public class MainWindowController {
#FXML
private TabPane mainTabPane;
#FXML
// fx:id of the fx:include with "Controller" appended
private MainTabController mainWindowTabController ;
public void initialize() {
mainWindowTabController.setMainWindowController(this);
}
public void createTeamTab(Team team) {
mainTabPane.getTabs().add(new Tab(team.getTeamName()));
}
}
and then in MainTabController do
public class MainWindowController {
private MainWindowController mainWindowController ;
public void setMainWindowController(MainWindowController mainWindowController) {
this.mainWindowController = mainWindowController ;
}
#FXML
public void handleSubmit() {
String teamName = teamNameTextField.getText();
Roster roster = rosterComboBox.getValue();
int startWeek = spinner.getValue();
Team newTeam = new Team(teamName, startWeek, roster);
TeamData.addTeam(newTeam);
mainWindowController.createTeamTab(newTeam);
}
}
I have an FXML file with an empty Label named welcomeText.
It's the main Scene of my program and I would like to set the label text to something like that: Hello "username" when I start the program on Windows or Linux.
public class MainAdminController implements Initializable {
#FXML
private Label welcomeText;
final String username = System.getProperty("user.name");
#FXML
private void SetWelcome() {
welcomeText.setText("Hello " +username);
}
}
But it isn't show anything. Any idea how can I set the Label properly when I open the scene? Thanks.
The SetWelcome method is superfluous.
Define an initialize() method for your controller and it will automatically be invoked when the FXMLLoader loads a new document linked to the controller.
public void initialize() {
welcomeText.setText("Hello " +username);
}
Let's say I have a button in a nested (child) fxml file, and in the child's controller I have created an action event that fires on button click. From that method I want to disable or enable certain controls (for instance some tabs in a tabpane) in my main (parent) fxml.
How can I achieve this?
This is the closest thread I found, which discussed how to do it the other way around: JavaFX - Access fx:id from nested FXML
Any help is greatly appreciated!
Define an observable property in the nested controller, and observe it from the surrounding controller:
public class ChildController {
private final BooleanProperty stuffShouldBeDisabled = new SimpleBooleanProperty();
public BooleanProperty stuffShouldBeDisabledProperty() {
return stuffShouldBeDisabled ;
}
public final boolean getStuffShouldBeDisabled() {
return stuffShouldBeDisabledProperty().get();
}
#FXML
private void handleButtonClick(ActionEvent event) {
stuffShouldBeDisabled.set( ! stufShouldBeDisabled.get() );
}
// ...
}
and then in the "surrounding" (Parent) controller (i.e. the controller for the FXML file with the <fx:include> tag):
public class MainController {
#FXML
private ChildController childController ; // injected via <fx:include fx:id="child" ... />
#FXML
private Tab someTab ;
public void initialize() {
childController.stuffShouldBeDisabledProperty().addListener((obs, wasDisabled, isNowDisabled) -> {
someTab.setDisable(isNowDisabled);
}
}
// ...
}
I have an app in JavaFX, which has main scene with menu and toolbar, and smaller scenes, which are injected into this main scene, after one of menu buttons are being pressed.
Now, HomeCntroller is responsible for either scene components: Home Scene (with toolbar and menu), and injected scene. This leads me to create massive, huge and very unprofessional controller if number of injected scenes is more than one.
How to split controller responsibility?
Now my Controller looks like this:
changeDashboardPane method injects smaller Pane into my main HomePane.
#Component
#RequiredArgsConstructor(onConstructor = #__(#Autowired) )
public class HomeController extends AbstractController {
private static final Logger LOG = Logger.getLogger(HomeController.class);
private final BudgetProfileService budgetProfileService;
#FXML
private Label usernameLabel;
#FXML
private ComboBox<String> budgetProfilesComboBox;
#FXML
private AnchorPane dashBoardPane;
#FXML
public void initialize() {
refreshUsernameLabel();
getAllBudgetProfiles();
changeDashboardPane(PaneFactoryKeys.FINANCES_PANE);
}
private void refreshUsernameLabel() {
String username = UserAccountProvider.getLoggedUser().getUsername();
usernameLabel.setText(username);
}
private void getAllBudgetProfiles() {
List<String> budgetProfileNames = budgetProfileService.getAllBudgetProfileNames();
if (!budgetProfileNames.isEmpty()) {
budgetProfilesComboBox.getItems().clear();
budgetProfilesComboBox.getItems().addAll(budgetProfileNames);
}
}
#FXML
public void handleFinancesButtonAction() {
changeDashboardPane(PaneFactoryKeys.FINANCES_PANE);
}
#FXML
public void handlePeriodButtonAction() {
changeDashboardPane(PaneFactoryKeys.PERIOD_PANE);
}
#FXML
public void handleStatisticsButtonAction() {
changeDashboardPane(PaneFactoryKeys.STATISTICS_PANE);
}
#FXML
public void handleSettingsButtonAction() {
changeDashboardPane(PaneFactoryKeys.SETTINGS_PANE);
}
private final void changeDashboardPane(String paneFactoryKey) {
double injectedPanePosition = 0.0;
Pane paneToChange = getPaneFromFactory(paneFactoryKey);
dashBoardPane.getChildren().clear();
AnchorPane.setTopAnchor(paneToChange, injectedPanePosition);
dashBoardPane.getChildren().add(paneToChange);
}
}
To get this more clear, screens:
without injected second pane
with injected second pane
Any ideas guys?
I would recommend you to divide your main scene in smaller ones, for example you can have a tools scene, a header scene, a content scene and so on. Then you should have one controller for every scene.
After that I would use a publisher-subscriber pattern to deal with behaviors, like when you press a button on settings scene, it triggers an event that other scenes listen to and then they handle it changing their state accordingly.
I hope it was clear and can help!
Create multiple controllers , multiple FXML files - to continue on my answer that i provided you before, JavaFX how to inject new FXML content to current Scene each of those views that have separate fxml file also has
fx:controller="appplication.ExampleViewController"
attached to it.So what you do is create main controller as was mentioned , that is basically the FRAME CONTAINER that encapsulates controls to change your dynamic container.If your application is really ui rich and have a lot of functionality in one controller , you can break down your view even further:
For instance take out menu and put it into separated controller , and insert it into your main view with main controller
/same way as in method setView()/
, what you are doing is just taking it away to keep controller code smaller, YOU DONT DECREASE/INCREASE SCENE GRAPH THIS WAY, doesnt have a drawback its just a personal preference.
You gonna end up with more fxml files and controllers in the end.Its all the same thing as from your previous question there is no additional code needed you can actually reuse what was already provided.
Data between controllers are passed thru MODEL. - look more into MVC dont work with application data in controllers only care about view or passing them from/into model
To avoid a huge contoller class, as I am using multiple tabs, I split the tabs to single java files.
My solution was to create a cascade of classes:
Base: Containing all defs for FX types
Tab1 extends Base: Tab one implementation
Tab2 extends Tab1: Tab two implementation
Controller extends Tab2 implements Initializable: Implements initialize(URL url, ResourceBundle resourceBundle)
Important:
Any accessed object must be definded in the current tab or before.
Any Objects in Base are available in Controller whereas no object of Controller is accessable in Base, Tab1 or Tab2.
Feel free to add your opinion as comment or submit a improvement.
I have one question regarding Javafx controller.
Lets say, I have multiple fxml files that are bind together in a main app. Then I have separate controllers for every fxml files. Lets see the following structure
com.par.app
- MainApp.java -> This is the main Application
- FirstController.java
- SecondController.java
com.par.app.view
- First.fxml
- Second.fxml
com.par.app.model
- MyModel -> This has some getter and setter methods.
Now as per above structure, I have a checkbox in First.fxml and a label in Second.fxml.
My Question : How can i set the label text in Second.FXML by checking and unchecking the checkbox in First.FXML , I have tried like this:
// In FirstController.Java
1) Initialize the SecondController
2) Get checkbox from FXMl as , priate CheckBox box1;
3) On initialize(....) method, I have set the event handler, as box1.setOnAction(enableHandle)
4) Finally the event Handler as,
EventHandler<ActionEvent> enableHandle = new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
if (box1.isSelected()) {
secondController.setLabelText("PoP");
} else {
secondController.setText("Gone Wrong");
}
}
};
Similarly, On my second controller I have declared FXML control as,
#FXML
private Label lblTes;
// the method
public void setLabelText(String a)
{
this.lblTes.settest(a);
}
-> The above wont work as it returns Unknown Source.
The next way I tried is by using the MyModel , and using getter and setter methods, But unsuccessful.
I'm sorry my question is really long. I have tried but not succeeded.
What can I do to solve this?
Thanks in advance.
// my model looks like
public class MyModel {
private String btnname;
public String getBtnname() {
return btnname;
}
public void setBtnname(String btnname) {
this.btnname = btnname;
}
}
When you check the check box then in the controller of the FirstView (where you implement an event handler for the check box click) change the label text in your model.
Your model should be bound to your views therefore the label text in your SecondView should be updated.
If you did not bind the model to your views you may use an Observer pattern.
1.Change your model and extend java.util.Observable
public class MyModel extends Observable {
private String btnname;
public String getBtnname() {
return btnname;
}
public void setBtnname(String btnname) {
this.btnname = btnname;
pingObservers()
}
private void pingObservers() {
setChanged();
notifyObservers();
}
}
Register your SecondController as an Observer of the model. When you set the model to the controller add a line similar to this:
model.addObserver(this);
SecondController must implement java.util.Observer.update(...)
void update(Observable o, Object o1) {
// Set the label text with model value
}
In you event handler in the FirstController when you call the setBtnname() method on your model the update() method in the SecondController will be called. There up to you to add the code to change your label text. Since the label is in the view controlled by SecondController you just need to inject a reference of the label in the controller with #FXML annotation.