我有一个TabPane,用户可以在每个选项卡上输入/编辑数据,并且可以在切换到新选项卡之前自由切换选项卡,而无需保存更改。一个选项卡有一个TableView,我想阻止用户在输入无效数据时离开该选项卡。我原来的方法与this question的方法相同,但不能正常工作 - 标签不能可靠地改回来。我喜欢James_D的answer,并试图实现类似的东西。但是,大多数情况下,输入表中的数据是可选的,因此在用户输入数据之前禁用其他选项卡不是一种选择。
我最终做的是扩展TableColumn以添加一个BooleanProperty'invalid',然后我将其绑定到Tab的disableProperty。在该列的提交事件中,我验证新值,如果未通过,则设置invalid = true,这将禁用相应的选项卡。这也行不通。我有自定义表格单元格,提供失去焦点的编辑。如果单击其他选项卡失去焦点,则提交事件为时已晚 - 首先选择选项卡,然后禁用。我一直在捣乱我的大脑以寻找解决方法,但是我没有想法。如果有人有任何建议,我会非常感激!
简短示例(清除任何姓氏并单击Tab 2):
import javafx.application.Application;
import javafx.application.Platform;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.control.Alert;
import javafx.scene.control.Alert.AlertType;
import javafx.scene.control.ButtonType;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableColumn.CellEditEvent;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.stage.Stage;
import javafx.util.Callback;
public class TabPaneTableTest extends Application {
@Override
public void start(Stage primaryStage) {
TableView<Person> table = new TableView<>();
ObservableList<Person> data = FXCollections.observableArrayList();
table.setEditable(true);
MyTableColumn<Person, String> firstNameCol = new MyTableColumn<>("First Name");
firstNameCol.setCellValueFactory(new PropertyValueFactory<Person, String>("firstName"));
MyTableColumn<Person, String> lastNameCol = new MyTableColumn<>("Last Name");
lastNameCol.setCellValueFactory(new PropertyValueFactory<Person, String>("lastName"));
Callback<TableColumn<Person, String>, TableCell<Person, String>> cellFactory = (TableColumn<Person, String> p) -> new MyEditingCell<Person>();
firstNameCol.setCellFactory(cellFactory);
lastNameCol.setCellFactory(cellFactory);
firstNameCol.setOnEditCommit((CellEditEvent<Person, String> event) -> {
event.getRowValue().setFirstName(event.getNewValue());
});
lastNameCol.setOnEditCommit((CellEditEvent<Person, String> event) -> {
if(event.getNewValue().trim().isEmpty()) {
new Alert(AlertType.ERROR, "Last name must be filled out!", ButtonType.OK).showAndWait();
lastNameCol.setInvalid(true);
}
else {
event.getRowValue().setLastName(event.getNewValue());
lastNameCol.setInvalid(false);
}
});
table.getColumns().addAll(firstNameCol, lastNameCol);
table.setItems(data);
data.add(new Person("Luke", "Skywalker"));
data.add(new Person("Han", "Solo"));
data.add(new Person("R2", "D2"));
TabPane tabPane = new TabPane();
Tab tab1 = new Tab("Tab 1");
tab1.setClosable(false);
tab1.setContent(table);
Tab tab2 = new Tab("Tab 2");
tab2.setClosable(false);
tab2.disableProperty().bind(lastNameCol.invalidProperty());
tabPane.getTabs().addAll(tab1, tab2);
Scene scene = new Scene(tabPane, 400, 200);
primaryStage.setTitle("Tab Pane Table Validation Test");
primaryStage.setScene(scene);
primaryStage.show();
}
public class MyEditingCell<S> extends TableCell<S, String> {
private TextField editingField;
private void createEditingField() {
editingField = new TextField(getString());
editingField.focusedProperty().addListener((ov, oldValue, newValue) -> {
if(!newValue) {
commitEdit(editingField.getText());
}
});
}
@Override
public void startEdit() {
super.startEdit();
createEditingField();
setText(null);
setGraphic(editingField);
Platform.runLater(() -> {
editingField.requestFocus();
editingField.selectAll();
});
}
@Override
public void cancelEdit() {
super.cancelEdit();
setText((String)getItem());
setGraphic(null);
}
@Override
public void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if(empty) {
setText(null);
setGraphic(null);
}
else {
if(isEditing()) {
if(editingField != null) {
editingField.setText(getString());
}
setText(null);
setGraphic(editingField);
}
else {
setText(getString());
setGraphic(null);
}
}
}
private String getString() {
return getItem() == null ? "" : getItem().toString();
}
}
public class MyTableColumn<S, T> extends TableColumn<S, T> {
private BooleanProperty invalid = new SimpleBooleanProperty(false);
public MyTableColumn(String header) {
super(header);
setEditable(true);
}
public BooleanProperty invalidProperty() {
return invalid;
}
public boolean getInvalid() {
return invalid.get();
}
public void setInvalid(boolean value) {
invalid.set(value);
}
}
public class Person {
private StringProperty firstName;
private StringProperty lastName;
public Person(String first, String last) {
firstName = new SimpleStringProperty(this, "firstName", first);
lastName = new SimpleStringProperty(this, "lastName", last);
}
public void setFirstName(String value) {
firstNameProperty().set(value);
}
public String getFirstName() {
return firstNameProperty().get();
}
public StringProperty firstNameProperty() {
if(firstName == null)
firstName = new SimpleStringProperty(this, "firstName", "First");
return firstName;
}
public void setLastName(String value) {
lastNameProperty().set(value);
}
public String getLastName() {
return lastNameProperty().get();
}
public StringProperty lastNameProperty() {
if(lastName == null)
lastName = new SimpleStringProperty(this, "lastName", "Last");
return lastName;
}
}
public static void main(String[] args) {
launch(args);
}
}
答案 0 :(得分:0)
如果使用提取器创建可观察列表,例如:
ObservableList<Person> data = FXCollections.observableArrayList(person ->
new Observable[] { person.lastNameProperty() });
然后,只要任何元素中的任何指定属性发生更改,列表就会触发更新通知(在这种情况下,只要lastName
更改列表中的任何内容)。
现在您可以为invalid
:
BooleanBinding invalid = Bindings.createBooleanBinding(
() -> data.stream().anyMatch(person -> person.getLastName().isEmpty()),
data);
然后你可以观察到这种绑定:
invalid.addListener((obs, wasInvalid, isNowInvalid) -> {
if (isNowInvalid) {
// show alert, etc...
}
});
或通过绑定节点来禁用节点:
someNode.disableProperty().bind(invalid);
您可以类似地将invalid
子类中的TableColumn
属性(如果您仍然需要)绑定到此绑定。