Context menu on TableRow<Object> does not show up on first right click - javafx

So I followed this example on using context menu with TableViews from here. I noticed that using this code
row.contextMenuProperty().bind(Bindings.when(Bindings.isNotNull(row.itemProperty()))
.then(rowMenu)
.otherwise((ContextMenu)null));
does not show up on first right click on a row with values. I need to right click on that row again for the context menu to show up. I also tried this code(which is my first approach, but not using it anymore because I've read somewhere that that guide is the best/good practice for anything related about context menu and tableview), and it displays the context menu immediately
if (row.getItem() != null) {
rowMenu.show(row, event.getScreenX(), event.getScreenY());
}
else {
// do nothing
}
but my problem with this code is it throws a NullPointerException whenever i try to right click on a row that has no data.
What could I possibly do to prevent NullPointerException while having the context menu show up immediately after a right click? In my code, I also have a code that a certain menu item in the context menu will be disabled based on the property of the myObject binded to row, that's why i need the context menu to pop up right away.
I noticed this too with the first block of code. Even if the property of myObject has already changed, it still has a menu item enabled/disabled unless I right click on that row again. I hope that you could help me. Thank you!
Here is a 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);
}
}
Here is the People 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();
}
}
Edit: MCVE added
Edit2: Updated the MCVE. Still requires to be right-clicked twice before the contextMenu pops up

Below's a code snippet as a quick demonstration of how-to/where-to instantiate and configure a per-row ContextMenu. It
creates a ContextMenu/MenuItem for each TableRow at the row's instantiation time
creates a conditional binding that binds the menu to the row's contextMenuProperty if not empty (just the same as you did)
configures the contextMenu in an onShowing handler, depending on the current item (note: no need for a guard against null, because the conditional binding will implicitly guarantee to not show the the menu in that case)
The snippet:
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;
});

Related

Using filteredList in an editable ListView

I'm using a FilteredList for my ListView to enable searching. The problem is that FilteredList does not allow mutating the data in any way, it only responds to changes in underlying ObservableList.
Also, it is declared final, so I can't simply extend it to forward the edit requests to the source.
So, how can I use it in an Editable ListView?
Here is the code to reproduce the problem
public class Main extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
//problematic code
var observableList = FXCollections.observableArrayList("name", "name 2", "name 3");
FilteredList<String> filteredList = new FilteredList<>(observableList);
var list = new ListView<>(filteredList);
list.setEditable(true);
list.setCellFactory(TextFieldListCell.forListView());
//boilerplate code
VBox wrapper = new VBox(list);
primaryStage.setScene(new Scene(wrapper));
primaryStage.show();
}
}
Edit: added an minimal reproducible example
The problem - as you noticed - is that none of the concrete implementations of TransformationList (Sorted/FilteredList) is modifiable. So the default commit handler fails (with UnsupportedOperationException) while trying to set the newValue:
private EventHandler<ListView.EditEvent<T>> DEFAULT_EDIT_COMMIT_HANDLER = t -> {
int index = t.getIndex();
List<T> list = getItems();
if (index < 0 || index >= list.size()) return;
list.set(index, t.getNewValue());
};
The way out is a custom commit handler. Its implementation depends on context, it can
set a new item in the underlying source list
modify a property of the item
Code snippet for setting an item:
// monolithic items
ObservableList<String> data = FXCollections.observableArrayList("afirst", "abString", "other");
FilteredList<String> filteredData = new FilteredList<>(data);
filteredData.setPredicate(text -> text.contains("a"));
// set up an editable listView
ListView<String> list = new ListView<>(filteredData);
list.setEditable(true);
list.setCellFactory(TextFieldListCell.forListView());
// commitHandler resetting the underlying data element
list.setOnEditCommit(v -> {
ObservableList<String> items = list.getItems();
int index = v.getIndex();
if (items instanceof TransformationList<?, ?>) {
TransformationList transformed = (TransformationList) items;
items = transformed.getSource();
index = transformed.getSourceIndex(index);
}
items.set(index, v.getNewValue());
});
Code snippet for changing a property of an item:
// items with properties
ObservableList<MenuItem> data = FXCollections.observableArrayList(
new MenuItem("afirst"), new MenuItem("abString"), new MenuItem("other"));
FilteredList<MenuItem> filteredData = new FilteredList<>(data);
// filter on text property
filteredData.setPredicate(menuItem -> menuItem.getText().contains("a"));
// set up an editable listView
ListView<MenuItem> list = new ListView<>(filteredData);
list.setEditable(true);
// converter for use in TextFieldListCell
StringConverter<MenuItem> converter = new StringConverter<>() {
#Override
public String toString(MenuItem menuItem) {
return menuItem != null ? menuItem.getText() : null;
}
#Override
public MenuItem fromString(String text) {
return new MenuItem(text);
}
};
list.setCellFactory(TextFieldListCell.forListView(converter));
// commitHandler changing a property of the item
list.setOnEditCommit(v -> {
ObservableList<MenuItem> items = list.getItems();
MenuItem column = items.get(v.getIndex());
MenuItem standIn = v.getNewValue();
column.setText(standIn.getText());
});

Setting a style class only for first-level nodes in JavaFX treeTableView

I have a restaurant menu with dishes and categories implemented as a treeTableView in javaFX.
I want to make the the category rows appear different with CSS but I just can't find a way to filter them out and apply a class. Moving the images a bit to the left would also be nice. I also had no luck using a rowFactory. I've seen this answer but I don't understand it.
This is how I fill the table. I've left out the column- and cellfactories.
private void fillDishes(List<Dish> dishes){
root.getChildren().clear();
Map<String,TreeItem<Dish>> categoryMap = new HashMap<>();
for (Category c: allCats) {
TreeItem<Dish> newCat = new TreeItem<>(new Dish(c.getName(),null,null,null));
//newCat.getGraphic().getStyleClass().add("category");
categoryMap.put(c.getName(),newCat);
root.getChildren().add(newCat);
}
for (Dish d: dishes) {
categoryMap.get(d.getCategory()).getChildren().add(new TreeItem<>(d));
}
}
TreeTableView uses the rowFactory to create the TreeTableRows. At some time later it assigns a TreeItem to a TreeTableRow. This may happen again with different TreeItems for the same row. For this reason you need to handle changes those changes which can be done by adding a ChangeHandler to the TreeTableRow.treeItem property. If a new TreeItem is assigned to the row, you can check for top-level nodes by checking the children of the (invisible) root item for the row item.
I prefer the approach that does not require searching the child list though. It's possible to compare the parent of the item with the root.
public static class Item {
private final String value1;
private final String value2;
public Item(String value1, String value2) {
this.value1 = value1;
this.value2 = value2;
}
public String getValue1() {
return value1;
}
public String getValue2() {
return value2;
}
}
#Override
public void start(Stage primaryStage) {
final TreeItem<Item> root = new TreeItem<>(null);
TreeTableView<Item> ttv = new TreeTableView<>(root);
ttv.setShowRoot(false);
TreeTableColumn<Item, String> column1 = new TreeTableColumn<>();
column1.setCellValueFactory(new TreeItemPropertyValueFactory<>("value1"));
TreeTableColumn<Item, String> column2 = new TreeTableColumn<>();
column2.setCellValueFactory(new TreeItemPropertyValueFactory<>("value2"));
ttv.getColumns().addAll(column1, column2);
final PseudoClass topNode = PseudoClass.getPseudoClass("top-node");
ttv.setRowFactory(t -> {
final TreeTableRow<Item> row = new TreeTableRow<>();
// every time the TreeItem changes, check, if the new item is a
// child of the root and set the pseudoclass accordingly
row.treeItemProperty().addListener((o, oldValue, newValue) -> {
boolean tn = false;
if (newValue != null) {
tn = newValue.getParent() == root;
}
row.pseudoClassStateChanged(topNode, tn);
});
return row;
});
// fill tree structure
TreeItem<Item> c1 = new TreeItem<>(new Item("category 1", null));
c1.getChildren().addAll(
new TreeItem<>(new Item("sub1.1", "foo")),
new TreeItem<>(new Item("sub1.2", "bar")));
TreeItem<Item> c2 = new TreeItem<>(new Item("category 2", null));
c2.getChildren().addAll(
new TreeItem<>(new Item("sub2.1", "answer")),
new TreeItem<>(new Item("sub2.2", "42")));
root.getChildren().addAll(c1, c2);
Scene scene = new Scene(ttv);
scene.getStylesheets().add("style.css");
primaryStage.setScene(scene);
primaryStage.show();
}
style.css
.tree-table-row-cell:top-node {
-fx-background: orange;
}
Moving the images a bit to the left would also be nice.
Usually you do this from a custom TreeTableCell returned by a TreeTableColumn.cellFactory. Depending on the behavior you want to implement setting fitWidth/fitHeight may be sufficient, but in other cases dynamically modifying those values based on the cell size may be required.

How to set the scrollPane to the desired vValue?

I have a tilepane inside a scrollpane. The tilepane is filled with buttons that have actions like delete or edit. After I delete or edit a button, the scrollpane automatically goes back to the top, which I don't want. I want it to remain at the current position (the position the user scrolled to).
I tried getting and setting the vValue of the scrollpane. getvValue gets the value and the setter sets it, but the scrollpane doesn't respond to it and goes back to the top after the action (delete/ edit). I tried a solution from this question: JavaFX ScrollPane setVvalue() not working as intended, but the layout() method doesn't do anything either. What am I doing wrong here? How do I make the scrollpane stay where it is?
EDIT: I found the problem, but still don't know how to fix it. Apparently setting and changing the visibility on the hBoxes in fxml puts the scrollpane back to the top. If I remove the setVisible methods and run it, I can delete items and the scrollpane stays in position. How to fix this?
On request I created a Minimal, Complete, and Verifiable example of my code. Run it, scroll down a bit, right click a button with a name and price on it and then click delete. It scrolls back up to the top.
This is my code:
public class Controller {
private static LinkedHashMap<String, BigDecimal> mProductMap = new LinkedHashMap<>();
#FXML private TilePane fieldContainer = new TilePane();
#FXML private ScrollPane scroll;
#FXML private Button deletebtn;
#FXML private Button editbtn;
#FXML private HBox homeBar;
#FXML private HBox actionBar;
#FXML
private void initialize() {
addProduct("Coffee", new BigDecimal("2.00"));
addProduct("Tea", new BigDecimal("2.00"));
addProduct("Cappuccino", new BigDecimal("2.00"));
addProduct("Espresso", new BigDecimal("2.00"));
addProduct("Cooky", new BigDecimal("2.00"));
addProduct("Candy", new BigDecimal("2.00"));
addProduct("Chocobar", new BigDecimal("2.00"));
addProduct("Cola", new BigDecimal("2.00"));
addProduct("Fanta", new BigDecimal("2.00"));
addProduct("Beer", new BigDecimal("2.00"));
addProduct("Salad", new BigDecimal("2.00"));
addProduct("Sandwich", new BigDecimal("2.00"));
addProduct("Water", new BigDecimal("2.00"));
addProduct("Cassis", new BigDecimal("2.00"));
}
// makes the delete and edit buttons appear after selecting a button from the tilepane
private void select(String selectedProduct) {
/*
the setVisible method for the hBoxes in fxml cause the scrollbar to go back to the top
without them, the scrollpane stays where it is.
I tried changing visibility with both setVisible and CSS, but they both cause the problem
I need the actionbar to appear when you select a button (right click on it)
*/
homeBar.setVisible(false);
actionBar.setVisible(true);
EventHandler<ActionEvent> delete = event -> {
deleteProduct(selectedProduct); // deletes an item from a LinkedHashMap
homeBar.setVisible(true);
actionBar.setVisible(false);
};
deletebtn.setOnAction(delete);
// I want the same to happen when the edit handler is used, scrollpane needs to remain its position
EventHandler<ActionEvent> edit = event -> {
editProduct(selectedProduct); // edits an item from a LinkedHashMap
homeBar.setVisible(true);
actionBar.setVisible(false);
};
editbtn.setOnAction(edit);
}
/*
Code below does not cause the problem, but I added it as a reference
*/
private void deleteProduct(String product) {
if (mProductMap.containsKey(product)) {
mProductMap.remove(product);
System.out.printf("%s has been deleted!%n", product);
} else {
System.out.printf("%s does not exist. Please try again.%n", product);
}
addButtons();
}
private void editProduct(String product) {
List<String> indexKeys = new ArrayList<>(mProductMap.keySet());
List<BigDecimal> indexValues = new ArrayList<>(mProductMap.values());
BigDecimal price = mProductMap.get(product); // gets the product's value (the price)
int indexKey = indexKeys.indexOf(product);
int indexValue = indexValues.indexOf(price);
if (mProductMap.containsKey(product)) {
int sizeBefore = mProductMap.size();
addingProduct();
int sizeAfter = mProductMap.size();
if (sizeAfter > sizeBefore) {
indexKeys.remove(product);
indexValues.remove(price);
mProductMap.remove(product);
// Make a new list to get the new entry at the end
List<Map.Entry<String,BigDecimal>> entryList = new ArrayList<>(mProductMap.entrySet());
Map.Entry<String, BigDecimal> lastEntry = entryList.get(entryList.size()-1);
String key = lastEntry.getKey();
BigDecimal value = lastEntry.getValue();
indexKeys.add(indexKey, key);
indexValues.add(indexValue, value);
mProductMap.clear();
// Put the keys and values from the two lists back to the map
for (int i=0; i<indexKeys.size(); i++) {
addProduct(indexKeys.get(i), indexValues.get(i));
}
}
} else {
System.out.printf("%s does not exist. Please try again.%n", product);
}
}
void addProduct(String product, BigDecimal price) {
mProductMap.put(product, price);
addButtons();
}
// Adding buttons to the TilePane fieldContainer in center of BorderPane
// One button per key-value pair of mProductMap
private void addButtons() {
// clears the TilePane to prevent duplicate buttons
fieldContainer.getChildren().clear();
for (Map.Entry<String, BigDecimal> entry : mProductMap.entrySet()) {
StackPane newField = new StackPane();
Button main = new Button();
main.setOnMousePressed(me -> {
if (me.getButton() == MouseButton.SECONDARY) { // = right click
select(entry.getKey());
}
});
main.setText(entry.getKey() + "\n" + entry.getValue());
newField.getChildren().add(main);
fieldContainer.setAlignment(Pos.TOP_LEFT);
fieldContainer.getChildren().add(newField);
}
}
// Popup for adding products to the Map with the + button
#FXML
private void addingProduct(){
Stage newStage = new Stage();
VBox popup = new VBox();
final BooleanProperty firstTime = new SimpleBooleanProperty(true); // Variable to store the focus on stage load
TextField product = new TextField("");
product.setId("product");
product.setPromptText("Enter the item name...");
// code to remove the focus from first textfield on stage load
product.focusedProperty().addListener((observable, oldValue, newValue) -> {
if(newValue && firstTime.get()){
popup.requestFocus(); // Delegate the focus to container
firstTime.setValue(false); // Variable value changed for future references
}
});
TextField price = new TextField("");
price.setId("price");
price.setPromptText("Enter the item price...");
Button submit = new Button("Submit");
Label label = new Label();
label.setId("label");
submit.setOnAction(e -> {
if ( (product.getText() != null && !product.getText().isEmpty() &&
price.getText() != null && !price.getText().isEmpty() ) ) {
addProduct(product.getText(), new BigDecimal(price.getText()) );
newStage.close();
} else {
label.setText("Fill in both fields");
}
});
popup.getChildren().add(product);
popup.getChildren().add(price);
popup.getChildren().add(submit);
popup.getChildren().add(label);
Scene stageScene = new Scene(popup, 300, 200);
newStage.setScene(stageScene);
newStage.showAndWait();
}
}
The FXML:
<BorderPane xmlns="http://javafx.com/javafx/8.0.121" xmlns:fx="http://javafx.com/fxml/1" fx:controller="sample.Controller">
<top>
<StackPane>
<HBox fx:id="homeBar" styleClass="main-bar" visible="true">
<Button StackPane.alignment="BOTTOM_LEFT">Home</Button>
<Button onAction="#addingProduct" StackPane.alignment="BOTTOM_RIGHT">Add a new product</Button>
</HBox>
<HBox fx:id="actionBar" styleClass="main-bar" visible="false">
<Button fx:id="deletebtn" StackPane.alignment="BOTTOM_CENTER">Delete</Button>
<Button fx:id="editbtn" StackPane.alignment="BOTTOM_CENTER">Edit</Button>
<Button onAction="#addingProduct" StackPane.alignment="BOTTOM_RIGHT">Add a new product</Button>
</HBox>
</StackPane>
</top>
<center>
<ScrollPane fx:id="scroll" hbarPolicy="NEVER">
<TilePane fx:id="fieldContainer" prefColumns="2" prefTileHeight="100.0" prefTileWidth="144.0">
</TilePane>
</ScrollPane>
</center>
<bottom>
</bottom>
</BorderPane>
Main:
public class Main extends Application {
#Override
public void start(Stage primaryStage) throws Exception{
Parent root = FXMLLoader.load(getClass().getResource("sample.fxml"));
primaryStage.setScene(new Scene(root, 300, 275));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
I fixed it myself, with help from answers from this post: ScrollPane jumps to top when deleting nodes.
After deleting a button from the TilePane, the ScrollPane finds the next node to focus on, which by default is the first node from the pane. By requesting the focus on the TilePane (fieldContainer) the ScrollPane stays in place.
I added this code to both the delete and edit methods:
fieldContainer.requestFocus();

JavaFX copying over a object from one tableview to another with a button

So I´m building a warband calculator for a tabletop game and currently it looks like this
What I wanna do is that when I click on a "add" button it copies that unit/creature over to the tableview on the right side(So if I click on the "add" button on the Zombie row, a zombie gets copied over the tableview on the right).
The problem is I can only make it work if you select the row THEN click the button, but I want to be able to rely solely on the button. I think the problem is that I use "getSelectionModel().getSelectedItem()" in the button class to get the object to be copied over, But I cant find any other way to do it.
The relevant code parts from the main class
//Creates the lists
ObservableList<Unit> rightSideList = FXCollections.observableArrayList();
ObservableList<Unit> leftSideList = FXCollections.observableArrayList();
// Puts some test data in the lists
public Main() {
rightSideList.add(new Unit("Skeleton",5,4,9,4,1,6,9));
rightSideList.add(new Unit("Ghoul",6,4,010,4,1,6,7));
rightSideList.add(new Unit("Zombie",4,5,1,3,1,6,5));
rightSideList.add(new Unit("Wraith",4,5,19,3,1,6,5));
rightSideList.add(new Unit("Spectre",4,5,1,3,1,6,5));
leftSideList.add(new Unit("Skeleton",5,4,0,4,1,6,9));
}
//Creates the tables
final TableView<Unit> table1 = new TableView<>(
rightSideList
);
final TableView<Unit> table2 = new TableView<>(
leftSideList
);
//Defines the table columns
//Columns for table 1
TableColumn<Unit,String> unitNameCol = new TableColumn<>("Unit");
unitNameCol.setCellValueFactory(new PropertyValueFactory("unitName"));
unitNameCol.setMinWidth(100);
TableColumn<Unit,Integer> speedCol = new TableColumn<>("Spd");
speedCol.setCellValueFactory(new PropertyValueFactory("speed"));
TableColumn<Unit,Integer> meleeCol = new TableColumn<>("Me");
meleeCol.setCellValueFactory(new PropertyValueFactory("melee"));
TableColumn<Unit,Integer> rangedCol = new TableColumn<>("Ra");
rangedCol.setCellValueFactory(new PropertyValueFactory("ranged"));
TableColumn<Unit,Integer> defenseCol = new TableColumn<>("Def");
defenseCol.setCellValueFactory(new PropertyValueFactory("defense"));
TableColumn<Unit,Integer> attackCol = new TableColumn<>("Att");
attackCol.setCellValueFactory(new PropertyValueFactory("attack"));
TableColumn<Unit,Integer> toughnessCol = new TableColumn<>("To");
toughnessCol.setCellValueFactory(new PropertyValueFactory("toughness"));
TableColumn<Unit,Integer> costCol = new TableColumn<>("Cost");
costCol.setCellValueFactory(new PropertyValueFactory("cost"));
TableColumn<Unit, Boolean> actionCol = new TableColumn<>("Action");
actionCol.setSortable(false);
actionCol.setMinWidth(35);
// define a simple boolean cell value for the action column so that the column will only be shown for non-empty rows for table 1
actionCol.setCellValueFactory(new Callback<TableColumn.CellDataFeatures<Unit, Boolean>, ObservableValue<Boolean>>() {
#Override public ObservableValue<Boolean> call(TableColumn.CellDataFeatures<Unit, Boolean> features) {
return new SimpleBooleanProperty(features.getValue() != null);
}
});
// create a cell value factory with an add button for each row in the table for table 1
actionCol.setCellFactory(new Callback<TableColumn<Unit, Boolean>, TableCell<Unit, Boolean>>() {
#Override public TableCell<Unit, Boolean> call(TableColumn<Unit, Boolean> unitBooleanTableColumn) {
return new AddUnitCell(mainStage, table1);
}
});
And here is the button class
/** A table cell containing a button for adding a unit */
private class AddUnitCell extends TableCell<Unit, Boolean> {
// a button for adding a new Unit.
final Button addButton = new Button("Add");
// pads and centers the add button in the cell.
final StackPane paddedButton = new StackPane();
/**
* AddUnitCell constructor
* #param stage the stage in which the table is placed.
* #param table the table to which a unit can be added.
*/
AddUnitCell(final Stage stage, final TableView<Unit> table) {
paddedButton.setPadding(new Insets(3));
paddedButton.getChildren().add(addButton);
addButton.setOnAction(new EventHandler<ActionEvent>() {
#Override public void handle(ActionEvent actionEvent) {
Unit selectedUnit = table.getSelectionModel().getSelectedItem();
leftSideList.add(selectedUnit);
}
});
}
/** places an add button in the row only if the row is not empty. */
#Override protected void updateItem(Boolean item, boolean empty) {
super.updateItem(item, empty);
if (!empty) {
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
setGraphic(paddedButton);
} else {
setGraphic(null);
}
}
}
If you need to check in detail here is a link to the projects repository
https://github.com/MagnusLindstrom88/Star-Struck-City-Warband-Calculator/blob/master/src/application/Main.java
Instead of
Unit selectedUnit = table.getSelectionModel().getSelectedItem();
in the button event handler's handle() method, do
Unit selectedUnit = table.getItems().get(getIndex());
Instead of relying on the selection model to get the item, you should get the item from the TableRow:
Unit selectedUnit = (Unit) getTableRow().getItem();

vaadin] how to Showing extra data for Grid rows

I used grid component, and added row details..
However grid doesn't show details..
It's my code.
====
Grid grid = new Grid("Plain Grid");
grid.setDetailsGenerator(new DetailsGenerator() {
#Override
public Component getDetails(RowReference rowReference) {
// Find the bean to generate details for
final TrainingMemberVo bean = (TrainingMemberVo) rowReference.getItemId();
// A basic label with bean data
Label label = new Label("Extra data for " + bean.getMemberName());
// A button just for the sake of the example
Button button = new Button("Click me", new Button.ClickListener() {
#Override
public void buttonClick(ClickEvent event) {
Notification.show("Button clicked for " + bean.getMemberName());
}
});
// Wrap up all the parts into a vertical layout
VerticalLayout layout = new VerticalLayout(label, button);
layout.setSpacing(true);
layout.setMargin(true);
return layout;
}
});
grid.addItemClickListener(new ItemClickListener() {
#Override
public void itemClick(ItemClickEvent event) {
if (event.isDoubleClick()) {
Object itemId = event.getItemId();
grid.setDetailsVisible(itemId, !grid.isDetailsVisible(itemId));
}
}
});
===
double clicked row, noting shows details..
Works for me without any issues. Have you checked if the event in the itemClick() is correctly fired? I. e. remove the boolean check for event.isDoubleClick() to see if it gets executed.

Resources