I want to make TableViewCell using only fxml. How can I do it.
Now I have a model class DuplicateFileInfo
class DuplicateFileInfo(var id: Long, var path: String, var editableField: String?) {}
And I have TableView
<TableView AnchorPane.bottomAnchor="50.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0"
editable="true"
layoutX="121.0" layoutY="6.0" fx:id="duplicatesList">
<columns>
<TableColumn prefWidth="300.0" text="%file.filename" fx:id="fileNameColumn" editable="false">
<cellValueFactory>
<PropertyValueFactory property="path" />
</cellValueFactory>
</TableColumn>
<TableColumn prefWidth="150.0" text="%file.EditableField" fx:id="editableColumn">
<cellValueFactory>
<PropertyValueFactory property="editableField" />
</cellValueFactory>
<cellFactory>
<TextFieldTableCell fx:factory="forTableColumn" />
</cellFactory>
</TableColumn>
</columns>
</TableView>
In this case I have editable table view. But the value doesn't set to model after editing is finish.
Is it possible to make this work without codding?
Thanks for James_D
I could get the result.
The model class with kotlin should be like this
class DuplicateFileInfo(id: Long, path: String, shouldBeDeleted: Boolean) {
private val id: LongProperty
private val path: StringProperty
private val shouldBeDeleted: BooleanProperty
init {
this.id = SimpleLongProperty(id)
this.path = SimpleStringProperty(path)
this.shouldBeDeleted = SimpleBooleanProperty(shouldBeDeleted)
}
fun getId(): Long {
return id.get()
}
fun idProperty(): LongProperty {
return id
}
fun setId(id: Long) {
this.id.set(id)
}
fun getPath(): String {
return path.get()
}
fun pathProperty(): StringProperty {
return path
}
fun setPath(path: String) {
this.path.set(path)
}
var isShouldBeDeleted: Boolean
get() = shouldBeDeleted.get()
set(shouldBeDeleted) = this.shouldBeDeleted.set(shouldBeDeleted)
fun shouldBeDeletedProperty(): BooleanProperty {
return shouldBeDeleted
}
override fun toString(): String {
val sb = StringBuffer("DuplicateFileInfo{")
sb.append("id=").append(id.get())
sb.append(", path=").append(path.get())
sb.append(", shouldBeDeleted=").append(shouldBeDeleted.get())
sb.append('}')
return sb.toString()
}
}
Related
I am trying to create a simple Contacts Application in JavaFX. It has a main window with TableView and a DialogPane opening when I want to create new contact. In the DialogPane there are several TextFields from which I want to collect text in order to create a Contacts List. My problem is that when I want to read the input from the DialogPane (from TextFields), which is the separate fxml file with separate controller (separate from main Controller) the application runs an error (java.lang.NullPointerException). And when I put a TextField in my main window FXML file, then I can access this text from the textField just fine. Why do I get an error when I want to read data from DialogPane (Error-->File: Controller.java, I commented the section where I get an error)?? I am stuck. Can anyone suggest what am I doing wrong? Here is my code:
Main.java
public class Main extends Application {
#Override
public void start(Stage primaryStage) throws Exception{
Parent root = FXMLLoader.load(getClass().getResource("mainwindow.fxml"));
primaryStage.setTitle("Your Contacts");
primaryStage.setScene(new Scene(root, 900, 600));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Controller.java
public class Controller {
#FXML
private BorderPane mainPanel;
public ObservableList<Contact> contactsList = FXCollections.observableArrayList();
#FXML
public void showAddContactDialog() {
Dialog<ButtonType> dialog = new Dialog<>();
dialog.initOwner(mainPanel.getScene().getWindow());
dialog.setTitle("Add new contact");
FXMLLoader fxmlLoader = new FXMLLoader();
fxmlLoader.setLocation(getClass().getResource("addContactDialog.fxml"));
try {
dialog.getDialogPane().setContent(fxmlLoader.load());
} catch (IOException e) {
Alert alert = new Alert(Alert.AlertType.ERROR);
alert.setTitle("Error");
alert.setHeaderText(null);
alert.setContentText("Couldn't load the dialog");
alert.showAndWait();
e.printStackTrace();
return;
}
dialog.getDialogPane().getButtonTypes().add(ButtonType.OK);
dialog.getDialogPane().getButtonTypes().add(ButtonType.CANCEL);
Optional<ButtonType> result = dialog.showAndWait();
if(result.isPresent() && result.get() == ButtonType.OK) {
//========================================================================================
//here is the error, I cannot read values from addContactDialog controller
ContactController contactController = new ContactController();
String firstName = contactController.getNewContact().getFirstName();
String lastName = contactController.getNewContact().getLastName();
String phoneNumber = contactController.getNewContact().getPhoneNumber();
String emailAddress = contactController.getNewContact().getEmailAddress();
Contact newContact = new Contact(firstName, lastName, phoneNumber, emailAddress);
contactsList.add(newContact);
//or alternatively
// Contact newContact = contactController.getNewContact();
// contactsList.add(newContact);
//========================================================================================
}
}
}
ContactController.java
public class ContactController {
#FXML
private TextField firstNameField;
#FXML
private TextField lastNameField;
#FXML
private TextField phoneNumberFiled;
#FXML
private TextField emailField;
public Contact getNewContact() {
String firstName = firstNameField.getText();
String lastName = lastNameField.getText();
String phoneNumber = phoneNumberFiled.getText();
String emailAddress = emailField.getText();
Contact newContact = new Contact(firstName, lastName, phoneNumber, emailAddress);
return newContact;
}
Contact.java
public class Contact {
private SimpleStringProperty firstName = new SimpleStringProperty("");
private SimpleStringProperty lastName = new SimpleStringProperty("");
private SimpleStringProperty phoneNumber = new SimpleStringProperty("");
private SimpleStringProperty emailAddress = new SimpleStringProperty("");
public Contact() {
}
public Contact(String firstName, String lastName, String phoneNumber, String emailAddress) {
this.firstName.set(firstName);
this.lastName.set(lastName);
this.phoneNumber.set(phoneNumber);
this.emailAddress.set(emailAddress);
}
public String getFirstName() {
return firstName.get();
}
public SimpleStringProperty firstNameProperty() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName.set(firstName);
}
public String getLastName() {
return lastName.get();
}
public SimpleStringProperty lastNameProperty() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName.set(lastName);
}
public String getPhoneNumber() {
return phoneNumber.get();
}
public SimpleStringProperty phoneNumberProperty() {
return phoneNumber;
}
public void setPhoneNumber(String phoneNumber) {
this.phoneNumber.set(phoneNumber);
}
public String getEmailAddress() {
return emailAddress.get();
}
public SimpleStringProperty emailAddressProperty() {
return emailAddress;
}
public void setEmailAddress(String emailAddress) {
this.emailAddress.set(emailAddress);
}
#Override
public String toString() {
return "Contact{" +
"firstName=" + firstName +
", lastName=" + lastName +
", phoneNumber=" + phoneNumber +
", emailAddress=" + emailAddress +
'}';
}
}
mainwindow.fxml
<BorderPane fx:id="mainPanel" fx:controller="sample.Controller"
xmlns:fx="http://javafx.com/fxml">
<top>
<MenuBar>
<menus>
<Menu text="Contacts">
<items>
<MenuItem text="Add new" onAction="#showAddContactDialog"/>
</items>
<items>
<MenuItem text="Edit" />
</items>
<items>
<MenuItem text="Delete"/>
</items>
<items>
<MenuItem text="Exit"/>
</items>
</Menu>
</menus>
<menus>
<Menu text="Info">
<items>
<MenuItem text="About"/>
</items>
</Menu>
</menus>
</MenuBar>
</top>
<center>
<TableView fx:id="contactsTable">
<columnResizePolicy>
<TableView fx:constant="CONSTRAINED_RESIZE_POLICY"/>
</columnResizePolicy>
<columns>
<TableColumn text="First Name">
<cellValueFactory>
<PropertyValueFactory property="firstName"/>
</cellValueFactory>
</TableColumn>
<TableColumn text="Last Name">
<cellValueFactory>
<PropertyValueFactory property="lastName"/>
</cellValueFactory>
</TableColumn>
<TableColumn text="Phone Number">
<cellValueFactory>
<PropertyValueFactory property="phoneNumber"/>
</cellValueFactory>
</TableColumn>
<TableColumn text="Email">
<cellValueFactory>
<PropertyValueFactory property="emailAddress"/>
</cellValueFactory>
</TableColumn>
</columns>
</TableView>
</center>
</BorderPane>
addContactDialog.fxml
<DialogPane fx:controller="sample.ContactController" xmlns:fx="http://javafx.com/fxml">
<headerText>
Fill in the information for the new Contact
</headerText>
<content>
<GridPane vgap="10" hgap="10">
<Label text="First Name: " GridPane.rowIndex="0" GridPane.columnIndex="0"/>
<TextField fx:id="firstNameField" GridPane.rowIndex="0" GridPane.columnIndex="1"/>
<Label text="Last Name: " GridPane.rowIndex="1" GridPane.columnIndex="0"/>
<TextField fx:id="lastNameField" GridPane.rowIndex="1" GridPane.columnIndex="1"/>
<Label text="Phone Number: " GridPane.rowIndex="2" GridPane.columnIndex="0"/>
<TextField fx:id="phoneNumberField" GridPane.rowIndex="2" GridPane.columnIndex="1"/>
<Label text="Notes: " GridPane.rowIndex="3" GridPane.columnIndex="0"/>
<TextField fx:id="notesField" GridPane.rowIndex="3" GridPane.columnIndex="1"/>
</GridPane>
</content>
</DialogPane>
Ok, I found the mistake - I made a typo in the ContactController.java file. It should have phoneNumberField instead of phoneNumberFiled String...
Did you tried putting .toString
like this
String firstName = contactController.getNewContact().getFirstName().toString();
I need to pass the date property to the setCellValueFactory , this is the code for the seters and geters of my Persona class, what is the correct method to do that, i think i having an issue whit the correct declaration, so i need a real direction here, i need to declare some date in the java.sql.date class, do some reference? any idea?. please a little help here.
package application;
import java.time.LocalDate;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
public class Persona {
private StringProperty nombres;
private StringProperty apellidos;
private IntegerProperty id_cliente;
private ObjectProperty <LocalDate>fechacliente;
public Persona ( String nombres, String apellidos, Integer id_cliente, Object fechacliente) {
this.nombres= new SimpleStringProperty (nombres);
this.apellidos= new SimpleStringProperty ( apellidos);
this.id_cliente=new SimpleIntegerProperty (id_cliente);
this.fechacliente= new SimpleObjectProperty<LocalDate>();
}
public Object getFecha() {
return fechacliente.get();
}
public void setFecha(Object fechacliente) {
this.fechacliente=new SimpleObjectProperty<>();
}
public String getNombres() {
return nombres.get();
}
public void setNombres(String nombres) {
this.nombres=new SimpleStringProperty (nombres);
}
public String getApellidos() {
return apellidos.get();
}
public void setApellidos(String apellidos) {
this.apellidos=new SimpleStringProperty ( apellidos);
}
public Integer getId_cliente() {
return id_cliente.get();
}
public void setid_cliente(Integer id_cliente) {
this.id_cliente=new SimpleIntegerProperty (id_cliente);
}
}
Some of Controller here that set the values to the tableview
public void initialize(URL arg0, ResourceBundle arg1) {
clienteid.setCellValueFactory(new PropertyValueFactory <Persona, Integer>("id_cliente"));
nombrescol.setCellValueFactory(new PropertyValueFactory <Persona, String>("nombres"));
apellidoscol.setCellValueFactory(new PropertyValueFactory <Persona, String>("apellidos"));
fechacli.setCellValueFactory(new PropertyValueFactory <Persona, LocalDate>("fechacliente"));
seleccionaregistros();
seleccionanombre();
seleccionapellido();
}
this is the method i am using to retrieve the data in the tableview but the date does not show
public void seleccionaregistros() {
ObservableList <Persona> data =FXCollections.observableArrayList();
Connection conn=null;{
try {
conn = DriverManager.getConnection("jdbc:sqlserver://localhost:1433;databaseName=prueba", "sa", "milkas87");
Statement mostrar=conn.createStatement();
ResultSet rs;
rs= mostrar.executeQuery("select * from cliente");
while ( rs.next() )
{
data.add(new Persona(
rs.getString("nombre"),
rs.getString("apellido"),
rs.getInt("id"),
rs.getDate(4)
));
tablacliente.setItems(data);
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
The date does not show in the tableview , i need to format the date to pass that value in the tableview i think. please some orientation here be helpful.
this is my FXML Code
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.layout.AnchorPane?>
<AnchorPane prefHeight="497.0" prefWidth="943.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="application.ConexionController">
<children>
<Pane layoutY="-3.0" prefHeight="605.0" prefWidth="1084.0">
<children>
<Button fx:id="btn" layoutX="145.0" layoutY="109.0" mnemonicParsing="false" onAction="#cargarconexion" prefHeight="46.0" prefWidth="117.0" text="Prueba Conexion" />
<Button fx:id="mtn" layoutX="15.0" layoutY="183.0" mnemonicParsing="false" onAction="#cargarregistro" prefHeight="46.0" prefWidth="117.0" text="Inserta Registro" />
<Label layoutX="14.0" layoutY="279.0" prefHeight="17.0" prefWidth="105.0" text="NOMBRES" />
<Label layoutX="15.0" layoutY="327.0" prefHeight="17.0" prefWidth="79.0" text="APELLIDOS" />
<TextField fx:id="nm" layoutX="159.0" layoutY="275.0" prefHeight="25.0" prefWidth="149.0" />
<TextField fx:id="ap" layoutX="159.0" layoutY="323.0" />
<Button fx:id="lmp" layoutX="159.0" layoutY="484.0" mnemonicParsing="false" onAction="#borrarcasillatexto" prefHeight="25.0" prefWidth="150.0" text="Limpiar Texto" />
<TableView fx:id="tablacliente" layoutX="355.0" layoutY="15.0" prefHeight="383.0" prefWidth="696.0">
<columns>
<TableColumn fx:id="clienteid" prefWidth="159.0" text="ID" />
<TableColumn fx:id="nombrescol" prefWidth="159.0" text="NOMBRES" />
<TableColumn fx:id="apellidoscol" minWidth="0.0" prefWidth="169.0" text="APELLIDOS" />
<TableColumn fx:id="fechacli" prefWidth="235.0" text="FECHA DE NACIMIENTO" />
</columns>
</TableView>
<Button fx:id="mts" layoutX="15.0" layoutY="109.0" mnemonicParsing="false" onAction="#mostrartodo" prefHeight="46.0" prefWidth="117.0" text="Mostrar" />
<TextField fx:id="bq" layoutX="554.0" layoutY="417.0" prefHeight="25.0" prefWidth="149.0" />
<Button fx:id="bqd" layoutX="758.0" layoutY="417.0" mnemonicParsing="false" onAction="#buscanm" prefHeight="25.0" prefWidth="155.0" text="BUSCAR NOMBRE" />
<Button fx:id="bqape" layoutX="758.0" layoutY="458.0" mnemonicParsing="false" onAction="#buscaape" prefHeight="25.0" prefWidth="155.0" text="BUSCAR POR APELLIDO" />
<TextField fx:id="bqa" layoutX="554.0" layoutY="458.0" />
<ComboBox layoutX="159.0" layoutY="430.0" prefWidth="150.0" />
<Label layoutX="15.0" layoutY="434.0" prefHeight="17.0" prefWidth="55.0" text="GENERO" />
<MenuBar fx:id="menucombo" layoutY="3.0">
<menus>
<Menu mnemonicParsing="false" text="Agregar">
<items>
<MenuItem mnemonicParsing="false" onAction="#inicializacombo" text="Datos Cliente" />
</items>
</Menu>
</menus>
</MenuBar>
<Button fx:id="botonborrar" layoutX="758.0" layoutY="507.0" mnemonicParsing="false" onAction="#borraregistroid" prefHeight="25.0" prefWidth="155.0" text="BORRAR REGISTRO" />
<TextField fx:id="borrar" layoutX="554.0" layoutY="507.0" />
<DatePicker fx:id="mifecha" layoutX="158.0" layoutY="371.0" prefHeight="25.0" prefWidth="150.0" />
<Label layoutX="15.0" layoutY="375.0" prefHeight="17.0" prefWidth="150.0" text="FECHA DE NACIMIENTO" />
</children>
</Pane>
</children>
</AnchorPane>
You're using JavaFX properties incorrectly. A property for a JavaFX Bean needs a getter, setter (if writable), and a property getter. Also, the setter should not be creating a new property each time; it should be setting the value of the existing property.
To simplify your example, if we have a Person with a single property, name, it would look like this:
public class Person {
private final StringProperty name = new SimpleStringProperty(this, "name");
public Person(String name) {
setName(name);
}
public final void setName(String name) { // setter
this.name.set(name);
}
public final String getName() { // getter
return name.get();
}
public final StringProperty nameProperty() { // property getter
return name;
}
}
Note: You can still initialize the properties in the constructor, instead of at the field declaration, if you want.
To use the name property in a TableView or TreeTableView you would simply return it in the cellValueFactory. Example using a TableView:
TableView<Person> table = new TableView<>();
TableColumn<Person, String> nameCol = new TableColumn<>("Name");
nameCol.setCellValueFactory(features -> features.getValue().nameProperty());
table.getColumns().add(nameCol);
Another issue with your code is that you're using a raw ObjectProperty. Don't use raw types. Instead, you should be using an ObjectProperty<Date> where Date is java.sql.Date. For example:
public class Person {
private final ObjectProperty<Date> clientDate = new SimpleObjectProperty<>(this, "clientDate");
public final void setClientDate(Date clientDate) {
this.clientDate.set(clientDate);
}
public final Date getClientDate() {
return clientDate.get();
}
public final ObjectProperty<Date> clientDateProperty() {
return clientDate;
}
}
Note: It'd probably be better to use one of the java.time classes (e.g. LocalDate, OffsetDateTime, etc...).
And again, to add it to a TableView it'd look like:
TableView<Person> table = new TableView<>();
TableColumn<Person, Date> clientDateCol= new TableColumn<>("Client Date");
clientDateCol.setCellValueFactory(features -> features.getValue().clientDateProperty());
table.getColumns().add(clientDateCol);
I have this TableView
<TableView fx:id="tableView">
<columns>
<TableColumn prefWidth="220.0" text="Source">
<cellValueFactory>
<PropertyValueFactory property="sourceContract" />
</cellValueFactory>
</TableColumn>
</columns>
<items>
<FXCollections fx:factory="observableArrayList">
<GridRowModel sourceContract="some contract" />
</FXCollections>
</items>
</TableView>
and these classes
public class GridRowModel {
private ObjectProperty<ContractConfig> sourceContract = new SimpleObjectProperty<>();
public GridRowModel() {
}
public ObjectProperty<ContractConfig> sourceContractProperty() {
return sourceContract;
}
public ContractConfig getSourceContract() {
return sourceContract.get();
}
public void setSourceContract(ContractConfig sourceContract) {
this.sourceContract.set(sourceContract);
}
}
public class ContractConfig {
private String name;
private String userFriendlyName;
public ContractConfig() {
}
public ContractConfig(String name) {
this.name = name;
}
public void setName(String name) {
this.name = name;
}
public void setUserFriendlyName(String userFriendlyName) {
this.userFriendlyName = userFriendlyName;
}
public String getName() {
return name;
}
public String getUserFriendlyName() {
return userFriendlyName;
}
}
I get this obvious error:
Caused by: java.lang.IllegalArgumentException: Unable to coerce some contract to class com.ui.util.ContractConfig.
at com.sun.javafx.fxml.BeanAdapter.coerce(BeanAdapter.java:496)
at com.sun.javafx.fxml.BeanAdapter.put(BeanAdapter.java:258)
at com.sun.javafx.fxml.BeanAdapter.put(BeanAdapter.java:54)
I also tried this
public void setSourceContract(String sourceContract) {
ContractConfig cc = new ContractConfig();
cc.setUserFriendlyName(sourceContract);
this.sourceContract.set(cc);
}
But I get this error
Caused by: com.sun.javafx.fxml.PropertyNotFoundException: Property "sourceContract" does not exist or is read-only.
at com.sun.javafx.fxml.BeanAdapter.put(BeanAdapter.java:253)
at com.sun.javafx.fxml.BeanAdapter.put(BeanAdapter.java:54)
at javafx.fxml.FXMLLoader$Element.applyProperty(FXMLLoader.java:512)
Is it possible to use ObjectProperty with FXML values and if so, how can I use my ContractConfig object in the FXML?
You use the wrong fxml code for the class structure you've created. It should look like this instead:
<GridRowModel>
<sourceContract>
<ContractConfig name="some contract"/>
</sourceContract>
</GridRowModel>
You can also add a constructor with #NamedArg to GridRowModel and use
<GridRowModel sourceContract="some contract" />
private final ObjectProperty<ContractConfig> sourceContract;
private GridRowModel(ContractConfig sourceContract) {
this.sourceContract = new SimpleObjectProperty<>(sourceContract);
}
public GridRowModel() {
this((ContractConfig) null);
}
public GridRowModel(#NamedArg("sourceContract") String sourceContract) {
this(new ContractConfig(sourceContract));
}
I have the following tableView in fxml
<TableView fx:id="tableView" prefHeight="525.0" prefWidth="814.0">
<columns>
<TableColumn prefWidth="75.0">
<graphic><ToggleButton fx:id="mainToggleButton" maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" onAction="#onMainToggleButtonAction" text="Start all" /></graphic>
<cellFactory><ToggleButtonTableCellFactory maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" activatedText="Started" deactivatedText="Stopped"/></cellFactory>
<cellValueFactory><PropertyValueFactory property="state"/></cellValueFactory>
</TableColumn>
<TableColumn prefWidth="75.0" text="Side">
<cellValueFactory><PropertyValueFactory property="side"/></cellValueFactory>
</TableColumn>
<TableColumn prefWidth="75.0" text="Source">
<cellValueFactory><PropertyValueFactory property="sourceContract"/></cellValueFactory>
</TableColumn>
<TableColumn prefWidth="75.0" text="Reference" editable="true">
<cellFactory>
<ChoiceBoxTableCellFactory maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308">
<items>
<FXCollections fx:factory="observableArrayList">
<String fx:value="r01" />
<String fx:value="r02" />
</FXCollections>
</items>
</ChoiceBoxTableCellFactory>
</cellFactory>
<cellValueFactory><PropertyValueFactory property="referenceContract"/></cellValueFactory>
</TableColumn>
<TableColumn prefWidth="75.0" text="Destination">
<cellValueFactory><PropertyValueFactory property="destinationContract"/></cellValueFactory>
</TableColumn>
<TableColumn prefWidth="75.0" text="Margin" editable="true">
<cellFactory><SpinnerTableCellFactory maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" min="0" max="1" initialValue="0" amountToStepBy="0.025" decimalFormat="0.000"/></cellFactory>
<cellValueFactory><PropertyValueFactory property="margin"/></cellValueFactory>
</TableColumn>
<TableColumn prefWidth="75.0" text="Bot">
<cellValueFactory><PropertyValueFactory property="bot"/></cellValueFactory>
</TableColumn>
<TableColumn prefWidth="75.0" text="Price">
<cellValueFactory><PropertyValueFactory property="price"/></cellValueFactory>
</TableColumn>
<TableColumn prefWidth="75.0" text="Volume">
<cellValueFactory><PropertyValueFactory property="volume"/></cellValueFactory>
</TableColumn>
</columns>
<items>
<FXCollections fx:factory="observableArrayList">
<GridRowModel state="false" side="BID" sourceContract="s01" referenceContract="r01" destinationContract="d01" margin="0" bot="MinMax" price="15.125" volume="0" />
<GridRowModel state="false" side="ASK" sourceContract="s02" referenceContract="r01" destinationContract="d02" margin="0" bot="MinMax" price="15.125" volume="0" />
</FXCollections>
</items>
</TableView>
The ChoiceBoxTableCellFactory has a constructor which takes both named arguments from the fxml and a setter for the items element.
public class ChoiceBoxTableCellFactory<S, T> implements Callback<TableColumn<S, String>, TableCell<S, String>> {
private ObservableList<String> items;
private double maxHeight;
private double maxWidth;
public ChoiceBoxTableCellFactory() {
}
public ChoiceBoxTableCellFactory(
#NamedArg("maxHeight") double maxHeight,
#NamedArg("maxWidth") double maxWidth) {
this.maxHeight = maxHeight;
this.maxWidth = maxWidth;
}
#Override
public TableCell<S, String> call(TableColumn<S, String> param) {
return new TableCell<S, String>() {
ChoiceBox<String> choiceBox = new ChoiceBox<>(getItems());
{
choiceBox.setMaxHeight(maxHeight);
choiceBox.setMaxWidth(maxWidth);
choiceBox.valueProperty().addListener((obs, oldValue, newValue) -> {
ObservableValue<String> value = getTableColumn().getCellObservableValue(getIndex());
if (value instanceof WritableValue) {
((WritableValue<String>) value).setValue(newValue);
}
});
}
#Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setText(null);
setGraphic(null);
} else {
if (isEditing()) {
setText(null);
setGraphic(null);
} else {
choiceBox.setValue(item);
setText(null);
setGraphic(choiceBox);
}
}
}
};
}
public ObservableList<String> getItems() {
return items;
}
public void setItems(ObservableList<String> items) {
this.items = items;
}
}
But that throws this exception
Caused by: java.lang.IllegalArgumentException: Unable to coerce [[r01, r02]] to interface javafx.collections.ObservableList.
at com.sun.javafx.fxml.BeanAdapter.coerce(BeanAdapter.java:496)
at com.sun.javafx.fxml.builder.ProxyBuilder$Setter.invoke(ProxyBuilder.java:533)
at com.sun.javafx.fxml.builder.ProxyBuilder.createObjectFromDefaultConstructor(ProxyBuilder.java:338)
... 144 more
When I remove the maxHeight and maxWidth attributes the items field is set correctly through the setter. With these attributes the items value is wrapped in an additional array. How can I achieve the desired result?
That looks like it should work. I tried a simpler test and the following workarounds seemed to work there:
If you don't need the setItems() method anywhere else in your code, remove it and use the read only list properties approach. I.e. remove the setItems(...) method entirely, and in the FXML do
<ChoiceBoxTableCellFactory maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308">
<items>
<String fx:value="r01" />
<String fx:value="r02" />
</items>
</ChoiceBoxTableCellFactory>
The other way around seems to be to use an <fx:define> block to define the items, and initialize it using an attribute:
<fx:define>
<FXCollections fx:factory="observableArrayList" fx:id="items">
<String fx:value="r01"/>
<String fx:value="r02"/>
</FXCollections>
<fx:define>
<ChoiceBoxTableCellFactory maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308"
items="$items" />
As you will see I am a massive noob when it comes to Java and Javafx. I have spent a lot of time reading around (various forum posts, and tuts) and trying to figure out myself where I am getting this issue but it has come to the point for me to post for feedback from someone who knows their business.
When replying, please could you take the time to also explain why something isn't working and some general pointers? Here is what I have so far (my FXML and my two classes) any pointers would be fantastic!!
My FXML;
<Pane id="myScene" fx:id="myScene" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity"
prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1"
fx:controller="sample.TestController">
<children>
<TableView id="employeesTable" fx:id="employeesTable" layoutX="131.0" layoutY="64.0" prefHeight="200.0" prefWidth="360.0">
<columns>
<TableColumn id="colFirstName" fx:id="colFirstName" prefWidth="75.0" text="First Name" />
<TableColumn id="colLastName" fx:id="colLastName" prefWidth="75.0" text="Last Name" />
<TableColumn id="colEmail" fx:id="colEmail" prefWidth="75.0" text="email" />
</columns>
</TableView>
</children>
</Pane>
Now the Employee class I have;
public class Employee {
private StringProperty firstName;
private StringProperty lastName;
private StringProperty email;
public Employee(String a, String b, String c) {
this.firstName = new SimpleStringProperty(a);
this.lastName = new SimpleStringProperty(b);
this.email = new SimpleStringProperty(c);
}
public Employee() {
}
public String getFirstName() {
return firstName.get();
}
public StringProperty firstNameProperty() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName.set(firstName);
}
public String getLastName() {
return lastName.get();
}
public StringProperty lastNameProperty() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName.set(lastName);
}
public String getEmail() {
return email.get();
}
public StringProperty emailProperty() {
return email;
}
public void setEmail(String email) {
this.email.set(email);
}
}
And finally the class for my test controller is
public class TestController {
public Label login;
public TextField loginUserName;
public PasswordField loginPassword;
public TextField testOutput;
#FXML TableView<Employee> employeesTable;
#FXML TableColumn<Employee, String> colFirstName;
#FXML TableColumn<Employee, String> colLastName;
#FXML TableColumn<Employee, String> colEmail;
#FXML Pane myScene;
//public javafx.scene.control.TableView employeesTable;
private ObservableList<Employee> myData;
private MainController MainController;
public void loadEmployeeForm(ActionEvent actionEvent) throws IOException, SQLException, ClassNotFoundException {
myData = FXCollections.observableArrayList(DBCON.getEmployees());
System.out.println(myData.size());
Parent root = FXMLLoader.load(getClass().getResource("frmEmployees.fxml"));
Scene myScene = new Scene( root );
sample.MainController.setScene(myScene);
colFirstName.setCellValueFactory(new PropertyValueFactory<Employee, String>("firstName"));
colLastName.setCellValueFactory(new PropertyValueFactory<Employee, String>("lastName"));
colEmail.setCellValueFactory(new PropertyValueFactory<Employee, String>("email"));
employeesTable.setItems(null);
employeesTable.setItems(myData);
employeesTable.setVisible(true);
}
I get a null pointer exception when I go to set colFirstName to the property value factory which make me think I haven't initialized something somewhere but I am utterly clueless on how to go about adding that.
If I add in lines such as;
TableColumn colFirstName = new TableColumn("firstName");
for each of my columns and the tablename it works (ie it doesn't throw a load of error messages at me) that way but then I don't get any data loading into the tableview because I think that's me creating a new tableView not using the one generated from the FXML?
I have a feeling it will be very simple, but as I said I am a massive noob and any points would be much obliged.
Thanks
Mark
Update 1;
The method for load employee form is called from a button on myMain.fxml;
<GridPane alignment="CENTER" hgap="10" prefHeight="300" prefWidth="300" vgap="10" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/8"
fx:controller="sample.TestController" stylesheets="/sample/myFirst.css">
<children>
<Button onAction="#login" text="Login" GridPane.halignment="CENTER" GridPane.rowIndex="5" GridPane.valignment="CENTER" />
<Button text="GoEmployees" onAction="#loadEmployeeForm" GridPane.halignment="CENTER" GridPane.rowIndex="3" GridPane.valignment="CENTER" />
<Label fx:id="login" GridPane.rowIndex="1" />
<Label text="UserName" GridPane.columnIndex="0" GridPane.rowIndex="1" />
<Label text="Password" GridPane.columnIndex="0" GridPane.rowIndex="2" />
<TextField fx:id="loginUserName" GridPane.rowIndex="1" GridPane.columnIndex="1" />
<PasswordField fx:id="loginPassword" GridPane.rowIndex="2" GridPane.columnIndex="1" blendMode="OVERLAY" />
<TextField fx:id="testOutput" GridPane.rowIndex="4" GridPane.columnIndex="0" GridPane.columnSpan="3" />
</children>
<columnConstraints>
<ColumnConstraints prefWidth="125.0" />
</columnConstraints>
<rowConstraints>
<RowConstraints prefHeight="50.0" />
</rowConstraints>
<padding>
<Insets bottom="10.0" left="9.0" right="10.0" top="10.0" />
</padding>
</GridPane>
Is having my testController control two different FXMLs a problem/a no go?
When the FXMLLoader tries to load an fxml file, it will create new instance of the controller class defined with fx:controller in fxml file. Then it creates and maps the #FXML annotated fields with fx:id components in fxml file. Finally, it calls the controller's initialize() method. You can get the instantiated controller with fxmlloader.getController() after fxmlloader.load().
According to this basic work flow, the pitfall in your code is:
myMain.fxml's controller is TestController, but myMain.fxml does not contain TableColumns with fx:id colFirstName etc. So these fields are null, when the myMain.fxml has been loaded. As a result, there will be NPE in loadEmployeeForm() while trying to use these fields.
Move the TableView and TableColumns to frmEmployees.fxml's controller, and configure them (setCellValueFactory, initial data etc.) in this controller's initialize() method.
You never use your Employee(String, String, String) constructor. This constructor initializes the firstName, lastName, and email. Otherwise, your references will point to nothing.