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
  • 80,457
  • 12
  • 86
  • 114
Maria
  • 11
  • 2
  • 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
  • 12,173
  • 3
  • 41
  • 59
  • 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
  • 521
  • 7
  • 17
  • 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<- the root variable somewhere to be able to see it @Maria . Or did you add the MWControllerGast fxml file in the parent fxml file? in this case your way getting the controller is wrong. I mean you load it atm but never show it anywhere no wonder you can not see the list filed up. But from the few code sniplets you posted it is hard to tell what is going on. – 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