简体   繁体   中英

Initializing TableView with columns inside fx:root using FXML

I have a custom class inside a fxml file called TablePatientView . I use it like <TablePatientView fx:id="tablePatientView" layoutX="20.0" layoutY="45.0" prefHeight="409.0" prefWidth="363.0" /> and it works almost fine. The fx:root looks like the following:

<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.*?>

<fx:root type="javafx.scene.control.TableView" xmlns:fx="http://javafx.com/fxml">
    <TableView>
        <columns>
            <TableColumn fx:id="patId" prefWidth="101.0" text="Patient-ID" />
            <TableColumn fx:id="patVorname" prefWidth="69.0" text="Vorname" />
            <TableColumn fx:id="patNachname" prefWidth="98.0" text="Nachname" />
            <TableColumn fx:id="patGebdat" prefWidth="96.0" text="Geburtsdatum" />
        </columns>
    </TableView>
</fx:root>

with the following class

public class TablePatientView extends TableView<TablePatient> {

    private Parent root;

    @FXML
    private TableColumn<TablePatient,String> patId;
    @FXML
    private TableColumn<TablePatient,String> patVorname;
    @FXML
    private TableColumn<TablePatient,String> patNachname;
    @FXML
    private TableColumn<TablePatient, LocalDate> patGebdat;

    public TablePatientView() {
        FXMLLoader loader = new FXMLLoader(Main.class.getResource("/fxml/TablePatientView.fxml"));
        loader.setController(this);
        loader.setRoot(this);
        try {
            root = loader.load();
        } catch (IOException e) {
            e.printStackTrace();
        }
        setCellValueFactories();
        // getColumns().addAll(patId, patVorname, patNachname, patGebdat); works, but not with plain fxml
    }

    private void setCellValueFactories() {
        patId.setCellValueFactory(param -> param.getValue().idProperty().asString());
        patVorname.setCellValueFactory(param -> param.getValue().vornameProperty());
        patNachname.setCellValueFactory(param -> param.getValue().nachnameProperty());
        patGebdat.setCellValueFactory(param -> param.getValue().gebDatProperty());
    }
}

The columns are correctly initialized, but for some reason not added as columns to the TableView. I know I can do it via java code (see the commented line in the code above) but I would like to know why this doesn't work with just fxml. Using the <TableView> directly inside fxml without a custom class and root works just fine.

The <fx:root type="TableView"> references a TableView which is supplied via the call to loader.setRoot(this) .

You have defined another TableView inside that TableView 1 . The columns are added to that TableView , not the one referenced by <fx:root> .

The correct FXML is

<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.*?>

<fx:root type="javafx.scene.control.TableView" xmlns:fx="http://javafx.com/fxml">
    <columns>
        <TableColumn fx:id="patId" prefWidth="101.0" text="Patient-ID" />
        <TableColumn fx:id="patVorname" prefWidth="69.0" text="Vorname" />
        <TableColumn fx:id="patNachname" prefWidth="98.0" text="Nachname" />
        <TableColumn fx:id="patGebdat" prefWidth="96.0" text="Geburtsdatum" />
    </columns>
</fx:root> 

Alternative approach

I'm not a big fan of subclassing control classes, such as TableView . The reason is that you're not really adding functionality to the TableView , you're just essentially setting properties on it, and this is not a good use of inheritance.

Consider instead using a creational pattern. A simple static factory method works well, and plays nicely with FXML via the fx:factory attribute.

TablePatientView.fxml:

<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.*?>

<TableView xmlns:fx="http://javafx.com/fxml" fx:controller="com.jamesd.examples.TablePatientController">
    <columns>
        <TableColumn fx:id="patId" prefWidth="101.0" text="Patient-ID" />
        <TableColumn fx:id="patVorname" prefWidth="69.0" text="Vorname" />
        <TableColumn fx:id="patNachname" prefWidth="98.0" text="Nachname" />
        <TableColumn fx:id="patGebdat" prefWidth="96.0" text="Geburtsdatum" />
    </columns>
</TableView>

TablePatientController.java:

public class TablePatientContorller {

    @FXML
    private TableColumn<TablePatient,String> patId;
    @FXML
    private TableColumn<TablePatient,String> patVorname;
    @FXML
    private TableColumn<TablePatient,String> patNachname;
    @FXML
    private TableColumn<TablePatient, LocalDate> patGebdat;
    
    @FXML
    private void initialize() {
        setCellValueFactories();
    }

    private void setCellValueFactories() {
        patId.setCellValueFactory(param -> param.getValue().idProperty().asString());
        patVorname.setCellValueFactory(param -> param.getValue().vornameProperty());
        patNachname.setCellValueFactory(param -> param.getValue().nachnameProperty());
        patGebdat.setCellValueFactory(param -> param.getValue().gebDatProperty());
    }
}

Tables.java:

public class Tables {

    public static TableView<TablePatient> tablePatientView() throws Exception {
        FXMLLoader loader = new FXMLLoader(Main.class.getResource("/fxml/TablePatientView.fxml"));
        return loader.load();
    }
}

And then you can use this in FXML with

<Tables fx:factory="tablePatientView" />

Footnote

  1. What happens here is that the second TableView is added to the original TableView 's items list, since the @DefaultProperty of a TableView is that list. So eventually the table view would attempt to display that as a row in the table. It seems like there should be a ClassCastException at some point, but possibly it gets silently squashed since FXML can't specify generic types.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM