I'm trying to switch between two anchor panes when I click a button in JavaFX. The first button should navigate to the dashboard pane and the second should navigate to the projects pane.
Can someone help me out
#FXML
public JFXButton btndashBoardPane;
public JFXButton btnProjectsPane;
#FXML
public AnchorPane dashBoardPane,projectsPane;
#FXML
public void handleButtonAction(ActionEvent event){
if(event.getSource()==btnProjectsPane){
projectsPane.setVisible(true);
projectsPane.toFront();
}
if(event.getSource()==btndashBoardPane){
dashBoardPane.setVisible(true);
dashBoardPane.toFront();
}
}
You could simply add an EventHandler to onAction for both buttons.
btndashBoardPane.setOnAction(new EventHandler<ActionEvent>() {
#Override
void handle(ActionEvent actionEvent) {
dashBoardPane.setVisible(true);
projectsPane.setVisible(false);
}
});
And likewise the same thing vice versa for the btnProjectsPane.
Another approach, but probably less polished would be to have a parent Anchorpane to whose children you add the pane you want to be selected first. The buttons would then clear the children and add their respective pane to the parent Anchorpane.
Related
I've got a view (SplitPane horizontaly seperated). So lets name these two parts lowerAnchorPane and upperAnchorPane. In upperUnchorPane I've got a button. I want the application after pressing that button to switch lowerAnchorPane with another AnchorPane created in FXML file. I want to achive this in function showPatients() but it seems not working, can you suggest me any solutions? I don't know any other way to load fxml file
public class MainController {
private Patient model;
#FXML
private Button newPatientButton;
#FXML
private Button newVisitButton;
#FXML
private Button showVisitsButton;
#FXML
private Button showPatientsButton;
#FXML
private AnchorPane lowerAnchorPane;
public void initData(Patient model)
{
this.model = model;
}
#FXML
void showPatients(ActionEvent event) throws IOException { // when button pressed
FXMLLoader loader = new FXMLLoader(getClass().getResource("/fxml/ListOfPatients.fxml"));
lowerAnchorPane = loader.load();
}
You can inject the SplitPane and replace the existing lower anchor pane with the one you load:
#FXML
private SplitPane splitPane ; // with the corresponding fx:id in the fxml file
#FXML
void showPatients(ActionEvent event) throws IOException { // when button pressed
FXMLLoader loader = new FXMLLoader(getClass().getResource("/fxml/ListOfPatients.fxml"));
lowerAnchorPane = loader.load();
// assuming the existing pane is the second one in the split pane:
splitPane.getItems().set(1, lowerAnchorPane);
}
Im looking at this problem from in a new way:
I have a simple FXML app that has a mainapp, a controller and a view. The view displays a gridpane consisting of three rows where each row contains a single label element. So the gridpane looks like this to the user:
row 1
row 2
row 3
There is also a button called Delete Selected Row that is clicked after the user selects a row. When the button is clicked, the element is temporarily removed, or hidden, from the gridpane. So if row 2 is selected and the button is clicked the user will see
row 1
row 3
The above view is created with Scene Builder that creates an XML file called AppOverview.fxml.
There is a MainApp that paints the scene that has the following code:
public class MainApp extends Application {
private Stage primaryStage;
private BorderPane rootLayout;
//Constructor
public MainApp() {}
#Override
public void start(Stage primaryStage)
{
this.primaryStage = primaryStage;
primaryStage.initStyle(StageStyle.UNDECORATED);
initRootLayout();
showAppOverview();
}
// Initializes the root layout.
public void initRootLayout() {
try {
// Load root layout from fxml file.
FXMLLoader loader = new FXMLLoader();
loader.setLocation(MainApp.class.getResource("view/RootLayout.fxml"));
rootLayout = (BorderPane) loader.load();
// Show the scene containing the root layout.
Scene scene = new Scene(rootLayout);
primaryStage.setScene(scene);
primaryStage.show();
} catch (IOException e) {
System.out.println("Error in MainApp:initRootLayout()");
System.exit(0);
}
}
// Shows the app overview inside the root layout.
public void showAppOverview() {
try {
// Load overview.
FXMLLoader loader = new FXMLLoader();
loader.setLocation(MainApp.class.getResource("view/AppOverview.fxml"));
AnchorPane appOverview = (AnchorPane) loader.load();
// Set overview into the center of root layout.
rootLayout.setCenter(appOverview);
// Give the controller access to the main app.
AppController controller = loader.getController();
controller.setMainApp(this);
} catch (IOException e) {
System.exit(0);
}
}
// Returns the main stage.
public Stage getPrimaryStage(){
return primaryStage;
}
public static void main(String[] args) {
launch(args);
}
}
The controller in my app.view package contains listeners on the handleDeleteBtn button (and a couple other buttons) and handleSelectedGridPaneRow() to manage row selection in the gridpane:
public class AppController {
//Reference to MainApp
private MainApp mainApp;
#FXML
private void handleRestoreBtn(){
System.out.println("You are restoring the hidden row");
}
#FXML
private void handleDeleteBtn(){
System.out.println("You are hiding the selected row");
}
#FXML
private void handleExitBtn(){
System.exit(0);
}
#FXML
private void handleSelectedGridPaneRow(){
System.out.println("You have selected a row to hide");
}
public AppController(){}
// Initialize the <code>AppController</code> instance.
#FXML
private void initialize(){}
public void setMainApp(MainApp mainApp)
{
this.mainApp = mainApp;
}
}
My specific questions are:
1) Is this kind of spec achievable, ie, being able to dynamically manage a gridpane as described?
2) If yes, where and how would I access the gridpane? Presumably somewhere in the method 'handleDeleteBtn()' in AppController. What do I need to do to reference the gridpane in the Controller and then execute statements such as gridPane.getChildren().remove(0,2);?
I think the fact I got know answers to my post indicated that my approach was all wrong. Anyway the solution I have found works very well. Instead of trying to remove and restore rows in the gridpane, I just left the gridpane alone with the original 1 column and 3 row and instead changed the content of each cell according to the button being pressed. So I changed the names of the button handlers to something more reflective to what I was attempting, and added an extra to display another combination of rows:
#FXML
private void showRow1and2Btn(){
...
}
#FXML
private void showRow2and3Btn(){
...
}
#FXML
private void showAll(){
...
}
Then I declared three labels to show the contents of the rows I want:
#FXML
private Label label_0;
#FXML
private Label label_1;
#FXML
private Label label_2;
These labels are bound to three labels that I built in Scene Builder and which occupy the three rows in the gridpane.
I added this method to get the three gridpane rows populated according to my spec:
private void setgridPaneRows(Label label, String str)
{
label.setText(str);
}
Then its just a case of getting something into the button handlers so the desired effect is achieved, for example:
private void showRow1and2Btn()
{
setgridPaneRows(label_0,"Label 0");
setgridPaneRows(label_1,"Label 1");
setgridPaneRows(label_2,null);
}
#FXML
private void showRow2and3Btn(){
setgridPaneRows(label_0,"Label 1");
setgridPaneRows(label_1,"Label 2");
setgridPaneRows(label_2,null);
}
#FXML
private void ShowAllBtn(){
setgridPaneRows(label_0,"Label 0");
setgridPaneRows(label_1,"Label 1");
setgridPaneRows(label_2,"Label 2");
}
In another application I have successfully extended this approach to adding a second column to the gridpane and populating these elements with different controls, so for example, I can make gridpane 1,0 contain a combo box for one input requirement and then contain a textfield for another kind of input requirement.
i'm trying to add an ActionListener to a label whitch pop out whenever user types wrong password or login.
Here is my Login Controller
public class LoginController implements Initializable {
#FXML
private Label label;
#FXML
private TextField LoginField;
#FXML
private PasswordField PasswdField;
#FXML
private Button LogInButton;
#FXML
private Label IncorrectDataLabel;
//private String uri = "http://google.com";
#FXML
private void LogIn(ActionEvent event) throws IOException {
if(LoginField.getText().equals("MKARK")&&PasswdField.getText().equals("KACZOR1"))
{
Parent parent = FXMLLoader.load(getClass().getResource("/fxmlFiles/MainScreen.fxml"));
Scene MainScene = new Scene(parent);
Stage stage = (Stage) ((Node) event.getSource()).getScene().getWindow();
stage.setScene(MainScene);
stage.show();
}
else
{
IncorrectDataLabel.setVisible(true);
// <------------------- Here I want to bind hyperlink to a label with would open the google site, whenever user clicks it.
}
#Override
public void initialize(URL url, ResourceBundle rb) {
// TODO
}
How am i able to fix that issue? I've tried many times (setOnAction, addMouseListener) but nothing worked :(.
If You dont mind i would also ask about the public void initialize function. What is it for? It pop out automatically when i created the class.
Thanks in advance
Labels do not fire action events. You could use a listener for mouse clicked events, e.g:
#FXML
private void gotoGoogle() {
// open url etc
}
and in the FXML file
<Label fx:id="IncorrectDataLabel" onMouseClicked="#gotoGoogle" ... />
However, it probably makes more sense to use a Hyperlink for this, which would give the user better visual feedback that it was something on which they were expected to click. Just replace the label with
<Hyperlink fx:id="IncorrectDataLabel" onAction="#gotoGoogle" text="..." ... />
and update the type in the controller accordingly:
#FXML
private Hyperlink IncorrectDataLabel ;
You need the appropriate import for javafx.control.Hyperlink in both the FXML file and in the controller.
Off-topic note: use proper Java naming conventions for your variable and method names.
I am creating tabs dinamically and i am using the same fxml file for all of them. So, all type of controls that I've included has de same "fx:id". I use this: "#FXML TextField textField". How could I use the TextField of the second tab, the TextField of the first tab, etc?
#Controller
public class MascotaTabControllerImpl implements MascotaTabController
{
private AnchorPane anchorPane;
private Tab tab;
private Mascota mascota;
#FXML
private ComboBox<String> comboMascota;
#FXML
private ComboBox<String> comboTamano;
#FXML
private TextField fieldNombreMascota;
#FXML
private RadioButton radioAlergiaSi;
#FXML
private RadioButton radioAlergiaNo;
#FXML
private TextField fieldRaza;
#FXML
private TextField fieldPeso;
#FXML
private ComboBox<String> comboSexo;
#FXML
private ComboBox<String> comboAgresividad;
#FXML
private TextArea areaObservaciones;
#FXML
private Button buttonEditar;
#FXML
private Button buttonCancelar;
#Override
public void inicializacionFxmlFile(TabPane tabPane, Collection<Mascota> mascotas)
{
try
{
for(Mascota mascota : mascotas)
{
anchorPane = new AnchorPane();
FXMLLoader loader = new FXMLLoader(getClass().getResource("/view/mascotaTab.fxml"));
loader.setController(this);
anchorPane.getChildren().setAll(loader.load());
tab = new Tab();
tab.setContent(anchorPane);
tabPane.getTabs().add(tab);
tab.setText(mascota.getNombre());
fieldNombreMascota.setText(mascota.getNombre());
fieldRaza.setText(mascota.getRaza());
comboSexo.setValue(mascota.getSexo());
fieldPeso.setText(String.valueOf(mascota.getPeso()));
comboTamano.setValue(mascota.getTamano());
comboAgresividad.setValue(mascota.getAgresividad());
areaObservaciones.setText(mascota.getObservaciones());
mascota.setNombre(fieldNombreMascota.getText());
}
tabSelected(tabPane, mascotas);
buttonEditar.setOnAction(new EventHandler<ActionEvent>()
{
#Override
public void handle(ActionEvent event)
{
mascota.setNombre(fieldNombreMascota.getText());
mascota.setRaza(fieldRaza.getText());
mascota.setSexo(comboSexo.getValue());
mascota.setPeso(Float.parseFloat(fieldPeso.getText()));
mascota.setTamano(comboTamano.getValue());
mascota.setAgresividad(comboAgresividad.getValue());
mascota.setObservaciones(areaObservaciones.getText());
clienteService.actualizarMascota(mascota);;
}
});
buttonCancelar.setOnAction(new EventHandler<ActionEvent>()
{
#Override
public void handle(ActionEvent event)
{
return;
}
});
}
catch(IOException ioe)
{
System.out.println(ioe.getMessage());
}
}
Edit: imagine "for" statement runs twice. How could call controls (TextFields, ComboBoxes, etc.) of the first tab?
For example, when "for" statement runs the first time "fieldNombreMascota" reference is "id=110" but when "for" statement runs the second time, the same TextField reference is "id=113". So how can I call them with annotation #FXML?
Because you call
loader.setController(this);
inside your loop, every time you call loader.load() you will reuse the same object as the controller for the FXML you are loading. This means each time you go through that for loop, the #FXML-injected fields will get redefined. So by doing this, you essentially make it impossible to access any FXML fields except the ones from the last iteration of the loop.
You must not call loader.setController(this). The "standard" way to combine FXML files and controllers is to use an fx:controller attribute in the root element of the FXML file, specifying a class name. This will cause the FXMLLoader to create a new instance of that class every time you call loader.load(). If there is some other problem you are trying to solve by attempting the non-standard way you are doing things, then the solution you are attempting is not the right approach.
If you organize your code well, the FXML creates a controller instance, and there should be no need whatsoever to access that controller externally. You may need some of your controllers to access a shared data model of some kind, but this is certainly not the right approach to achieve that.
I use the accordion control. Depending on the titled pane, I need to load a fxml file into an anchorPane. So I have two parts: one for accordion and another one for anchorPane to display contents depending on click.
#FXML
private StackPane tmpPane;
#FXML
private void itemMembres(MouseEvent event) throws IOException {
tmpPane.getChildren().add((Node)FXMLLoader.load(getClass().getResource("/view/test.fxml")));
}
tmpPane is an anchorPane in the view.
thanks
I found the solution
Create a node by loanding a fxml file
Use setAll of AnchorPane
#FXML
private AnchorPane tmpPane
#FXML
private void itemMembres(MouseEvent event) throws IOException{
Node node;
node = (Node)FXMLLoader.load(getClass().getResource("/view/MembresTableau.fxml"));
tmpPane.getChildren().setAll(node);
...}