2016-09-21 47 views
2

我试图启用JavaFX Button,具体取决于TableView中选定行中属性值的聚合。以下是一个示例应用程序,演示了此问题:基于TableView中选定项目的JavaFX布尔绑定

package test; 

import java.util.Random; 

import javafx.application.Application; 
import javafx.beans.binding.Bindings; 
import javafx.beans.property.BooleanProperty; 
import javafx.beans.property.SimpleBooleanProperty; 
import javafx.collections.ObservableList; 
import javafx.scene.Scene; 
import javafx.scene.control.Button; 
import javafx.scene.control.MultipleSelectionModel; 
import javafx.scene.control.SelectionMode; 
import javafx.scene.control.TableColumn; 
import javafx.scene.control.TableView; 
import javafx.scene.layout.VBox; 
import javafx.stage.Stage; 

public class Main extends Application { 

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

    private static class Row { 
     private final BooleanProperty myProp; 
     public Row(final boolean value) { 
      myProp = new SimpleBooleanProperty(value); 
     } 
     public BooleanProperty propProperty() { return myProp; } 
    } 

    @Override 
    public void start(final Stage window) throws Exception { 
     // Create a VBox to hold the table and button 
     final VBox root = new VBox(); 
     root.setMinSize(200, 200); 

     // Create the table, and enable multi-select 
     final TableView<Row> table = new TableView<>(); 
     final MultipleSelectionModel<Row> selectionModel = table.getSelectionModel(); 
     selectionModel.setSelectionMode(SelectionMode.MULTIPLE); 
     root.getChildren().add(table); 

     // Create a column based on the value of Row.propProperty() 
     final TableColumn<Row, Boolean> column = new TableColumn<>("Value"); 
     column.setCellValueFactory(p -> p.getValue().propProperty()); 
     table.getColumns().add(column); 

     // Add a button below the table 
     final Button button = new Button("Button"); 
     root.getChildren().add(button); 

     // Populate the table with true/false values 
     final ObservableList<Row> rows = table.getItems(); 
     rows.addAll(new Row(false), new Row(false), new Row(false)); 

     // Start a thread to randomly modify the row values 
     final Random rng = new Random(); 
     final Thread thread = new Thread(() -> { 
      // Flip the value in a randomly selected row every 10 seconds 
      try { 
       do { 
        final int i = rng.nextInt(rows.size()); 
        System.out.println("Flipping row " + i); 
        Thread.sleep(10000); 
        final BooleanProperty prop = rows.get(i).propProperty(); 
        prop.set(!prop.get()); 
       } while (true); 
      } catch (final InterruptedException e) { 
       System.out.println("Exiting Thread"); 
      } 
     }, "Row Flipper Thread"); 
     thread.setDaemon(true); 
     thread.start(); 

     // Bind the button's disable property such that the button 
     //  is only enabled if one of the selected rows is true 
     final ObservableList<Row> selectedRows = selectionModel.getSelectedItems(); 
     button.disableProperty().bind(Bindings.createBooleanBinding(() -> { 
      for (int i = 0; i < selectedRows.size(); ++i) { 
       if (selectedRows.get(i).propProperty().get()) { 
        return false; 
       } 
      } 
      return true; 
     }, selectedRows)); 

     // Show the JavaFX window 
     final Scene scene = new Scene(root); 
     window.setScene(scene); 
     window.show(); 
    } 
} 

测试,启动上述应用程序,并选择由文本“翻转行N”,其中N是在所指示的列[0,2]。当所选行的值更改为真时...

观察到的行为button仍然被禁用。
希望的行为button变为启用。

有谁知道如何创建一个BooleanBinding展现出所需的行为?

+2

仅供参考,您需要执行JavaFX代码在JavaFX线程上。 'final BooleanProperty prop = rows.get(i).propProperty(); prop.set(!prop.get());'应该用'Platform.runLater()'包装。 –

回答

5

如果所选行的任何一个propProperty发生更改,则您的绑定需要失效。目前绑定仅观察选定的项目列表,当列表内容改变时(即,项目变为被选择或未被选择),该绑定将触发事件,但当属于该列表中的项目的属性改变值时,绑定不会被触发。

要做到这一点,创建一个列表与提取:当项目被添加或删除,或

final ObservableList<Row> selectedRows = 
    FXCollections.observableArrayList(r -> new Observable[]{r.propProperty()}); 

这份名单将触发事件时,任何项目的列表中的变化propProperty()。 (如果需要观察多个值,可以将它们包含在Observable的数组中)。

当然,您仍然需要此列表来包含表中选定的项目。您可以通过绑定列表中选择模型的selectedItems内容确保这一点:

Bindings.bindContent(selectedRows, selectionModel.getSelectedItems()); 

下面是一个使用这个你MCVE的版本:

import java.util.Random; 

import javafx.application.Application; 
import javafx.application.Platform; 
import javafx.beans.Observable; 
import javafx.beans.binding.Bindings; 
import javafx.beans.property.BooleanProperty; 
import javafx.beans.property.SimpleBooleanProperty; 
import javafx.collections.FXCollections; 
import javafx.collections.ObservableList; 
import javafx.scene.Scene; 
import javafx.scene.control.Button; 
import javafx.scene.control.MultipleSelectionModel; 
import javafx.scene.control.SelectionMode; 
import javafx.scene.control.TableColumn; 
import javafx.scene.control.TableView; 
import javafx.scene.layout.VBox; 
import javafx.stage.Stage; 

public class Main extends Application { 

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

    private static class Row { 
     private final BooleanProperty myProp; 
     public Row(final boolean value) { 
      myProp = new SimpleBooleanProperty(value); 
     } 
     public BooleanProperty propProperty() { return myProp; } 
    } 

    @Override 
    public void start(final Stage window) throws Exception { 
     // Create a VBox to hold the table and button 
     final VBox root = new VBox(); 
     root.setMinSize(200, 200); 

     // Create the table, and enable multi-select 
     final TableView<Row> table = new TableView<>(); 
     final MultipleSelectionModel<Row> selectionModel = table.getSelectionModel(); 
     selectionModel.setSelectionMode(SelectionMode.MULTIPLE); 
     root.getChildren().add(table); 

     // Create a column based on the value of Row.propProperty() 
     final TableColumn<Row, Boolean> column = new TableColumn<>("Value"); 
     column.setCellValueFactory(p -> p.getValue().propProperty()); 
     table.getColumns().add(column); 

     // Add a button below the table 
     final Button button = new Button("Button"); 
     root.getChildren().add(button); 

     // Populate the table with true/false values 
     final ObservableList<Row> rows = table.getItems(); 
     rows.addAll(new Row(false), new Row(false), new Row(false)); 

     // Start a thread to randomly modify the row values 
     final Random rng = new Random(); 
     final Thread thread = new Thread(() -> { 
      // Flip the value in a randomly selected row every 10 seconds 
      try { 
       do { 
        final int i = rng.nextInt(rows.size()); 
        System.out.println("Flipping row " + i); 
        Thread.sleep(10000); 
        final BooleanProperty prop = rows.get(i).propProperty(); 
        Platform.runLater(() -> prop.set(!prop.get())); 
       } while (true); 
      } catch (final InterruptedException e) { 
       System.out.println("Exiting Thread"); 
      } 
     }, "Row Flipper Thread"); 
     thread.setDaemon(true); 
     thread.start(); 


     // Bind the button's disable property such that the button 
     //  is only enabled if one of the selected rows is true 
     final ObservableList<Row> selectedRows = 
       FXCollections.observableArrayList(r -> new Observable[]{r.propProperty()}); 
     Bindings.bindContent(selectedRows, selectionModel.getSelectedItems()); 
     button.disableProperty().bind(Bindings.createBooleanBinding(() -> { 
      for (int i = 0; i < selectedRows.size(); ++i) { 
       if (selectedRows.get(i).propProperty().get()) { 
        return false; 
       } 
      } 
      return true; 
     }, selectedRows)); 

     // Show the JavaFX window 
     final Scene scene = new Scene(root); 
     window.setScene(scene); 
     window.show(); 
    } 
} 
+0

请注意,更改是在设置属性值的线程中添加了'Platform.runLater(...)','FXCollections.observableArrayList(...)'用提取器创建了一个新的'ObservableList', Bindings.bindContent(...)'将新列表的内容绑定到'TableView'中的选定行。 –

+1

我遇到了这种方法的问题,我想在其他人遇到同样问题时记录这个问题。当我在表中选择一行或多行时,在主UI线程上调用'table.getItems()。clear()',我得到一个'IndexOutOfBoundsException'。为了解决这个问题,我改变了调用clear清单来确定列表 items = table.getItems(); while(!items.isEmpty())items.remove(items.size() - 1);',然后改变for循环为'for(final Row selectedRow:selectionModel.getSelectedItems())',而不是迭代在'selectedRows'列表中。 –