2

Update2

I guess i have misunderstood a bit about the mcv pattern and fxml in javafx. I belive im on the right way now. Im still a bit confused but im used the example from @GJCode and worked a bit on it.

First at all thanks you, for you answers. It already helped me!
Theres still something i dont understand.
So the MCV pattern is basically diveded in 3 Components, right?
M : Model - Basically the logic of the code. Like a function to do a simple math addition or return a simple text.
C : Controller - Basically the core of the project. Creates a connection between the View and the Model? Basically i would manipulate view components here like setting the Text of a textfield? V : View - Just the raw Gui, no logic. In this case the fxml?

My Project setup atm looks like this : Project setup
(Ignore the Week.java, its unnecessary atm)
My current goal is to create a JavaFX Application thats works via mcv Pattern and it should just print a text in a existing TextField(for now). The text to print will received via the Modelclass.
Main Class :

package main; 
import controller.ControllerClass;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
import model.InitGui;
import view.*;
public class Main extends Application {
    public static void main(String[] args) {  
    launch(args);
    }
    @Override
    public void start(Stage primaryStage) throws Exception {
           try {
                FXMLLoader loader = new FXMLLoader(getClass().getResource("/view/Design.fxml"));
                Parent root = loader.load();
                Scene scene = new Scene(root);
                // optionally add a stylesheet
                primaryStage.setScene(scene);
                // obtain controller
                ControllerClass controller = loader.getController();
                // set model 
                controller.setModel(new InitGui());
                primaryStage.show();
            } catch(Exception e) {
                e.printStackTrace();
            }
    }
}

ControllerClass :

package controller;
import java.io.IOException;

import javafx.application.Application;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.TextField;
import model.InitGui;


public class ControllerClass {
    @FXML
    public TextField fxWeeks;
    public InitGui initGui;
    public void setModel(InitGui initGui) {
        this.initGui = initGui; 
    }
    public ControllerClass () {
        fxWeeks.setText(initGui.getExample());
    }
}

ModelClass :

package model;
import javafx.fxml.FXML;
import javafx.scene.control.TextField;
public class InitGui {
    public String getExample () {
        return "Hello";
    }
} 

My ViewClass (fxml)

<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>
<Pane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="450" prefWidth="450" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.controller.ControllerClass">
    <MenuBar fx:id="menuBar">
        <menus>
            <Menu text="Datei">
               <MenuItem text="Öffnen"/>
               <MenuItem text="Speichern"/>
               <MenuItem text="Speichern unter"/>
               <SeparatorMenuItem  />
               <MenuItem text="Schließen"/>
            </Menu>
        </menus>
    </MenuBar>
    <children>
       <TableView fx:id="fxTable" layoutX="10.0" layoutY="75.0" prefHeight="410.0" prefWidth="600.0"/> 
       <Label fx:id="fxLabel" layoutX="630.0" layoutY="60.0" Text="Kalenderwoche"/> 
       <TextField fx:id="fxWeek" layoutX="630.0"  layoutY="75.0" prefWidth="95.0"/>
       <Button fx:id="fxButton" layoutX="630.0" layoutY="125.0" Text="Arbeitszeit berechnen"/>
       <Label fx:id="fxAlllbl" layoutX="10.0" layoutY="500.0" Text="Gesamt :"/>
       <TextField fx:id="fxAllContent" layoutX="80.0" layoutY="497.0"/>
   </children>
</Pane>

So as far i have understand the Main class will launch the start Methode with launch(args) it will create the gui with the FXMLLoader,scene and setScene.
Those parts i dont really understand : ControllerClass controller = loader.getController();controller.setModel(new InitGui());
Im creating a controller object, but what is loader.getController and what exactly would the "modelsetter" do?
I want to receive datas with the model-component and edit the view with the controller element(add a string received from model to the fxTextField via the controller).

Is my approach even right? Or do i miss something complety? Could you help me with the example im trying to achieve.

Community
  • 1
  • 1
  • Instead of replacing the text field, just change its value. Although that doesn't have anything to do with your exception. You should either resolve that problem first and get your appliation to run, or first post a question about that problem / change this question to be about it. – millimoose Jun 18 '18 at 10:02
  • My hunch is you need to run `Application.launch` and let that create `Gui` for you instead of trying to create it programatically. – millimoose Jun 18 '18 at 10:03
  • Yea, i have also tried to just change the value insetad of the textfield (see my edit in main post). Also the program **runs** when i dont try to create an object an change their value via get/set. –  Jun 18 '18 at 10:08
  • 1
    the object you create manually has nothing to do with the object that creates `Application#launch`. so it is not initialized and the logic in it will pop up outside the thread of the FX application – mr mcwolf Jun 18 '18 at 10:34
  • In addition, `FXMLLoader` will create another instance of an object that will be used as a controller. – mr mcwolf Jun 18 '18 at 10:40
  • Yea and thats exactly my question. Is there anyway to assign the Object i created with Application#launch to the object i manually created. Or is there even a way to get rid of the Application#launch and init a gui like that : `view.start(); view.setTextFieldA("HellO");` I think Jframe works like that, but is there a way to do it with javafx? –  Jun 18 '18 at 10:40
  • try to manually call `Platform::runLater` and initialize your FX components there (you have to get started and `swing` applications on a similar basis). the question, however, is why do you want to change the lifecycle of your app? – mr mcwolf Jun 18 '18 at 10:54
  • It is not a good idea to have your `Application` class also function as a FXML controller (https://stackoverflow.com/a/33304137/639562). In fact, I'm a little confused. You have a class named `ControllerClass` with no `FXML` annotated fields. Then you have your `Gui` class which _does_ have `FXML` annotated fields..? I'm not sure I understand your intent. – Slaw Jun 18 '18 at 11:33
  • my guess is that NPE at `com.view.Gui.setFxWeek(Gui.java:58)` is because `TextField fxWeek` is not initialized in the `fxml`. The `fxml` is missing from your post. This maybe a good example why [mcve] are essential and effective. – c0der Jun 18 '18 at 13:00
  • "Is there anyway to assign the Object i created with Application#launch to the object i manually created" - this is a very, very exotic thing to do. What are you trying to accomplish? JavaFX works the best when the component tree is as unchanging as at all possible and purely declarative - just change the models (the data) it's bound to – millimoose Jun 18 '18 at 14:42
  • Just let Application.launch and other internal mechanisms manage the lifetime of the components; you should almost never need to `new` up GUI controls with FXML. – millimoose Jun 18 '18 at 14:42

1 Answers1

0

usually Main class launch the application so try something like this:

public class Main extends Application {
@Override
public void start(Stage primaryStage) {
    try {
        FXMLLoader loader = new FXMLLoader(getClass().getResource("fxmlFileName.fxml"));
        BorderPane root = loader.load();
        Scene scene = new Scene(root);
        // optionally add a stylesheet
        scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
        primaryStage.setScene(scene);
        // obtain controller
        ControllerClass controller = loader.getController();
        // set model 
        controller.setModel(new Model());
        primaryStage.show();
    } catch(Exception e) {
        e.printStackTrace();
    }
}

public static void main(String[] args) {
    launch(args);
}
}

this is how you can implement MCV pattern. Main class start the application, a view represented by an fxml file is connected to the controller and the class Model represent the logic of your application, so doing setModel() the controller is now linked to the logic. setModel() is just a setter method.

Controller class:

public class ControllerClass {

@FXML
private ResourceBundle resources;

@FXML
private URL location;

@FXML
private TextField textField;

@FXML
private Button button;

private Model model;

public void setModel(Model model) {
      this.model = model;
}

@FXML
 public void changeTextOnClick(ActionEvent event) {
        String text = model.getExample();
        textField.setText(text);
 }

@FXML
void initialize() {
    assert button != null : "fx:id=\"button\" was not injected: check 
    your FXML file 'fxmlFileName.fxml'.";

    assert textField != null : "fx:id=\"textField\" was not injected: check 
    your FXML file 'fxmlFileName.fxml'.";
}

}

And your fxml file:

  <?xml version="1.0" encoding="UTF-8"?>

   <?import javafx.scene.control.Button?>
   <?import javafx.scene.layout.BorderPane?>
   <?import javafx.scene.layout.VBox?>

<BorderPane xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/9" fx:controller="it.gjcode.plusFX.TestController">
   <center>
      <VBox prefHeight="200.0" prefWidth="490.0" BorderPane.alignment="CENTER">
         <children>
            <Button fx:id="button" mnemonicParsing="false" onAction="#changeTextOnClick" text="Change Text" />
         </children>
       <children>
       <TextField fx:id="textField" prefHeight="163.0" prefWidth="161.0" ">
                 <font>
                    <Font size="40.0" />
                 </font>
              </TextField>
       </children>
      </VBox>
   </center>
</BorderPane>

loader.getController simply get a controller object, in this case ControllerClass object, with the model setter your controller can access to the logic of your application, in this case doing model.getExample(). Remember that in MVC pattern logic must be as separate as possible from the controller and the view, so in this way changes on the logic will not affect the other components of your application.

GJCode
  • 1,959
  • 3
  • 13
  • 30
  • Hello, thanks you for ur comment. It really helped me to understand the mcv pattern a bit better. Unfortunately theres still something im not understandig. I edited the main post. –  Jun 19 '18 at 08:18
  • answer edited, hope this can help you a little more. – GJCode Jun 19 '18 at 09:22