TableRow<Object> 上的上下文菜单在第一次右键单击时不显示
Context menu on TableRow<Object> does not show up on first right click
因此,我按照 here 中关于将上下文菜单与 TableViews 结合使用的示例进行了操作。我注意到使用这段代码
row.contextMenuProperty().bind(Bindings.when(Bindings.isNotNull(row.itemProperty()))
.then(rowMenu)
.otherwise((ContextMenu)null));
第一次右键单击包含值的行时不显示。我需要再次右键单击该行以显示上下文菜单。我还尝试了这段代码(这是我的第一种方法,但不再使用它,因为我在某处读到 that 指南是 best/good 与上下文菜单和 tableview 相关的任何内容的实践),并且它立即显示上下文菜单
if (row.getItem() != null) {
rowMenu.show(row, event.getScreenX(), event.getScreenY());
}
else {
// do nothing
}
但我对这段代码的问题是,每当我尝试右键单击没有数据的行时,它都会抛出 NullPointerException。
在右键单击后立即显示上下文菜单的同时,我可以做些什么来防止 NullPointerException?在我的代码中,我还有一个代码,根据绑定到行的 myObject 的 属性,上下文菜单中的某个菜单项将被禁用,这就是为什么我需要上下文菜单立即弹出的原因。
我在第一段代码中也注意到了这一点。即使 myObject 的 属性 已经改变,它仍然有一个菜单项 enabled/disabled,除非我再次右键单击该行。我希望你能帮助我。谢谢!
这是一个 MCVE:
public class MCVE_TableView extends Application{
@Override
public void start(Stage primaryStage) throws Exception {
BorderPane myBorderPane = new BorderPane();
TableView<People> myTable = new TableView<>();
TableColumn<People, String> nameColumn = new TableColumn<>();
TableColumn<People, Integer> ageColumn = new TableColumn<>();
ContextMenu rowMenu = new ContextMenu();
ObservableList<People> peopleList = FXCollections.observableArrayList();
peopleList.add(new People("John Doe", 23));
nameColumn.setMinWidth(100);
nameColumn.setCellValueFactory(
new PropertyValueFactory<>("Name"));
ageColumn.setMinWidth(100);
ageColumn.setCellValueFactory(
new PropertyValueFactory<>("Age"));
myTable.setItems(peopleList);
myTable.getColumns().addAll(nameColumn, ageColumn);
myTable.setRowFactory(tv -> {
TableRow<People> row = new TableRow<>();
row.setOnContextMenuRequested((event) -> {
People selectedRow = row.getItem();
rowMenu.getItems().clear();
MenuItem sampleMenuItem = new MenuItem("Sample Button");
if (selectedRow != null) {
if (selectedRow.getAge() > 100) {
sampleMenuItem.setDisable(true);
}
rowMenu.getItems().add(sampleMenuItem);
}
else {
event.consume();
}
/*if (row.getItem() != null) { // this block comment displays the context menu instantly
rowMenu.show(row, event.getScreenX(), event.getScreenY());
}
else {
// do nothing
}*/
// this requires the row to be right clicked 2 times before displaying the context menu
row.contextMenuProperty().bind(Bindings.when(Bindings.isNotNull(row.itemProperty()))
.then(rowMenu)
.otherwise((ContextMenu)null));
});
return row;
});
myBorderPane.setCenter(myTable);
Scene scene = new Scene(myBorderPane, 500, 500);
primaryStage.setTitle("MCVE");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main (String[] args) {
launch(args);
}
}
这里是人民 Class
public class People {
SimpleStringProperty name;
SimpleIntegerProperty age;
public People(String name, int age) {
this.name = new SimpleStringProperty(name);
this.age = new SimpleIntegerProperty(age);
}
public SimpleStringProperty NameProperty() {
return this.name;
}
public SimpleIntegerProperty AgeProperty() {
return this.age;
}
public String getName() {
return this.name.get();
}
public int getAge() {
return this.age.get();
}
}
编辑:添加了 MCVE
Edit2:更新了 MCVE。弹出contextMenu前仍然需要右键点击两次
下面的代码片段是 how-to/where-to 实例化和配置每行 ContextMenu 的快速演示。它
- 在行的实例化时为每个 TableRow 创建一个 ContextMenu/MenuItem
- 创建一个条件绑定,如果不为空,则将菜单绑定到该行的 contextMenuProperty(与您所做的一样)
- 根据当前项目在 onShowing 处理程序中配置 contextMenu(注意:不需要防止 null,因为条件绑定将隐式保证在这种情况下不显示菜单)
片段:
myTable.setRowFactory(tv -> {
TableRow<People> row = new TableRow<>() {
ContextMenu rowMenu = new ContextMenu();
MenuItem sampleMenuItem = new MenuItem("Sample Button");
{
rowMenu.getItems().addAll(sampleMenuItem);
contextMenuProperty()
.bind(Bindings
.when(Bindings.isNotNull(itemProperty()))
.then(rowMenu).otherwise((ContextMenu) null));
rowMenu.setOnShowing(e -> {
People selectedRow = getItem();
sampleMenuItem.setDisable(selectedRow.getAge() > 100);
});
}
};
return row;
});
因此,我按照 here 中关于将上下文菜单与 TableViews 结合使用的示例进行了操作。我注意到使用这段代码
row.contextMenuProperty().bind(Bindings.when(Bindings.isNotNull(row.itemProperty()))
.then(rowMenu)
.otherwise((ContextMenu)null));
第一次右键单击包含值的行时不显示。我需要再次右键单击该行以显示上下文菜单。我还尝试了这段代码(这是我的第一种方法,但不再使用它,因为我在某处读到 that 指南是 best/good 与上下文菜单和 tableview 相关的任何内容的实践),并且它立即显示上下文菜单
if (row.getItem() != null) {
rowMenu.show(row, event.getScreenX(), event.getScreenY());
}
else {
// do nothing
}
但我对这段代码的问题是,每当我尝试右键单击没有数据的行时,它都会抛出 NullPointerException。
在右键单击后立即显示上下文菜单的同时,我可以做些什么来防止 NullPointerException?在我的代码中,我还有一个代码,根据绑定到行的 myObject 的 属性,上下文菜单中的某个菜单项将被禁用,这就是为什么我需要上下文菜单立即弹出的原因。
我在第一段代码中也注意到了这一点。即使 myObject 的 属性 已经改变,它仍然有一个菜单项 enabled/disabled,除非我再次右键单击该行。我希望你能帮助我。谢谢!
这是一个 MCVE:
public class MCVE_TableView extends Application{
@Override
public void start(Stage primaryStage) throws Exception {
BorderPane myBorderPane = new BorderPane();
TableView<People> myTable = new TableView<>();
TableColumn<People, String> nameColumn = new TableColumn<>();
TableColumn<People, Integer> ageColumn = new TableColumn<>();
ContextMenu rowMenu = new ContextMenu();
ObservableList<People> peopleList = FXCollections.observableArrayList();
peopleList.add(new People("John Doe", 23));
nameColumn.setMinWidth(100);
nameColumn.setCellValueFactory(
new PropertyValueFactory<>("Name"));
ageColumn.setMinWidth(100);
ageColumn.setCellValueFactory(
new PropertyValueFactory<>("Age"));
myTable.setItems(peopleList);
myTable.getColumns().addAll(nameColumn, ageColumn);
myTable.setRowFactory(tv -> {
TableRow<People> row = new TableRow<>();
row.setOnContextMenuRequested((event) -> {
People selectedRow = row.getItem();
rowMenu.getItems().clear();
MenuItem sampleMenuItem = new MenuItem("Sample Button");
if (selectedRow != null) {
if (selectedRow.getAge() > 100) {
sampleMenuItem.setDisable(true);
}
rowMenu.getItems().add(sampleMenuItem);
}
else {
event.consume();
}
/*if (row.getItem() != null) { // this block comment displays the context menu instantly
rowMenu.show(row, event.getScreenX(), event.getScreenY());
}
else {
// do nothing
}*/
// this requires the row to be right clicked 2 times before displaying the context menu
row.contextMenuProperty().bind(Bindings.when(Bindings.isNotNull(row.itemProperty()))
.then(rowMenu)
.otherwise((ContextMenu)null));
});
return row;
});
myBorderPane.setCenter(myTable);
Scene scene = new Scene(myBorderPane, 500, 500);
primaryStage.setTitle("MCVE");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main (String[] args) {
launch(args);
}
} 这里是人民 Class
public class People {
SimpleStringProperty name;
SimpleIntegerProperty age;
public People(String name, int age) {
this.name = new SimpleStringProperty(name);
this.age = new SimpleIntegerProperty(age);
}
public SimpleStringProperty NameProperty() {
return this.name;
}
public SimpleIntegerProperty AgeProperty() {
return this.age;
}
public String getName() {
return this.name.get();
}
public int getAge() {
return this.age.get();
}
}
编辑:添加了 MCVE
Edit2:更新了 MCVE。弹出contextMenu前仍然需要右键点击两次
下面的代码片段是 how-to/where-to 实例化和配置每行 ContextMenu 的快速演示。它
- 在行的实例化时为每个 TableRow 创建一个 ContextMenu/MenuItem
- 创建一个条件绑定,如果不为空,则将菜单绑定到该行的 contextMenuProperty(与您所做的一样)
- 根据当前项目在 onShowing 处理程序中配置 contextMenu(注意:不需要防止 null,因为条件绑定将隐式保证在这种情况下不显示菜单)
片段:
myTable.setRowFactory(tv -> {
TableRow<People> row = new TableRow<>() {
ContextMenu rowMenu = new ContextMenu();
MenuItem sampleMenuItem = new MenuItem("Sample Button");
{
rowMenu.getItems().addAll(sampleMenuItem);
contextMenuProperty()
.bind(Bindings
.when(Bindings.isNotNull(itemProperty()))
.then(rowMenu).otherwise((ContextMenu) null));
rowMenu.setOnShowing(e -> {
People selectedRow = getItem();
sampleMenuItem.setDisable(selectedRow.getAge() > 100);
});
}
};
return row;
});