1

I have a problem with filling a ListView in my main window with objects per button event in another open window.

I tried several things, but it never seems to work unless I do it per button event in the actual window where the ListView is located. I wondered if it had anything to do with using an instance of the controller for the main window, but I'm new to JavaFX and I don't really know what I'm doing, tbh.

Some additional information:

Infos is a class where Infos are stored that I need to access in more than one controller class;
gastConrim is the method called in another open window (AddEditGastController);
the last part is the part that works (updating it per button event in the main window where the ListView is located) which would be similar to a "update ListView"-Button, but it's kinda inconvinient if you have to update the ListView manually.

public abstract class Infos {

    public static String ID; 

    static Hotelsystem ht = new Hotelsystem();

    public static Hotelsystem getHt(){
        return ht;
    }

    static void alert(String titel, String content) {
        Alert alert = new Alert(AlertType.CONFIRMATION);
        alert.setTitle(titel);
        alert.setHeaderText(null);
        alert.setContentText(content);
        alert.show();
    }

    static boolean idAbfragen(String titel, String content, String alertContent) {
        TextInputDialog dialog = new TextInputDialog();
        dialog.setTitle(titel);
        dialog.setHeaderText(null);
        dialog.setContentText(content);

        Optional<String> input = dialog.showAndWait();
        if (!input.isPresent() || input.get().trim().isEmpty()) {
            Alert alert = new Alert(AlertType.CONFIRMATION);
            alert.setTitle(titel);
            alert.setHeaderText(null);
            alert.setContentText(alertContent);
            alert.showAndWait();
            return false;
        }
        else {
            ID = input.get();
            return true;
        }
    }

    static ObservableList<Gast> gastData = FXCollections.observableArrayList();

    public static void updateGast() {
        gastData.setAll(ht.getGastList());
    }

}
@FXML
void gastConfirm(ActionEvent event) throws IOException {


    FXMLLoader fxmlLoader= new FXMLLoader(getClass().getResource("../gui/mainWindowGast.fxml"));
    Parent root = (Parent) fxmlLoader.load();
    MWControllerGast controller = (MWControllerGast) fxmlLoader.getController();


    double rabatt = 0.0;
    if (gastNameInput.getText().trim().isEmpty() || anredeToggle.getSelectedToggle() == null || gastGB.getValue() == null || gastTfInput.getText().trim().isEmpty() ||
            gastStrasseInput.getText().trim().isEmpty() || gastStadtInput.getText().trim().isEmpty() || gastLandInput.getText().trim().isEmpty()) {
            Infos.alert("Fehler", "Es fehlen Informationen.");
    }
    else {
        if (gastStatus.isSelected()) {
            if (!gastRabattInput.getText().trim().isEmpty()) {
                String rabattInput = gastRabattInput.getText();
                if (rabattInput.contains(","))
                    rabattInput.replace(",", ".");
                rabatt = Double.parseDouble(rabattInput);
                Infos.getHt().getHinzufuegen().vipHinzufuegen(gastNameInput.getText(), gastAnredeRadio1.getText(), gastGB.getValue(), gastTfInput.getText(), gastStrasseInput.getText(), gastStadtInput.getText(), gastLandInput.getText(), rabatt);
            }
            Infos.ht.getHinzufuegen().vipHinzufuegen(gastNameInput.getText(), gastAnredeRadio1.getText(), gastGB.getValue(), gastTfInput.getText(), gastStrasseInput.getText(), gastStadtInput.getText(), gastLandInput.getText(), rabatt);

        } else
            Infos.ht.getHinzufuegen().gastHinzufügen(gastNameInput.getText(), gastAnredeRadio1.getText(), gastGB.getValue(), gastTfInput.getText(), gastStrasseInput.getText(), gastStadtInput.getText(), gastLandInput.getText());

        System.out.println(rabatt);
        gastNameInput.clear();
        gastAnredeRadio1.setSelected(false);
        gastAnredeRadio2.setSelected(false);
        gastGB.setValue(null);
        gastTfInput.clear();
        gastStrasseInput.clear();
        gastStadtInput.clear();
        gastLandInput.clear();
        gastStatus.setSelected(false);
        gastRabattInput.clear();


        Infos.updateGast();

        controller.gastTable.getItems().setAll(Infos.gastData);


    }

@FXML
ListView<Gast> gastTable;

public ListView<Gast> getGastTable() {
    return gastTable;
}

@FXML
void sortieren(ActionEvent event) {

    Infos.updateGast();

    gastTable.getItems().setAll(Infos.gastData);
}

The expected result: Update the ListView in gastConfirm whenever I add a new Object.

The actual result: Nothing happens. At least nothing I can see in the console or user interface. It just doesn't add the object to the ListView.

fabian
  • 72,938
  • 12
  • 79
  • 109
Maria
  • 11
  • 1
  • Not sure how you load the main window. In `gastConfirm` you seem to load a scene/controller combination that is never shown though. Furthermore I've noted that you make use of the `setAll` method to assign the contents of the `items` list. If you do this when initializing the problematic scene in the first place and not just in the part where you load a throwaway scene, this is problematic, since `ObservableList.setAll` simply clears the list and then copies all elements from the list passed as parameter. Changes done later have no effect. Use `setItems` passing the OL as parameter instead?! – fabian Jun 18 '19 at 17:17
  • Have a look at SortedList Class the way u sort your items is strange – Alex Jun 18 '19 at 17:24
  • @Alex I.... don't actually sort them anywhere tho. If you are referring to the `sortieren()` then that's just a method I call on OnAction that I used to see if I'd be able to add items to the `ListView` like that. – Maria Jun 18 '19 at 17:29
  • The best way to achieve this is to study [this](https://stackoverflow.com/questions/32342864/applying-mvc-with-javafx). You will need an Observable List in your model that you set to the ListView. – SedJ601 Jun 18 '19 at 19:18

2 Answers2

0

You need to look at James D answer here. I have created a watered down version of James answer. Comments in the code.

Main

import java.io.IOException;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.stage.Stage;

/**
 *
 * @author blj0011
 */
public class JavaFXApplication124 extends Application
{
    @Override
    public void start(Stage primaryStage)
    {        
        try 
        {
            //Load both Stages while passing the model to each.
            FXMLLoader listLoader = new FXMLLoader(getClass().getResource("ListFXML.fxml"));
            Scene scene = new Scene(listLoader.load());
            ListController listController = listLoader.getController();            
            primaryStage.setScene(scene);
            primaryStage.setTitle("Hello World!");

            FXMLLoader editorLoader = new FXMLLoader(getClass().getResource("EditorFXML.fxml"));
            Stage secondStage = new Stage();
            secondStage.setScene(new Scene(editorLoader.load()));
            EditorController editorController = editorLoader.getController();            

            DataModel model = new DataModel();
            listController.initModel(model);
            editorController.initModel(model);

            primaryStage.show();
            secondStage.show();
        } 
        catch (IOException ex)
        {
            System.out.println(ex.toString());
        }
    }

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args)
    {
        launch(args);
    }
}

List Controller

import java.net.URL;
import java.util.ResourceBundle;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;

/**
 * FXML Controller class
 *
 * @author blj0011
 */
public class ListController implements Initializable {
    @FXML ListView<TestObject> listView;

    DataModel model;


    /**
     * Initializes the controller class.
     * @param url
     * @param rb
     */
    @Override
    public void initialize(URL url, ResourceBundle rb) {
        // TODO
    }    

    //Use to set model
    public void initModel(DataModel model) {
        if (this.model != null) {
            throw new IllegalStateException("Model can only be initialized once");
        }
        this.model = model ;

        //Set ListView cell factory.
        listView.setCellFactory((ListView<TestObject> param) -> {
            ListCell<TestObject> cell = new ListCell<TestObject>() {

                @Override
                protected void updateItem(TestObject item, boolean empty) {
                    super.updateItem(item, empty);
                    if (item != null) {
                        setText(item.getName());
                    } else {
                        setText("");
                    }
                }
            };
            return cell;
        });
        listView.setItems(model.getTestObjectsList());//Set List items
    }
}

List FXML

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

<?import javafx.scene.control.ListView?>
<?import javafx.scene.layout.AnchorPane?>


<AnchorPane id="AnchorPane" prefHeight="498.0" prefWidth="755.0" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/11.0.1" fx:controller="javaapplication12.ListController">
   <children>
      <ListView fx:id="listView" layoutX="234.0" layoutY="53.0" prefHeight="393.0" prefWidth="288.0" />
   </children>
</AnchorPane>

Editor Controller

import java.net.URL;
import java.util.ResourceBundle;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Button;
import javafx.scene.control.TextField;

/**
 * FXML Controller class
 *
 * @author blj0011
 */
public class EditorController implements Initializable {
    @FXML Button btn;
    @FXML TextField tf;

    DataModel model;

    /**
     * Initializes the controller class.
     * @param url
     * @param rb
     */
    @Override
    public void initialize(URL url, ResourceBundle rb) {
        // TODO


    }    

    //use to set model
    public void initModel(DataModel model) {
        if (this.model != null) {
            throw new IllegalStateException("Model can only be initialized once");
        }
        this.model = model ;

        //set button action
        btn.setOnAction((event) -> {
            TestObject testObject = new TestObject(tf.getText().isBlank() ? "Dummy Object" : tf.getText());
            model.getTestObjectsList().add(testObject);
        });
    }

}

Editor FXML

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

<?import javafx.scene.control.Button?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.AnchorPane?>


<AnchorPane id="AnchorPane" prefHeight="400.0" prefWidth="600.0" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/11.0.1" fx:controller="javaapplication12.EditorController">
   <children>
      <TextField fx:id="tf" layoutX="226.0" layoutY="94.0" />
      <Button fx:id="btn" layoutX="274.0" layoutY="175.0" mnemonicParsing="false" text="Button" />
   </children>
</AnchorPane>

Model

import javafx.collections.FXCollections;
import javafx.collections.ObservableList;

/**
 *
 * @author blj0011
 */
public class DataModel {
    private final ObservableList<TestObject> testObjects = FXCollections.observableArrayList();

    public ObservableList<TestObject> getTestObjectsList() {
        return testObjects ;
    }
}

TestObject

/**
 *
 * @author blj0011
 */
class TestObject {
    private String name;

    public TestObject(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "TestObject{" + "name=" + name + '}';
    }
}
SedJ601
  • 10,977
  • 3
  • 40
  • 54
  • 1
    Thank you so much! I've heard about Model View Controller concept, but it never came to mind that I should maybe be using it for this... I'll look into it! :) – Maria Jun 18 '19 at 20:10
-2

What I don't get is why do you load the controller and don't use it further...

FXMLLoader fxmlLoader= new FXMLLoader(getClass().getResource("../gui/mainWindowGast.fxml"));
Parent root = (Parent) fxmlLoader.load();
MWControllerGast controller = (MWControllerGast) fxmlLoader.getController();

You load the new Controller but you neither use the controller nor the root you loaded to actually show the View anywhere. Not sure if you do this elsewhere(actaully u can not because it is a local variable) as I dont see the whole code of yours but clearly something is wrong there. I am missing something like:

someParent.add(root);
Alex
  • 427
  • 6
  • 16
  • When I don't have these lines, trying to load the `root`, it gives me a `NullPointerException` since I do use the controller to have acces to the ListView. I came across a few posts where they said I'd have to load it to be able to use the instance of the controller and it did solve the problem when I added this line. – Maria Jun 18 '19 at 17:27
  • Yes but you need to ->use – Alex Jun 18 '19 at 22:28
  • Like which relationship do your controllers have. Are they in separate windows or alongside in a parent Controller. Separate stages? I mean in your code example you create a new instance of a controller which is never displayed anyway. – Alex Jun 18 '19 at 22:34