6

I am unable to bind in fxml a collection to a customized template. Here the code how I would do it in xaml:

<ListView ItemsSource="{Binding PersonCollection}">
    <StackPanel Orientation="Horizontal">
        <Label Content="{Binding FirstName}"></Label>
        <ListView ItemsSource="{Binding MiddleNames}">
            <Label Content="{Binding}"></Label>
        </ListView>
        <Label Content="{Binding LastName}"></Label>
    </StackPanel>
</ListView>

Here the Model:

class Person
{
    String FirstName, LastName;
    String[] MiddleNames;
}

And the Layout would look similar to this:

John Ivy
Robert Downey Junior
Max more middlenames in fact even thousands are possible lastname

Is the binding of an oberservable collection to a customized template possible? I tried the cellfactory but couldnt get my head wrapped around it, as everybody used only strings.

kadir
  • 1,417
  • 11
  • 35
  • What is a customized template? – jewelsea Mar 13 '15 at 21:47
  • I extended my question to further explain the setup. What it essentially does is, that for each item in the collection the template is applied. So for each item in the PersonCollection it generates a horizontal stackpanel, firstname label, lastname label and another listview with a template. – kadir Mar 13 '15 at 22:06
  • Where and how do you specify the input data (e.g. John Ivy, Robert Downey Junior etc)? Is it static and never changes or dymanic data which changes at runtime? Are you sure you really want a [ListView](http://docs.oracle.com/javase/8/javafx/user-interface-tutorial/list-view.htm#CEGGEDBF) here (cause that would be weird) or is that actually something completely different from a JavaFX ListView? (I have no familiarity at all with xaml). – jewelsea Mar 13 '15 at 22:16
  • The data is in a kind of controller, called datacontext in xaml. And the data can be changed. So if I would add another Person to the PersonCollection , the ListView would add this persons ui (or remove if removed from the collection). I dont really want a ListView, the real ui will be a mix of keyvalue pairs, and an accordian, but it seemed simple enough. I have made a fast (and horrible :-D) mockup: http://s8.postimg.org/l7jr1d5tx/image.png – kadir Mar 13 '15 at 22:40

1 Answers1

6

I am not 100% sure this is the answer to your question as I am totally unfamiliar with xaml, but hopefully it is...

Sample Implementation

The sample works by setting the model object (the Person) into the namespace of the FXML loader, which allows you to use a binding expression in FXML to bind to properties of the object.

In the sample, there are a few names initially in the model and you can modify the bound list in the model using the add and remove buttons to add or remove a few more canned names in the list.

sample image

All code goes in a package named sample.names.

name-display.fxml

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

<?import javafx.scene.control.Label?>
<?import javafx.scene.control.ListView?>
<?import javafx.scene.layout.HBox?>

<?import javafx.geometry.Insets?>

<?import javafx.scene.layout.VBox?>
<?import javafx.scene.control.Button?>
<VBox xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="sample.names.NameDisplayController">
  <HBox prefHeight="150.0" prefWidth="220.0" spacing="5.0"  >
    <children>
      <Label fx:id="firstNameLabel" text="${person.firstName}" />
      <ListView fx:id="middleNameList" prefWidth="100.0" items = "${person.middleNames}" />
      <Label fx:id="lastNameLabel" text="${person.lastName}" />
    </children>
    <padding>
      <Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
    </padding>
  </HBox>
  <HBox alignment="CENTER" spacing="30.0">
    <children>
      <Button mnemonicParsing="false" onAction="#addName" text="Add" />
      <Button mnemonicParsing="false" onAction="#removeName" text="Remove" />
    </children>
    <padding>
      <Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
    </padding>
  </HBox>
</VBox>

NameDisplayApp.java

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;

import java.io.IOException;

public class NameDisplayApp extends Application {

    @Override
    public void start(Stage stage) throws IOException {
        Person person = new Person(
                "Bruce",
                new String[] { "Simon", "Larry" },
                "Banner"
        );

        FXMLLoader loader = new FXMLLoader(
                getClass().getResource(
                        "name-display.fxml"
                )
        );
        loader.getNamespace().put(
                "person",
                person
        );
        Pane pane = loader.load();

        NameDisplayController controller = loader.getController();
        controller.setPerson(person);

        stage.setScene(new Scene(pane));
        stage.show();
    }

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

NameDisplayController.java

import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;

public class NameDisplayController {
    private Person person;

    private ObservableList<String> sampleNames = FXCollections.observableArrayList(
            "George", "Henry", "Wallace"
    );

    public void setPerson(Person person) {
        this.person = person;
    }

    public void addName(ActionEvent actionEvent) {
        if (!sampleNames.isEmpty()) {
            person.getMiddleNames().add(
                sampleNames.remove(0)
            );
        }
    }

    public void removeName(ActionEvent actionEvent) {
        if (!person.getMiddleNames().isEmpty()) {
            sampleNames.add(
                    person.getMiddleNames().remove(0)
            );
        }
    }
}

Person.java

import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;

public class Person {
    public StringProperty firstName;
    public StringProperty lastName;

    private ObservableList<String> middleNames;

    public Person(String firstName, String[] middleNames, String lastName) {
        this.firstName   = new SimpleStringProperty(firstName);
        this.middleNames = FXCollections.observableArrayList(middleNames);
        this.lastName    = new SimpleStringProperty(lastName);
    }

    public String getFirstName() {
        return firstName.get();
    }

    public StringProperty firstNameProperty() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName.set(firstName);
    }

    public ObservableList<String> getMiddleNames() {
        return middleNames;
    }

    public String getLastName() {
        return lastName.get();
    }

    public StringProperty lastNameProperty() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName.set(lastName);
    }
}

Alternate Implementations

There may be other (perhaps more preferred ways) of doing this - e.g. by associating bindings of items in code rather than FXML (which is what I usually do) or injecting the model using a dependency injection system. See afterburner.fx for an example of the injection approach - though I don't know if afterburner also places model objects into the FXML namespace or just injects into the controller (if it doesn't do the injection into the FXML namespace that might be a cool addition you could request for it).

jewelsea
  • 150,031
  • 14
  • 366
  • 406
  • This is similar to the progress I came, yet I was unable to template something else than Strings. What if I want a list of Buttons or Panes? Is that possible? I am even content with the answer no. – kadir Mar 14 '15 at 11:50
  • 1
    @kadir Then you have to specify a cellFactory. – Puce Mar 14 '15 at 13:30
  • I am trying to use the ${property} notation to pass variables into my factory methods and it isnt working. is there a limitation to this? – Travis Tubbs Sep 17 '18 at 22:54
  • @TravisTubbs there isn't a limitation of which I am aware. But I don't know exactly what you are doing. I suggest you create a new question with a [mcve] and somebody might take a look at that and help you out. – jewelsea Sep 17 '18 at 23:34