Reputation: 83
I have a layout which basically consists of ChoiceBox
and TableView
. What I'd like to achieve is to display different data in TableView
basing on the selected option in ChoiceBox
.
What I have so far is:
MainController class:
private void configureChoiceBox() {
choiceBox.getSelectionModel().selectedIndexProperty().addListener((v, oldValue, newValue) -> {
if(newValue.intValue() == 0) {
workshopList.setItems(data.getPipeCableList());
}
else if(newValue.intValue() == 1) {
workshopList.setItems(data.getElementList());
}
});
}
Data class:
private ObservableList<PipeCable> pipeCableList;
private ObservableList<Element> elementList;
/**/
private ObservableList<StoredItem> displayedList;
public Data() {
this.pipeCableList = FXCollections.observableArrayList();
this.elementList = FXCollections.observableArrayList();
/**/
this.displayedList = FXCollections.observableArrayList();
}
public ObservableList<StoredItem> getPipeCableList() {
displayedList.removeAll(elementList);
displayedList.addAll(pipeCableList);
return displayedList;
}
public ObservableList<StoredItem> getElementList() {
displayedList.removeAll(pipeCableList);
displayedList.addAll(elementList);
return displayedList;
}
The problem is: when I change between options in ChoiceBox
the data from both elementList
and pipeCableList
are mixed together and changing option in ChoiceBox
has no result whatsoever.
What I'd like to achieve: be able to display different data which is contained in elementList
and pipeCableList
according to option selected with ChoiceBox
. What's more, when one option is selected (one list is displayed) all new items which are added to this list will be visible on TableView
.
EDIT(added missing info): PipeCable
and Element
extends StoredItem
and TableView
takes items of type StoredItem
.
Upvotes: 0
Views: 1930
Reputation: 159566
What's going wrong
Get rid of displayList, the TableView already has a reference to the list of items it displays, so just set that to the appropriate list. Currently your display list values are getting out of synch with your underlying data values.
Assumptions
I assume your TableView takes items of type StoredItem and both PipeCable and Element are also of type StoredItem (though inheritance or interface implementation).
How to fix it
Usually, you could just do:
tableView.setItems(data.getPipeCableList())
and the same for the element list as appropriate when it is chosen. But due to some limitations of Java generics that I can't seem to easily get around, that does not compile. If both the element list and pipe cable list were the same types (rather than children of a common parent type), it would be no issue.
To get around the generics issue, you can do:
tableView.getItems().setAll(data.getPipeCableList())
Which works fine, but does not keep the table view items in synch with the data items if the data items change.
To keep these in synch, you can do:
Bindings.bindContent(tableView.getItems(), data.getPipeCableList());
which is a bit ugly, but appears to work.
Full Sample App
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.beans.property.*;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.*;
import javafx.stage.Stage;
import java.io.IOException;
import java.util.stream.IntStream;
public class MultiListTable extends Application {
enum ItemType {
PipeCable, Element
}
@Override public void start(Stage stage) throws IOException {
TableView<StoredItem> tableView = new TableView<>();
TableColumn<StoredItem, String> nameColumn = new TableColumn<>("Name");
nameColumn.setCellValueFactory(new PropertyValueFactory<>("name"));
nameColumn.setPrefWidth(120);
tableView.getColumns().add(nameColumn);
Data data = new Data();
ChoiceBox<ItemType> choiceBox = new ChoiceBox<>(
FXCollections.observableArrayList(ItemType.values())
);
choiceBox.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> {
switch (newValue) {
case PipeCable:
Bindings.bindContent(tableView.getItems(), data.getPipeCableList());
break;
case Element:
Bindings.bindContent(tableView.getItems(), data.getElementList());
break;
}
});
choiceBox.getSelectionModel().select(0);
Button addPipe = new Button("Add Pipe");
addPipe.setOnAction(event -> data.getPipeCableList().add(
new PipeCable("Pipe " + (data.getPipeCableList().size() + 1))
));
IntStream.range(0, 3).forEach(i -> addPipe.fire());
Button addElement = new Button("Add Element");
addElement.setOnAction(event -> data.getElementList().add(
new Element("Element " + (data.getElementList().size() + 1))
));
IntStream.range(0, 2).forEach(i -> addElement.fire());
HBox controls = new HBox(10, choiceBox, addPipe, addElement);
VBox layout = new VBox(10, controls, tableView);
layout.setPadding(new Insets(10));
Scene scene = new Scene(layout);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
class Data {
private ObservableList<PipeCable> pipeCableList = FXCollections.observableArrayList();
private ObservableList<Element> elementList = FXCollections.observableArrayList();
ObservableList<PipeCable> getPipeCableList() {
return pipeCableList;
}
ObservableList<Element> getElementList() {
return elementList;
}
}
static public class StoredItem {
private final ReadOnlyStringWrapper name;
public StoredItem(String name) {
this.name = new ReadOnlyStringWrapper(name);
}
public String getName() {
return name.get();
}
public ReadOnlyStringProperty nameProperty() {
return name.getReadOnlyProperty();
}
}
static public class PipeCable extends StoredItem {
public PipeCable(String name) {
super(name);
}
}
static public class Element extends StoredItem {
public Element(String name) {
super(name);
}
}
}
Upvotes: 1