首页 > 解决方案 > 如何将 TableView 内的 TableColumn 总和绑定到外部标签 textProperty()?

问题描述

有一个订单表视图,其中有一列用于表示订单成本。Order 对象的成本属性可以在 TableView 中更改。这意味着 Order 列单元格可以使用 TextFieldTableCell 进行更改。此外,TableView 之外还有一个标签,它应该代表订单成本的总和。现在,问题是我不知道如何将订单成本列的总和绑定到标签的文本属性()。

这里有一段代码可以澄清问题:

     class Order {
            private SimpleStringProperty name;
            private SimpleIntegerProperty cost;

            public String getName() {
                return this.name.get();
            }

            public void setName(String name) {
                this.name.set(name);
            }

            public SimpleStringProperty nameProperty() {
                return this.name;
            }

            public Integer getCost() {
                return this.cost.get();
            }

            public void setCost(Integer cost) {
                this.cost.set(cost);
            }

            public SimpleIntegerProperty costProperty() {
                return this.cost;
            }
        }

        TableView<Order> tableView = new TableView<>();

        TableColumn<Order, String> nameColumn = new TableColumn<>();
        nameColumn.setCellValueFactory(new PropertyValueFactory<>("name"));
        nameColumn.setCellFactory(TextFieldTableCell.forTableColumn());

        TableColumn<Order, String> costColumn = new TableColumn<>();
        costColumn.setCellValueFactory(new PropertyValueFactory<>("cost"));
        costColumn.setCellFactory(TextFieldTableCell.forTableColumn());

        tableView.getColumns().addAll(nameColumn, costColumn);

        Label totalCostLabel = new Label("Total cost should be updated in this label");

        VBox vBox = new VBox();
        vBox.getChildren().addAll(tableView, totalCostLabel);

标签: javajavafxbinding

解决方案


您可以执行以下操作:

  1. 创建一个ObservableList到 的extractor映射costProperty(),并将其用作表的items列表:

    tableView.setItems(FXCollections.observableArrayList(
        order -> new Observable[] { order.costProperty() }));
    

    这确保列表在列表的任何costProperty元素发生更改时触发更新事件(除了在列表中添加或删除元素时触发的通常事件等)

  2. 创建一个DoubleBinding绑定到列表的,并计算总成本:

    DoubleBinding totalCost = Bindings.createDoubleBinding(() -> {
        double total = 0 ;
        for (Order order : tableView.getItems()) {
            total = total + order.getCost();
        }
        return total ;
    }, tableView.getItems());
    
  3. 将标签绑定textProperty()到总成本:

    totalCostLabel.textProperty().bind(totalCost.asString());
    

如果您想更好地控制它的显示方式,您可以向该方法format提供a 。asString()

这是一个完整的示例,改编自您的代码:

import javafx.application.Application;
import javafx.beans.Observable;
import javafx.beans.binding.Bindings;
import javafx.beans.binding.DoubleBinding;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.control.cell.TextFieldTableCell;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.converter.IntegerStringConverter;


public class SummingTable extends Application {


    @Override
    public void start(Stage stage) {
        TableView<Order> tableView = new TableView<>();

        tableView.setItems(FXCollections.observableArrayList(
                order -> new Observable[] { order.costProperty() }));

        tableView.getItems().addAll(
                new Order("Order 1", 10),
                new Order("Order 2", 20));

        tableView.setEditable(true);


        TableColumn<Order, String> nameColumn = new TableColumn<>();
        nameColumn.setCellValueFactory(new PropertyValueFactory<>("name"));
        nameColumn.setCellFactory(TextFieldTableCell.forTableColumn());

        TableColumn<Order, Integer> costColumn = new TableColumn<>();
        costColumn.setCellValueFactory(cellData -> cellData.getValue().costProperty().asObject());
        costColumn.setCellFactory(TextFieldTableCell.forTableColumn(new IntegerStringConverter()));

        tableView.getColumns().addAll(nameColumn, costColumn);

        Label totalCostLabel = new Label("Total cost should be updated in this label");

        DoubleBinding totalCost = Bindings.createDoubleBinding(() -> {
            double total = 0 ;
            for (Order order : tableView.getItems()) {
                total = total + order.getCost();
            }
            return total ;
        }, tableView.getItems());

        totalCostLabel.textProperty().bind(totalCost.asString());

        VBox vBox = new VBox();
        vBox.getChildren().addAll(tableView, totalCostLabel);

        Scene scene = new Scene(vBox);
        stage.setScene(scene);
        stage.show();

    }

    public class Order {
        private final StringProperty name = new SimpleStringProperty();
        private final IntegerProperty cost = new SimpleIntegerProperty();

        public Order(String name, int cost) {
            setName(name);
            setCost(cost);
        }

        public String getName() {
            return this.name.get();
        }

        public void setName(String name) {
            this.name.set(name);
        }

        public StringProperty nameProperty() {
            return this.name;
        }

        public Integer getCost() {
            return this.cost.get();
        }

        public void setCost(Integer cost) {
            this.cost.set(cost);
        }

        public IntegerProperty costProperty() {
            return this.cost;
        }
    }

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

}

推荐阅读