在表格视图中刷新一组组合框,javafx

时间:2016-03-01 15:46:15

标签: java javafx combobox javafx-8

所以我有一个包含3列的表视图,其中一列是一组组合框,我创建组合框列的方式就是这样

    Source = new TableColumn<>("Configure Interface as..");
    Source.setCellValueFactory(i -> {
        final StringProperty value = i.getValue().optionProperty();
        // binding to constant value
        return Bindings.createObjectBinding(() -> value);
    });

    Source.setCellFactory(col -> {
        TableCell<TableViewTest, StringProperty> c = new TableCell<>();
        ComboBox<String> comboBox = new ComboBox<>(options);
        c.itemProperty().addListener((observable, oldValue, newValue) -> {
            if (oldValue != null) {
                comboBox.valueProperty().unbindBidirectional(oldValue);
            }
            if (newValue != null) {
                comboBox.valueProperty().bindBidirectional(newValue);
            }
        });
        c.graphicProperty().bind(Bindings.when(c.emptyProperty()).then((Node) null).otherwise(comboBox));
        return c;
    }); 

该列从getter方法optionProperty()获取其值,该值位于我的TableViewTest类中。

所以我遇到的问题是我的另一个combobox(comboBoxA)位于我的gui中的tableview表之上,当我改变comboBoxA的值时,我想要用列更改组合框的值。

我可以通过在侦听comboboxA的选择更改的方法中调用以下代码来实现此目的

Source.setCellValueFactory(i -> {
    final StringProperty value = i.getValue().optionTwoProperty();
    // binding to constant value
    return Bindings.createObjectBinding(() -> value);
});

但值不会改变,除非开始向下滚动到桌子底部附近。有没有办法强制组合框在getter方法optionTwoProperty()中更改为新值,而不必向下滚动?

修改

好的,这一行

      final StringProperty value = i.getValue().optionTwoProperty();

直到我开始向下滚动才开始被调用。

3 个答案:

答案 0 :(得分:1)

所以,在fabian的帮助下,我想我明白你希望表格上方的组合框改变你的模型类中的属性,该属性在表格列的单元格中表示。

执行此操作的一种方法是创建将模型类映射到属性的组合框函数的类型,并使用映射到所需属性的函数填充它。

然后,您可以使用绑定来表示表列的单元格值工厂,该绑定将观察可以表示的所有可能属性以及组合框中的选定值,并返回通过应用函数中的函数计算的值。模型实例的组合框(并检索其包装值)。

对于列的单元格工厂,您可以在单元格的组合框中观察所选值。当它发生变化时,使用表格上方组合框中的选定项目来确定要更新的属性。

这是一个SSCCE:

import java.util.function.Function;

import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Insets;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ComboBox;
import javafx.scene.control.ListCell;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;

public class TableWithSetAllComboBox extends Application {

    @Override
    public void start(Stage primaryStage) {
        TableView<Item> table = new TableView<>();
        TableColumn<Item, String> itemCol = new TableColumn<>("Item");
        itemCol.setCellValueFactory(cellData -> Bindings.createStringBinding(() -> cellData.getValue().getName()));
        table.getColumns().add(itemCol);

        TableColumn<Item, String> choiceCol = new TableColumn<>("Choice");

        ComboBox<Function<Item, StringProperty>> option = new ComboBox<>();

        option.getItems().add(Item::choiceProperty);
        option.getItems().add(Item::choice2Property);

        option.setCellFactory(lv -> createListCell());
        option.setButtonCell(createListCell());

        option.getSelectionModel().select(0);           


        ObservableList<String> choices = FXCollections.observableArrayList("First choice", "Second choice", "Third choice");

        choiceCol.setCellFactory(col -> {
            TableCell<Item, String> cell = new TableCell<>();
            ComboBox<String> combo = new ComboBox<>(choices);
            cell.graphicProperty().bind(Bindings.when(cell.emptyProperty()).then((Node)null).otherwise(combo));
            combo.valueProperty().addListener((obs, oldValue, newValue) -> {
                if (! cell.isEmpty() && newValue != null) {
                    Item item = table.getItems().get(cell.getIndex()) ;
                    StringProperty property = option.getValue().apply(item);
                    property.set(newValue);
                }
            });
            cell.itemProperty().addListener((obs, oldItem, newItem) -> combo.setValue(newItem));
            return cell ;
        });

        choiceCol.setPrefWidth(150);

        table.getColumns().add(choiceCol);

        choiceCol.setCellValueFactory(cellData -> Bindings.createStringBinding( 
                () -> option.getValue().apply(cellData.getValue()).get(), 
                cellData.getValue().choiceProperty(), 
                cellData.getValue().choice2Property(),
                option.valueProperty()));

        choiceCol.setGraphic(option);

        choiceCol.setPrefWidth(200);

        for (int i = 1; i <= 30 ; i++) table.getItems().add(new Item("Item "+i ,choices.get(0)));

        Button debug = new Button("Debug");
        debug.setOnAction(e -> table.getItems().stream().
                map(item -> String.format("%s (%s, %s)", item.getName(), item.getChoice(), item.getChoice2())).
                forEach(System.out::println));

        BorderPane root = new BorderPane(table);
        BorderPane.setMargin(debug, new Insets(5));
        root.setBottom(debug);

        primaryStage.setScene(new Scene(root, 600, 600));
        primaryStage.show();
    }

    private ListCell<Function<Item, StringProperty>> createListCell() {
        return new ListCell<Function<Item, StringProperty>>() {
            @Override
            public void updateItem(Function<Item, StringProperty> item, boolean empty) {
                super.updateItem(item, empty);
                setText(empty ? null : item.apply(new Item("", "")).getName());
            }
        };
    }

    public static class Item {
        private final String name ;
        private final StringProperty choice ;

        private final StringProperty choice2 ;

        public Item(String name, String choice) {
            this.choice = new SimpleStringProperty(this, "Choice", choice);
            this.choice2 = new SimpleStringProperty(this, "Choice 2", "Second choice");
            this.name = name ;
        }

        public final StringProperty choiceProperty() {
            return this.choice;
        }

        public final java.lang.String getChoice() {
            return this.choiceProperty().get();
        }

        public final void setChoice(final java.lang.String choice) {
            this.choiceProperty().set(choice);
        }

        public String getName() {
            return name;
        }
        public final StringProperty choice2Property() {
            return this.choice2;
        }

        public final java.lang.String getChoice2() {
            return this.choice2Property().get();
        }

        public final void setChoice2(final java.lang.String choice2) {
            this.choice2Property().set(choice2);
        }

    }

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

答案 1 :(得分:0)

很难说给出代码片段。也许你在进行更新时不在javaFX线程上?在这种情况下,使用Platform.runLater(...),或共享一些最小的代码来重现问题。

答案 2 :(得分:0)

问题是TableView没有收听对其列的元素的cellValueFactory属性的修改。因此TableView不知道它应该重绘它的单元格。在JavaFX 8u60中,为此目的添加了refresh()方法(由于某种原因,我无法在在线javadoc中找到它),这允许您更改方法的代码,更改cellValueFactory之类的这样:

Source.setCellValueFactory(i -> {
    final StringProperty value = i.getValue().optionTwoProperty();
    // binding to constant value
    return Bindings.createObjectBinding(() -> value);
});
tableview.refresh();

在旧版本中,您必须使用设置列值的解决方法来触发列表中的更改:

List<TableColumn<TableViewTest, ?>> columns = tableview.getColumns();
columns.set(columns.indexOf(Source), Source);

但是这种解决方法可能在将来的版本中停止工作,因为该列表实际上并未使用此操作进行修改,并且ObservableList的合同不需要触发列表更改事件(但替换TableColumn使用新实例(并复制属性)应始终有效。