【问题标题】:Refreshing a column of comboboxes in a table view, javafx刷新表视图中的一列组合框,javafx
【发布时间】:2016-06-14 03:04:04
【问题描述】:

所以我有一个包含 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;
    }); 

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

所以我遇到的问题是我的 gui 中的 tableview 表上方还有另一个 combobox (comboBoxA),每当我更改 comboBoxA 的值时,我都想更改组合框的值与列。

我可以通过在监听组合框A的选择变化的方法中调用以下代码来做到这一点

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();

在我开始向下滚动之前实际上不会被调用。

【问题讨论】:

  • 发布更改值的代码。由于您在这里使用了我的代码的修改版本stackoverflow.com/a/35134148/2991525,因此我使用comboBoxA.valueProperty().addListener((a, b, c) -&gt; { for (TableViewTest item : tableview.getItems()) { item.setOption(c); } }); 对此进行了测试,但无法重现错误。
  • 问题是我使用的是 fxml 文件,而我的代码在 fxml 控制器文件中,所以我不必为 comboBoxA 设置监听器,只需命名我想调用的方法选择更改时。我用来更改值的代码是Source.setcellValueFactory 部分
  • 您能出示您的声明以便我们了解事物的类型吗? (而且您的模型类也可能会有所帮助。)为什么您的单元格值是包装在绑定中的属性?你不能把它改成TableColumn&lt;Whatever, String&gt; 而不是TableColumn&lt;Whatever, StringProperty&gt;,它看起来就是这样吗?
  • 也许后退一步,解释一下你在这里真正想要做什么。是否在表格顶部有一个组合框可以更改所有值(类似于“设置所有”功能)?看起来你只是为了让某些东西发挥作用而把它弄得太复杂了。
  • 所以我的工具将获取有关思科交换机上每个受监控会话的信息并将其显示给用户,但也允许用户使用该工具设置跨度会话。因此表格视图上方的组合框 (COMBOA) 将包含交换机上所有会话的列表,即会话一、会话二等。因此,如果我从 COMBOA 中选择会话一,那么 tableview 中的所有组合框都必须根据会话一的信息进行更改,即。端口一是输入,端口二是输出。

标签: java javafx combobox javafx-8


【解决方案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);
    }
}

【讨论】:

  • 我不是 100% 确定,但如果我确实理解了这个问题,它基本上是关于更改列中显示的属性 (i.getValue().optionTwoProperty() i.getValue().optionProperty()),这可能应该是通过替换整个列来完成...TableView 之外的ComboBox 选择使用的属性...(我猜)如果我是对的,即使单元格只是显示文本内容而不是,这也行不通这个ComboBox 设置...
  • 好的,这是有道理的。它应该真的可以通过替换cellValueFactory来实现,但是更新cellValueFactory似乎不会(尽管我需要测试)导致updateItem在单元格上被调用(这似乎是一个错误)。从理论上讲,您应该能够将组合框设置为 ComboBox&lt;Callback&lt;CellDataFeatures&lt;...&gt;, TableCell&lt;...&gt;&gt;&gt; 并将 cellValueFactoryProperty 绑定到它的值,但是立即对此进行测试对我不起作用。您可能可以创建一个自定义绑定...
  • 自定义绑定会有问题,因为这里似乎需要写访问权限。
  • 您只需为选择属性的组合框选择适当的类型:查看更新的答案。
【解决方案2】:

问题是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实例(和复制属性)应该始终有效)。

【讨论】:

  • 但如果设置正确,所有这些变通方法都应该是不必要的......
  • tableview.refresh();帮助很大。谢谢@fabian :)
【解决方案3】:

鉴于代码 sn-ps 很难说。进行更新时,您可能不在 javaFX 线程上?在这种情况下,请使用 Platform.runLater(...),或者共享一些最少的代码来重现问题。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-06-05
    • 1970-01-01
    • 2019-01-24
    • 1970-01-01
    • 2016-05-09
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多