I have Created a custom control call it ComboBoxTablePopup extending Comboboxbase class. I have used a tableview as a popup content. Everything works fine,
update value, show popup, hide popup. After switching the focus from ComboBoxTablePopup to another control like a TextField or Spinner, it updates it self with null value.
So, I don't know what makes this happens. So here is my implementation on self executable class.
import com.sun.javafx.scene.control.behavior.ComboBoxBaseBehavior;
import com.sun.javafx.scene.control.behavior.KeyBinding;
import com.sun.javafx.scene.control.skin.ComboBoxListViewSkin;
import com.sun.javafx.scene.control.skin.ComboBoxPopupControl;
import javafx.application.Application;
import javafx.beans.InvalidationListener;
import javafx.beans.WeakInvalidationListener;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.ReadOnlyObjectProperty;
import javafx.beans.property.ReadOnlyObjectWrapper;
import javafx.beans.property.SimpleObjectProperty;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.collections.WeakListChangeListener;
import javafx.event.ActionEvent;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.input.KeyCode;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.StringConverter;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Predicate;
public class TestComboboxTablePopup extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
ComboBoxTablePopupControl<dataModel> comboBoxTablePopup = new ComboBoxTablePopupControl<>();
TableColumn<dataModel, Integer> tcId = new TableColumn<>("Id");
TableColumn<dataModel, String> tcName = new TableColumn<>("Name");
tcId.setCellValueFactory(new PropertyValueFactory<dataModel, Integer>("id"));
tcName.setCellValueFactory(new PropertyValueFactory<dataModel, String>("name"));
comboBoxTablePopup.setColumns(FXCollections.observableArrayList(tcId, tcName));
comboBoxTablePopup.setItems(FXCollections.observableArrayList(
new dataModel(1, "Data Model object 1"),
new dataModel(2, "Data Model object 2"),
new dataModel(3, "Data Model object 3")
));
VBox vBox = new VBox(comboBoxTablePopup);
Scene scene = new Scene(vBox);
primaryStage.setScene(scene);
primaryStage.setWidth(400);
primaryStage.setHeight(300);
primaryStage.show();
}
public class dataModel {
private int id;
private String name;
public dataModel(int id, String name) {
this.id = id;
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
private static <S> StringConverter<S> defaultStringConverter() {
return new StringConverter<S>() {
#Override
public String toString(S t) {
return t == null ? "abood fait" : t.toString();
}
#Override
public S fromString(String string) {
return null;
}
};
}
private class ComboBoxTablePopupControl<S> extends ComboBoxBase {
/***************************************************************************
* *
* Static properties and methods *
* *
**************************************************************************/
private static final String DEFAULT_STYLE_CLASS = "combobox-table-popup";
private ObjectProperty<ObservableList<S>> items = new SimpleObjectProperty<ObservableList<S>>(this, "items");
public final void setItems(ObservableList<S> value) {
itemsProperty().set(value);
}
public final ObservableList<S> getItems() {
return items.get();
}
public ObjectProperty<ObservableList<S>> itemsProperty() {
return items;
}
public ObjectProperty<StringConverter<S>> converterProperty() {
return converter;
}
private ObjectProperty<StringConverter<S>> converter =
new SimpleObjectProperty<StringConverter<S>>(this, "converter", defaultStringConverter());
public final void setConverter(StringConverter<S> value) {
converterProperty().set(value);
}
public final StringConverter<S> getConverter() {
return converterProperty().get();
}
// Editor
private ReadOnlyObjectWrapper<TextField> editor;
public final TextField getEditor() {
return editorProperty().get();
}
public final ReadOnlyObjectProperty<TextField> editorProperty() {
if (editor == null) {
editor = new ReadOnlyObjectWrapper<TextField>(this, "editor");
editor.set(new ComboBoxListViewSkin.FakeFocusTextField());
}
return editor.getReadOnlyProperty();
}
private
ObservableList<TableColumn<S, ?>> columns = FXCollections.observableArrayList();
public ObservableList<TableColumn<S, ?>> getColumns() {
return columns;
}
public void setColumns(ObservableList<TableColumn<S, ?>> columns) {
this.columns = columns;
}
/***************************************************************************
* *
* Constructors *
* *
**************************************************************************/
/**
* Creates a default ComboboxTablePopup instance with an empty
* {#link #itemsProperty() items} list and default
* {#link #selectionModelProperty() selection model}.
*/
public ComboBoxTablePopupControl() {
this(FXCollections.<S>emptyObservableList());
}
/**
* Creates a default ComboboxTablePopup instance with the provided items list and
* a default { selection model}.
*/
public ComboBoxTablePopupControl(ObservableList<S> items) {
setItems(items);
getStyleClass().add(DEFAULT_STYLE_CLASS);
setEditable(true);
valueProperty().addListener((observable, oldValue, newValue) -> {
System.out.println(newValue);
});
}
public ComboBoxTablePopupControl(ObservableList<S> items, ObservableList<TableColumn<S, ?>> columns) {
this(items);
this.columns = columns;
}
#Override
protected Skin<?> createDefaultSkin() {
return new ComboBoxTablePopupControlSkin<>(this);
}
}
public class ComboBoxTablePopupControlSkin<S> extends ComboBoxPopupControl {
private ComboBoxTablePopupControl comboBoxTablePopup;
private ObservableList<S> comboboxTablePopupItems;
private TableView<S> tableViewPopupContent;
private ObservableList<S> tableViewPopupItems;
private Predicate<S> predicate;
private final InvalidationListener itemsObserver;
private final ListChangeListener<S> tableViewItemsListener = new ListChangeListener<S>() {
#Override
public void onChanged(ListChangeListener.Change<? extends S> c) {
getSkinnable().requestLayout();
}
};
private final WeakListChangeListener<S> weakListViewItemsListener =
new WeakListChangeListener<S>(tableViewItemsListener);
public ComboBoxTablePopupControlSkin(ComboBoxTablePopupControl comboBoxTablePopup) {
super(comboBoxTablePopup, new ComboBoxBaseBehavior(comboBoxTablePopup, null));
this.comboBoxTablePopup = comboBoxTablePopup;
updateComboBoxTablePopupItems();
itemsObserver = observable -> {
updateComboBoxTablePopupItems();
updateTableViewItems();
};
this.comboBoxTablePopup.itemsProperty().addListener(new WeakInvalidationListener(itemsObserver));
tableViewPopupContent = createTableView();
tableViewPopupContent.setManaged(false);
getChildren().add(tableViewPopupContent);
updateTableViewItems();
registerChangeListener(comboBoxTablePopup.converterProperty(), "CONVERTER");
registerChangeListener(comboBoxTablePopup.itemsProperty(), "ITEMS");
registerChangeListener(comboBoxTablePopup.valueProperty(), "VALUE");
registerChangeListener(comboBoxTablePopup.editorProperty(), "EDITABLE");
}
private void updateTableViewItems() {
this.tableViewPopupItems = comboBoxTablePopup.getItems();
this.tableViewPopupContent.setItems(this.tableViewPopupItems);
if (tableViewPopupItems != null) {
tableViewPopupItems.removeListener(weakListViewItemsListener);
}
this.tableViewPopupItems = comboboxTablePopupItems;
tableViewPopupContent.setItems(tableViewPopupItems);
if (tableViewPopupItems != null) {
tableViewPopupItems.addListener(weakListViewItemsListener);
}
getSkinnable().requestLayout();
}
public void updateComboBoxTablePopupItems() {
comboboxTablePopupItems = comboBoxTablePopup.getItems();
comboboxTablePopupItems = comboboxTablePopupItems == null ? FXCollections.<S>emptyObservableList() : comboboxTablePopupItems;
}
private TableView<S> createTableView() {
final TableView<S> tableView = new TableView<>();
tableView.setId("table-view");
tableView.getSelectionModel().setSelectionMode(SelectionMode.SINGLE);
tableView.setFocusTraversable(false);
for (TableColumn tblColumn : tableColumns()) {
tableView.getColumns().add(tblColumn);
}
tableView.getSelectionModel().selectedItemProperty().addListener(o -> {
S selectedItem = tableView.getSelectionModel().getSelectedItem();
comboBoxTablePopup.setValue(selectedItem);
});
tableView.setOnKeyPressed(e -> {
if (e.getCode() == KeyCode.ENTER ||
e.getCode() == KeyCode.SPACE) {
S selectedItem = tableView.getSelectionModel().getSelectedItem();
comboBoxTablePopup.setValue(selectedItem);
comboBoxTablePopup.hide();
}
});
return tableView;
}
private ObservableList<TableColumn> tableColumns() {
return ((ComboBoxTablePopupControl) getSkinnable()).getColumns();
}
#Override
protected Node getPopupContent() {
return this.tableViewPopupContent;
}
#Override
protected TextField getEditor() {
return ((ComboBoxTablePopupControl) getSkinnable()).getEditor();
}
#Override
protected StringConverter<S> getConverter() {
return ((ComboBoxTablePopupControl) getSkinnable()).getConverter();
}
#Override
public Node getDisplayNode() {
Node displayNode;
displayNode = getEditableInputNode();
updateDisplayNode();
return displayNode;
}
#Override
protected void handleControlPropertyChanged(String p) {
if ("VALUE".equals(p)) {
updateDisplayNode();
System.out.println(comboBoxTablePopup.getValue());
comboBoxTablePopup.fireEvent(new ActionEvent());
} else if ("CONVERTER".equals(p)) {
updateDisplayNode();
System.out.println("Conveter peroptery");
} else if ("ITEMS".equals(p)) {
updateComboBoxTablePopupItems();
updateTableViewItems();
} else if ("EDITOR".equals(p)) {
getEditableInputNode();
} else
super.handleControlPropertyChanged(p);
}
}
}
If someone still has trouble with this issue, here is a workaround for a choicebox. I assume you already have a table with columns and an EventHandler. This example is not tested with any editable (isEditable(true)) object.
First set a custom Factory and an EventHandler for setOnEditStart
yourColumn.setCellFactory(value -> new CustomEditFactory(yourObsList));
In your CustomEditFactory class you have to extend TreeTableCell<KnotenObs, String> and #Override four methods
startEdit()
#Override
public void startEdit() {
super.startEdit();
choiceBox.getSelectionModel().select(getItem());
oldValue = choiceBox.getSelectionModel().getSelectedItem();
setGraphic(choiceBox);
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
}
commitEdit()
#Override
public void commitEdit(String newValue) {
if(newValue == null) {
super.commitEdit(oldValue);
} else {
super.commitEdit(newValue);
}
setContentDisplay(ContentDisplay.TEXT_ONLY);
}
cancelEdit()
#Override
public void cancelEdit() {
super.cancelEdit();
if(getItem() == null) {
setText(oldValue);
} else {
setText(getItem());
}
setContentDisplay(ContentDisplay.TEXT_ONLY);
}
updateItem()
#Override
public void updateItem(String item, boolean empty) {
if(item == null) {
super.updateItem(oldValue, empty);
setText(oldValue);
} else {
super.updateItem(item, empty);
setText(item);
}
setContentDisplay(ContentDisplay.TEXT_ONLY);
}
Related
I am trying to implement a TreeTableView in javafx where the first column holds string values and the third one is to be rendered as 3-state checkboxes.
With the following MCVE I am able to get a treetable but none of the selections in the checkboxes persist on parent collapse/expand or on resize of the table.
MCVE
Class A is parent.
Class B extends A and is child.
Class C represents the 2nd column, (rendered as checkboxes)
Class MVCECheckBox builds the treetable and displays it.
A.java
package mcve.checkbox;
import java.util.List;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
/**
*
* #author returncode13
*/
public class A {
StringProperty name=new SimpleStringProperty();
C check=new C();
List<A> children;
public StringProperty getName() {
return name;
}
public void setName(String name) {
this.name.set(name);
}
public C getCheck() {
return check;
}
public void setCheck(C check) {
this.check = check;
}
public StringProperty nameProperty(){
return name;
}
public List<A> getChildren() {
return children;
}
public void setChildren(List<A> children) {
this.children = children;
}
}
B.java
package mcve.checkbox;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
/**
*
* #author returncode13
*/
public class B extends A{
StringProperty name=new SimpleStringProperty();
C Check=new C();
#Override
public StringProperty getName() {
return name;
}
#Override
public void setName(String name) {
this.name.set(name);
}
#Override
public C getCheck() {
return Check;
}
#Override
public void setCheck(C Check) {
this.Check = Check;
}
#Override
public StringProperty nameProperty(){
return name;
}
}
C.java
package mcve.checkbox;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
/**
*
* #author returncode13
*/
public class C {
BooleanProperty checkUncheck=new SimpleBooleanProperty();
BooleanProperty indeterminate=new SimpleBooleanProperty();
public BooleanProperty getCheckUncheck() {
return checkUncheck;
}
public void setCheckUncheck(BooleanProperty checkUncheck) {
this.checkUncheck = checkUncheck;
}
public BooleanProperty getIndeterminate() {
return indeterminate;
}
public void setIndeterminate(BooleanProperty indeterminate) {
this.indeterminate = indeterminate;
}
public BooleanProperty checkUncheckProperty(){
return checkUncheck;
}
public BooleanProperty indeterminateProperty(){
return indeterminate;
}
}
MCVECheckBox.java
package mcve.checkbox;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.value.ObservableValue;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.CheckBox;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeTableCell;
import javafx.scene.control.TreeTableColumn;
import javafx.scene.control.TreeTableView;
import javafx.scene.control.cell.TreeItemPropertyValueFactory;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import javafx.util.Callback;
/**
*
* #author returncode13
*/
public class MCVECheckBox extends Application {
A selectedItem;
private TreeTableView<A> treetable=new TreeTableView<>();
#Override
public void start(Stage primaryStage) {
//setting up parents (A) and children (B)
A a1=new A();
a1.setName("A1");
List<A> A1Children=new ArrayList();
B b11=new B();
b11.setName("B11");
B b12=new B();
b12.setName("B12");
A1Children.add(b11);
A1Children.add(b12);
a1.setChildren(A1Children);
A a2=new A();
a2.setName("A2");
List<A> A2Children=new ArrayList();
B b21=new B();
b21.setName("B21");
B b22=new B();
b22.setName("B22");
A2Children.add(b21);
A2Children.add(b22);
a2.setChildren(A2Children);
//tree columns . first one holds strings
TreeTableColumn<A,String> name=new TreeTableColumn<>("Name");
name.setCellValueFactory(new TreeItemPropertyValueFactory<>("name"));
//2nd tree columns. rendered as checkboxes. boolean values
TreeTableColumn<A,Boolean> checks=new TreeTableColumn<>("Checks");
checks.setCellValueFactory(new Callback<TreeTableColumn.CellDataFeatures<A, Boolean>, ObservableValue<Boolean>>() {
#Override
public ObservableValue<Boolean> call(TreeTableColumn.CellDataFeatures<A, Boolean> param) {
A a=param.getValue().getValue();
SimpleBooleanProperty checkUncheck=new SimpleBooleanProperty();
SimpleBooleanProperty indeterminate=new SimpleBooleanProperty();
checkUncheck=(SimpleBooleanProperty) a.getCheck().getCheckUncheck();
indeterminate=(SimpleBooleanProperty) a.getCheck().getIndeterminate();
//to do: set parents status based on children status.
if(indeterminate.get()){
return indeterminate;
}else{
return checkUncheck;
}
}
});
checks.setCellFactory(new Callback<TreeTableColumn<A, Boolean>, TreeTableCell<A, Boolean>>() {
#Override
public TreeTableCell<A, Boolean> call(TreeTableColumn<A, Boolean> param) {
return new CheckBoxCell();
}
});
//building the tree;
TreeItem<A> a1item=new TreeItem<>(a1);
TreeItem<A> b11item=new TreeItem<>(b11);
TreeItem<A> b12item=new TreeItem<>(b12);
a1item.getChildren().add(b11item);
a1item.getChildren().add(b12item);
TreeItem<A> a2item=new TreeItem<>(a2);
TreeItem<A> b21item=new TreeItem<>(b21);
TreeItem<A> b22item=new TreeItem<>(b22);
a2item.getChildren().add(b21item);
a2item.getChildren().add(b22item);
TreeItem<A> root=new TreeItem<>();
root.getChildren().add(a1item);
root.getChildren().add(a2item);
treetable.getColumns().addAll(name,checks);
treetable.setRoot(root);
treetable.setShowRoot(false);
treetable.setEditable(true);
// StackPane rootSp = new StackPane();
Scene scene = new Scene(treetable, 300, 250);
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
//to render checkboxes in treetable
private class CheckBoxCell extends TreeTableCell<A, Boolean> {
CheckBox checkbox;
public CheckBoxCell() {
checkbox=new CheckBox();
checkbox.setAllowIndeterminate(true);
checkbox.selectedProperty().addListener((obs,wasSelected,isNowSelected) -> {
if(isNowSelected){
selectedItem=getTreeTableRow().getItem();
}
});
}
#Override
public void updateItem(Boolean b,boolean empty){
super.updateItem(b, empty);
if(empty){
setGraphic(null);
}else{
checkbox.setSelected(b);
setGraphic(checkbox);
}
}
}
}
I have earlier tried using the CheckTreeTableCell to set the cell factory on the second column, but soon found out that the CheckTreeTableCell doesn't support 3-state (check,uncheck,indeterminate) checkboxes.
After which I tried implementing the above code. Although I am able to bring in 3-state checkboxes, I am unable to let their state persist. Each time a parent is collapsed/expanded the checks made on its children become unselected.
Thanks for any help on determining a fix.
I am now able to implement the 3-state checkbox with the following modifications to the posted MCVE which is now a complete working example .
A.java (parent class)
package com.mycompany.yetanothercheckbox;
import java.util.Iterator;
import java.util.List;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
/**
*
* #author returncode13
*/
public class A {
private StringProperty name=new SimpleStringProperty();
C check=new C();
List<A> children;
final boolean isLeaf=false;
final boolean isParent=true;
public boolean updateParent=false;
public boolean updateChildren=false;
public boolean isLeaf() {
return isLeaf;
}
public boolean isParent() {
return isParent;
}
public StringProperty getName() {
return name;
}
public void setName(String name) {
this.name.set(name);
}
public C getCheck() {
return check;
}
public void setCheck(C check) {
this.check = check;
for (Iterator<A> iterator = children.iterator(); iterator.hasNext();) {
A next = iterator.next();
next.setCheck(check);
}
}
public StringProperty nameProperty(){
return name;
}
public List<A> getChildren() {
return children;
}
public void setChildren(List<A> children) {
this.children = children;
}
public A getParent() {
return this;
}
}
B.java (child class)
package com.mycompany.yetanothercheckbox;
import java.util.List;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
/**
*
* #author returncode13
*/
public class B extends A{
private StringProperty name=new SimpleStringProperty();
C Check=new C();
final boolean isLeaf=true;
final boolean isParent=false;
public boolean updateParent=false;
public boolean updateChildren=false;
A parent;
public A getParent() {
return parent;
}
public void setParent(A parent) {
this.parent = parent;
}
#Override
public boolean isLeaf() {
return isLeaf;
}
#Override
public boolean isParent() {
return isParent;
}
#Override
public StringProperty getName() {
return name;
}
#Override
public void setName(String name) {
this.name.set(name);
}
#Override
public C getCheck() {
return Check;
}
#Override
public void setCheck(C Check) {
this.Check = Check;
}
#Override
public StringProperty nameProperty(){
return name;
}
#Override
public List<A> getChildren() {
return parent.getChildren();
}
}
C.java (Hold Check states)
package com.mycompany.yetanothercheckbox;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
/**
*
* #author returncode13
*/
public class C {
BooleanProperty checkUncheck=new SimpleBooleanProperty();
BooleanProperty indeterminate=new SimpleBooleanProperty();
public BooleanProperty getCheckUncheck() {
return checkUncheck;
}
public void setCheckUncheck(BooleanProperty checkUncheck) {
this.checkUncheck = checkUncheck;
}
public BooleanProperty getIndeterminate() {
return indeterminate;
}
public void setIndeterminate(BooleanProperty indeterminate) {
this.indeterminate = indeterminate;
}
public BooleanProperty checkUncheckProperty(){
return checkUncheck;
}
public BooleanProperty indeterminateProperty(){
return indeterminate;
}
}
ThreeStateCheckBoxTreeTableCell.java (the 3state checkbox for tree table)
package com.mycompany.yetanothercheckbox;
import java.util.List;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.event.EventHandler;
import javafx.scene.control.CheckBox;
import javafx.scene.control.TreeTableCell;
import javafx.scene.control.TreeTableColumn;
import javafx.scene.input.MouseEvent;
/**
*
* #author returncode13
*/
//to render checkboxes in treetable
public class ThreeStateCheckBoxTreeTableCell extends TreeTableCell<A, Boolean> {
A selectedItem;
CheckBox checkbox;
TreeTableColumn<A,Boolean> param;
/*private static boolean updateParent=false;
private static boolean updateChildren=false;*/
public ThreeStateCheckBoxTreeTableCell(TreeTableColumn<A,Boolean> param) {
checkbox=new CheckBox();
this.param=param;
checkbox.setAllowIndeterminate(true);
checkbox.selectedProperty().addListener((obs,wasSelected,isNowSelected) -> {
int sel=getTreeTableRow().getIndex();
selectedItem=this.param.getTreeTableView().getSelectionModel().getModelItem(sel).getValue();
selectedItem.getCheck().getCheckUncheck().set(isNowSelected);
selectedItem.getCheck().getIndeterminate().set(false);
//ThreeStateCheckBoxTreeTableCell.this.param.getTreeTableView().refresh();
});
checkbox.indeterminateProperty().addListener((obx,ol,newV)->{
int sel=getTreeTableRow().getIndex();
selectedItem=this.param.getTreeTableView().getSelectionModel().getModelItem(sel).getValue();
selectedItem.getCheck().getIndeterminate().set(newV);
//ThreeStateCheckBoxTreeTableCell.this.param.getTreeTableView().refresh();
});
checkbox.setOnMouseClicked(new EventHandler<MouseEvent>(){
#Override
public void handle(MouseEvent event) {
int sel=getTreeTableRow().getIndex();
selectedItem=ThreeStateCheckBoxTreeTableCell.this.param.getTreeTableView().getSelectionModel().getModelItem(sel).getValue();
if(selectedItem.isParent()){
selectedItem.updateChildren=true;
for(A child:selectedItem.getChildren()){
child.updateParent=false;
}
updateDownwards();
}
if(selectedItem.isLeaf()){
selectedItem.getParent().updateChildren=false;
selectedItem.updateParent=true;
updateUpWards();
}
ThreeStateCheckBoxTreeTableCell.this.param.getTreeTableView().refresh();
}
});
}
#Override
public void updateItem(Boolean b,boolean empty){
super.updateItem(b, empty);
if(empty){
setGraphic(null);
}else{
if(b==null){
checkbox.setIndeterminate(true);
}
else{
checkbox.setIndeterminate(false);
checkbox.setSelected(b);
}
setGraphic(checkbox);
}
ThreeStateCheckBoxTreeTableCell.this.param.getTreeTableView().refresh();
}
private void updateUpWards(){
if(selectedItem.updateParent){
List<A> children=selectedItem.getChildren();
int indeterminateCount=0;
int selectedCount=0;
A parent=selectedItem.getParent();
for(A child:children){
indeterminateCount+=child.getCheck().getIndeterminate().get()?1:0;
selectedCount+=child.getCheck().getCheckUncheck().get()?1:0;
}
if(indeterminateCount>0) {
parent.getCheck().getIndeterminate().set(true);
}
else if(indeterminateCount==0 && selectedCount==children.size()){
parent.getCheck().getIndeterminate().set(false);
parent.getCheck().getCheckUncheck().set(true);
}else{
parent.getCheck().getIndeterminate().set(false);
parent.getCheck().getCheckUncheck().set(false);
}
}
ThreeStateCheckBoxTreeTableCell.this.param.getTreeTableView().refresh();
}
private void updateDownwards(){
List<A> children=selectedItem.getChildren();
if(selectedItem.isParent() && selectedItem.updateChildren ){
for(A child:children){
child.getCheck().getCheckUncheck().set(selectedItem.getCheck().getCheckUncheck().get());
child.getCheck().getIndeterminate().set(selectedItem.getCheck().getIndeterminate().get());
}
}
ThreeStateCheckBoxTreeTableCell.this.param.getTreeTableView().refresh();
}
}
MainApp.java (Application as a POC)
package com.mycompany.yetanothercheckbox;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import javafx.application.Application;
import static javafx.application.Application.launch;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.CheckBox;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeTableCell;
import javafx.scene.control.TreeTableColumn;
import javafx.scene.control.TreeTableView;
import javafx.scene.control.cell.TreeItemPropertyValueFactory;
import javafx.stage.Stage;
import javafx.util.Callback;
public class MainApp extends Application {
private TreeTableView<A> treetable=new TreeTableView<>();
#Override
public void start(Stage primaryStage) {
//setting up parents (A) and children (B)
A a1=new A();
a1.setName("A1");
List<A> A1Children=new ArrayList();
B b11=new B();
b11.setName("B11");
B b12=new B();
b12.setName("B12");
b11.setParent(a1);
b12.setParent(a1);
A1Children.add(b11);
A1Children.add(b12);
a1.setChildren(A1Children);
A a2=new A();
a2.setName("A2");
List<A> A2Children=new ArrayList();
B b21=new B();
b21.setName("B21");
B b22=new B();
b22.setName("B22");
b21.setParent(a2);
b22.setParent(a2);
A2Children.add(b21);
A2Children.add(b22);
a2.setChildren(A2Children);
//tree columns . first one holds strings
TreeTableColumn<A,String> name=new TreeTableColumn<>("Name");
name.setCellValueFactory(new TreeItemPropertyValueFactory<>("name"));
//2nd tree columns. rendered as checkboxes. boolean values
TreeTableColumn<A,Boolean> checks=new TreeTableColumn<>("Checks");
checks.setCellValueFactory(new Callback<TreeTableColumn.CellDataFeatures<A, Boolean>, ObservableValue<Boolean>>() {
#Override
public ObservableValue<Boolean> call(TreeTableColumn.CellDataFeatures<A, Boolean> param) {
A a=param.getValue().getValue();
SimpleBooleanProperty checkUncheck=new SimpleBooleanProperty();
SimpleBooleanProperty indeterminate=new SimpleBooleanProperty();
checkUncheck.bindBidirectional(a.getCheck().getCheckUncheck());
indeterminate.bindBidirectional(a.getCheck().getIndeterminate());
checkUncheck.addListener(new ChangeListener<Boolean>(){
#Override
public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
a.getCheck().indeterminateProperty().set(false);
a.getCheck().checkUncheckProperty().set(newValue);
}
});
indeterminate.addListener(new ChangeListener<Boolean>(){
#Override
public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
a.getCheck().indeterminateProperty().set(newValue);
}
});
if(indeterminate.get()){
return null;
}else{
return checkUncheck;
}
}
});
checks.setCellFactory(new Callback<TreeTableColumn<A, Boolean>, TreeTableCell<A, Boolean>>() {
#Override
public TreeTableCell<A, Boolean> call(TreeTableColumn<A, Boolean> param) {
return new ThreeStateCheckBoxTreeTableCell(param);
}
});
//building the tree;
TreeItem<A> a1item=new TreeItem<>(a1);
TreeItem<A> b11item=new TreeItem<>(b11);
TreeItem<A> b12item=new TreeItem<>(b12);
a1item.getChildren().add(b11item);
a1item.getChildren().add(b12item);
TreeItem<A> a2item=new TreeItem<>(a2);
TreeItem<A> b21item=new TreeItem<>(b21);
TreeItem<A> b22item=new TreeItem<>(b22);
a2item.getChildren().add(b21item);
a2item.getChildren().add(b22item);
TreeItem<A> root=new TreeItem<>();
root.getChildren().add(a1item);
root.getChildren().add(a2item);
treetable.getColumns().addAll(name,checks);
treetable.setRoot(root);
treetable.setShowRoot(false);
treetable.setEditable(true);
// StackPane rootSp = new StackPane();
Scene scene = new Scene(treetable, 300, 250);
primaryStage.setTitle("CheckBoxTreeTable Example");
primaryStage.setScene(scene);
primaryStage.show();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
}
Thank you very much for this great piece of code that gives insight on the fact that the 3-state checkbox does NOT exist for a tree item table cell (
For those who will feed on this post, I made it work with some minor changes on the class ''. Now looking like this :
package com.mycompany.yetanothercheckbox;
import java.util.List;
import javafx.scene.control.CheckBox;
import javafx.scene.control.TreeTableCell;
import javafx.scene.control.TreeTableColumn;
/**
*
* #author returncode13
*/
// to render checkboxes in treetable
public class ThreeStateCheckBoxTreeTableCell extends TreeTableCell<A, Boolean> {
A selectedItem;
CheckBox checkbox;
TreeTableColumn<A, Boolean> param;
/*
* private static boolean updateParent=false;
* private static boolean updateChildren=false;
*/
public ThreeStateCheckBoxTreeTableCell(TreeTableColumn<A, Boolean> param) {
checkbox = new CheckBox();
this.param = param;
checkbox.setAllowIndeterminate(true);
checkbox.selectedProperty().addListener((obs,
oldSelectedVal,
newSelectedVal) -> {
int sel = getTreeTableRow().getIndex();
selectedItem = this.param.getTreeTableView().getSelectionModel().getModelItem(sel).getValue();
selectedItem.getCheck().getCheckUncheck().set(newSelectedVal);
selectedItem.getCheck().getIndeterminate().set(false);
});
checkbox.indeterminateProperty().addListener((obx,
ol,
newV) -> {
int sel = getTreeTableRow().getIndex();
selectedItem = this.param.getTreeTableView().getSelectionModel().getModelItem(sel).getValue();
selectedItem.getCheck().getIndeterminate().set(newV);
});
checkbox.setOnMouseClicked(event -> {
int sel = getTreeTableRow().getIndex();
selectedItem = this.param.getTreeTableView().getSelectionModel().getModelItem(sel).getValue();
if (selectedItem.isParent()) {
selectedItem.updateChildren = true;
for (A child : selectedItem.getChildren()) { child.updateParent = false; }
updateDownwards();
}
if (selectedItem.isLeaf()) {
selectedItem.getParent().updateChildren = false;
selectedItem.updateParent = true;
updateUpWards();
}
});
}
#Override
public void updateItem(Boolean b,
boolean empty) {
super.updateItem(b, empty);
if (empty) {
setGraphic(null);
} else {
if (b == null) {
checkbox.setIndeterminate(true);
} else {
checkbox.setIndeterminate(false);
checkbox.setSelected(b);
}
setGraphic(checkbox);
}
}
private void updateUpWards() {
if (selectedItem.updateParent) {
List<A> children = selectedItem.getChildren();
int indeterminateCount = 0;
int selectedCount = 0;
A parent = selectedItem.getParent();
for (A child : children) {
indeterminateCount += child.getCheck().getIndeterminate().get() ? 1 : 0;
selectedCount += child.getCheck().getCheckUncheck().get() ? 1 : 0;
}
if (indeterminateCount > 0) {
parent.getCheck().getIndeterminate().set(true);
} else if (indeterminateCount == 0
&& selectedCount == children.size()) {
parent.getCheck().getIndeterminate().set(false);
parent.getCheck().getCheckUncheck().set(true);
} else {
parent.getCheck().getIndeterminate().set(false);
parent.getCheck().getCheckUncheck().set(false);
}
}
this.param.getTreeTableView().refresh();
}
private void updateDownwards() {
List<A> children = selectedItem.getChildren();
if (selectedItem.isParent()
&& selectedItem.updateChildren) {
for (A child : children) {
child.getCheck().getCheckUncheck().set(selectedItem.getCheck().getCheckUncheck().get());
child.getCheck().getIndeterminate().set(selectedItem.getCheck().getIndeterminate().get());
}
}
this.param.getTreeTableView().refresh();
}
}
I'm stuck with trying to format Long values in a TableView with JavaFX.
I have following class to store the rows that I want to display on the table:
import java.text.DecimalFormat;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.beans.property.SimpleLongProperty;
import javafx.beans.property.SimpleStringProperty;
public class DataByCurrencyPairRow {
private DecimalFormat integerFormat = new DecimalFormat("#,###");
private SimpleStringProperty currencyPair = new SimpleStringProperty("");
private SimpleDoubleProperty shareOfTotalVolume = new SimpleDoubleProperty(0);
private SimpleLongProperty totalVolume = new SimpleLongProperty(0);
private SimpleLongProperty currencyBought = new SimpleLongProperty(0);
private SimpleLongProperty currencySold = new SimpleLongProperty(0);
private SimpleLongProperty monthlyAverage = new SimpleLongProperty(0);
public DataByCurrencyPairRow() {
currencyPair.set("");
shareOfTotalVolume.set(0);
totalVolume.set(0);
currencyBought.set(0);
currencySold.set(0);
monthlyAverage.set(0);
}
public String getCurrencyPair() {
return currencyPair.getValue();
}
public void setCurrencyPair(String currencyPair) {
this.currencyPair.setValue(currencyPair);
}
public Long getMonthlyAverage() {
return monthlyAverage.getValue();
}
public void setMonthlyAverage(Long monthlyAverage) {
this.monthlyAverage.setValue(monthlyAverage);
}
public Long getCurrencySold() {
return currencySold.getValue();
}
public void setCurrencySold(Long currencySold) {
this.currencySold.setValue(currencySold);
}
public Long getCurrencyBought() {
return currencyBought.getValue();
}
public void setCurrencyBought(Long currencyBought) {
this.currencyBought.setValue(currencyBought);
}
public Long getTotalVolume() {
return totalVolume.getValue();
}
public void setTotalVolume(Long totalVolume) {
this.totalVolume.setValue(totalVolume);
}
public Double getShareOfTotalVolume() {
return shareOfTotalVolume.getValue();
}
public void setShareOfTotalVolume(Double shareOfTotalVolume) {
this.shareOfTotalVolume.setValue(shareOfTotalVolume);
}
}
Then I have the controller with initialize method where I have been trying to override the updateItem method to get the table to show comma as a thousand separator:
public class MainController {
private static final String DEFAULT_TIME_HORIZON = new String("0");
private final NumberFormat integerFormat = new DecimalFormat("#,###");
#FXML
TableView<DataByCurrencyPairRow> tableTransactionsByCurrencyPair;
#FXML
TableColumn<DataByCurrencyPairRow, Long> columnTotal;
#FXML
void initialize() {
columnTotal.setCellFactory(
new Callback<TableColumn<DataByCurrencyPairRow, SimpleLongProperty>, TableCell<DataByCurrencyPairRow, SimpleLongProperty>>() {
#Override
public TableCell<DataByCurrencyPairRow, SimpleLongProperty> call(TableColumn<DataByCurrencyPairRow, SimpleLongProperty> param
) {
return new TableCell<DataByCurrencyPairRow, SimpleLongProperty>() {
#Override
protected void updateItem(SimpleLongProperty item, boolean empty) {
super.updateItem(item, empty);
if (item == null || empty) {
setText("0");
setStyle("");
} else {
setText(integerFormat.format(item.longValue()));
}
}
};
}
}
);
And this is the method that populates the TableView:
public void updateByCurrencyPairTable() {
System.out.println("#MainController: Updating data in table view Markets volumes by currency pair");
ObservableList<DataByCurrencyPairRow> data = tableTransactionsByCurrencyPair.getItems();
data.clear();
// Add row items to the table view Markets volume by currency
for (DataByCurrencyPairRow row : customer.getDataByCurrencyPairR12m().getDataByCurrencyPair()) {
data.add(row);
}
}
Please help me by showing how to do this!! I also tried to override the updateItem method as Long instead of SimpleLongProperty and my IDE accepted the code but still the number is not formatted in the table.
Thank you guys in advance!!!
LongProperty implements ObservableValue<Number>, not ObservableValue<Long> (or ObservableValue<SimpleLongProperty>). So your table columns need to be of type TableColumn<DataByCurrencyPair, Number> and your cell factory needs to match those types accordingly.
Here's a simple example of a formatted column with Longs:
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.util.Random;
import javafx.application.Application;
import javafx.beans.property.LongProperty;
import javafx.beans.property.SimpleLongProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.scene.Scene;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.stage.Stage;
public class TableWithFormattedLong extends Application {
private final NumberFormat integerFormat = new DecimalFormat("#,###");
#Override
public void start(Stage primaryStage) {
TableView<Item> table = new TableView<>();
TableColumn<Item, String> itemColumn = new TableColumn<>("Item");
itemColumn.setCellValueFactory(cellData -> cellData.getValue().nameProperty());
TableColumn<Item, Number> valueColumn = new TableColumn<>("Value");
valueColumn.setCellValueFactory(cellData -> cellData.getValue().valueProperty());
valueColumn.setCellFactory(tc -> new TableCell<Item, Number>() {
#Override
protected void updateItem(Number value, boolean empty) {
super.updateItem(value, empty);
if (value == null || empty) {
setText("");
} else {
setText(integerFormat.format(value));
}
}
});
table.getColumns().add(itemColumn);
table.getColumns().add(valueColumn);
Random rng = new Random();
for (int i = 1 ; i <= 20 ; i++) {
table.getItems().add(new Item("Item "+i, rng.nextLong()));
}
primaryStage.setScene(new Scene(table, 600, 600));
primaryStage.show();
}
public static class Item {
private final StringProperty name = new SimpleStringProperty();
private final LongProperty value = new SimpleLongProperty();
public Item(String name, long value) {
setName(name);
setValue(value);
}
public final StringProperty nameProperty() {
return this.name;
}
public final String getName() {
return this.nameProperty().get();
}
public final void setName(final String name) {
this.nameProperty().set(name);
}
public final LongProperty valueProperty() {
return this.value;
}
public final long getValue() {
return this.valueProperty().get();
}
public final void setValue(final long value) {
this.valueProperty().set(value);
}
}
public static void main(String[] args) {
launch(args);
}
}
There is no need to set the Cellfactory, just set the CellValueFactory.
TableColumn<DataByCurrencyPairRow, String> columnTotal;
columnTotal.setCellValueFactory(new Callback<TableColumn.CellDataFeatures<DataByCurrencyPairRow,String>, ObservableValue<String>>() {
#Override
public ObservableValue<String> call(CellDataFeatures<DataByCurrencyPairRow, String> param) {
DataByCurrencyPairRow value = param.getValue();
return new ReadOnlyStringWrapper(NumberFormat.getNumberInstance(Locale.US).format(123123)); //replace the number with the calculated total
}
});
I'm attempting to extend the Wicket Library Repeaters/Contacts Editor example so that I can save the row I'm editing, but it doesn't update after I've changed the values.
http://www.wicket-library.com/wicket-examples-6.0.x/repeater/wicket/bookmarkable/org.apache.wicket.examples.source.SourcesPage?SourcesPage_class=org.apache.wicket.examples.repeater.FormPage&source=FormPage.java
Debugging the code, it appears that
(User) ActionPanel.this.getDefaultModelObject()
does not get an updated Model Object, just the one that was originally used to populate the RefreshingView.
This is the code I have at the moment:
UserAdmin.java
package wicketsandbox.pages;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.ajax.markup.html.AjaxLink;
import org.apache.wicket.ajax.markup.html.form.AjaxSubmitLink;
import org.apache.wicket.markup.html.WebMarkupContainer;
import org.apache.wicket.markup.html.WebPage;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.form.CheckBox;
import org.apache.wicket.markup.html.form.Form;
import org.apache.wicket.markup.html.form.TextField;
import org.apache.wicket.markup.html.panel.FeedbackPanel;
import org.apache.wicket.markup.html.panel.Panel;
import org.apache.wicket.markup.repeater.Item;
import org.apache.wicket.markup.repeater.RefreshingView;
import org.apache.wicket.markup.repeater.util.ModelIteratorAdapter;
import org.apache.wicket.model.CompoundPropertyModel;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.PropertyModel;
import org.apache.wicket.protocol.http.WebApplication;
import wicketsandbox.AccessProtector;
import wicketsandbox.DetachableUserModel;
import wicketsandbox.User;
import wicketsandbox.WicketSandboxApplication;
import wicketsandbox.panels.SignOutPanel;
import wicketsandbox.services.UserService;
public class UserAdmin extends WebPage {
private static final long serialVersionUID = 1425344451953935626L;
private Form<User> form;
private final FeedbackPanel feedbackPanel;
private RefreshingView<User> contactsGrid;
private WebMarkupContainer updateableContactsForm;
public UserAdmin() {
new AccessProtector().userIsLoggedIn();
Map<UUID, User> users = getUserService().getUsers();
List<User> usersList = new ArrayList<User>();
usersList.addAll(users.values());
addContactsGridForm(usersList);
this.add(new SignOutPanel("signOutPanel"));
feedbackPanel = new FeedbackPanel("feedbackPanel");
feedbackPanel.setOutputMarkupId(true);
this.add(feedbackPanel);
}
private UserService getUserService() {
return ((WicketSandboxApplication) WebApplication.get()).getUserService();
}
private void addContactsGridForm(final List<User> users) {
updateableContactsForm = new WebMarkupContainer("updateableContactsForm");
form = new Form<User>("contactsForm");
contactsGrid = new RefreshingView<User>("contactsGrid") {
private static final long serialVersionUID = 6885639412244652087L;
#Override
protected Iterator<IModel<User>> getItemModels() {
return new ModelIteratorAdapter<User>(users) {
#Override
protected IModel<User> model(User user) {
System.out.println("L81" + user.toString());
return new CompoundPropertyModel<User>(new DetachableUserModel(user));
}
};
}
#Override
protected void populateItem(Item<User> item) {
IModel<User> user = item.getModel();
System.out.println("L90" + user.toString());
item.add(new Label("id"));
item.add(new TextField<String>("name", new PropertyModel<String>(user, "name")));
item.add(new TextField<String>("username", new PropertyModel<String>(user, "username")));
item.add(new TextField<String>("role", new PropertyModel<String>(user, "role")));
item.add(new CheckBox("deleted", new PropertyModel<Boolean>(user, "deleted")));
item.add(new ActionPanel("actions", user));
}
};
updateableContactsForm.setOutputMarkupId(true);
form.add(contactsGrid);
updateableContactsForm.add(form);
this.add(updateableContactsForm);
}
private class ActionPanel extends Panel {
private static final long serialVersionUID = -2270412980024700449L;
public ActionPanel(String id, final IModel<User> user) {
super(id, user);
System.out.println("L111" + user.toString());
add(new AjaxLink<String>("save") {
private static final long serialVersionUID = -1678664978510212524L;
#Override
public void onClick(AjaxRequestTarget target) {
getUserService().updateUser((User) ActionPanel.this.getDefaultModelObject());
System.out.println("L120" + user.toString());
info("User Saved: " + user.getObject().getName().toString());
target.add(feedbackPanel);
}
});
add(new AjaxLink<String>("select") {
private static final long serialVersionUID = 4555384514269762524L;
#Override
public void onClick(AjaxRequestTarget target) {
System.out.println("L132" + user.toString());
info("User Information: " + user.getObject().toString());
target.add(feedbackPanel);
}
});
AjaxSubmitLink removeLink = new AjaxSubmitLink("remove", form) {
private static final long serialVersionUID = 3030918214811303564L;
#Override
protected void onSubmit(AjaxRequestTarget target, Form<?> form) {
User user = (User) ActionPanel.this.getDefaultModelObject();
System.out.println("L145" + user.toString());
getUserService().removeUser(user);
info("Removed user " + user);
target.add(feedbackPanel);
target.add(updateableContactsForm);
}
};
removeLink.setDefaultFormProcessing(false);
add(removeLink);
}
}
}
DetachableUserModel.java
package wicketsandbox;
import java.util.Map;
import java.util.UUID;
import org.apache.wicket.model.LoadableDetachableModel;
import org.apache.wicket.protocol.http.WebApplication;
public class DetachableUserModel extends LoadableDetachableModel<User> {
private static final long serialVersionUID = 2518759982972547608L;
private final UUID id;
protected Map<UUID, User> getContacts() {
return ((WicketSandboxApplication) WebApplication.get()).getUserService().getUsers();
}
public DetachableUserModel(User u) {
this(u.getId());
}
public DetachableUserModel(UUID id) {
if (id == null) {
throw new IllegalArgumentException();
}
this.id = id;
}
#Override
public int hashCode() {
return id.hashCode();
}
/**
* used for dataview with ReuseIfModelsEqualStrategy item reuse strategy
*
* #see org.apache.wicket.markup.repeater.ReuseIfModelsEqualStrategy
* #see java.lang.Object#equals(java.lang.Object)
*/
#Override
public boolean equals(final Object obj) {
if (obj == this) {
return true;
} else if (obj == null) {
return false;
} else if (obj instanceof DetachableUserModel) {
DetachableUserModel other = (DetachableUserModel) obj;
return other.id == id;
}
return false;
}
/**
* #see org.apache.wicket.model.LoadableDetachableModel#load()
*/
#Override
protected User load() {
return ((WicketSandboxApplication) WebApplication.get()).getUserService().getUser(id);
}
}
I've managed to solve this by using an AjaxFormComponentUpdatingBehavior for each field that was editable.
contactsGrid = new RefreshingView<User>("contactsGrid") {
private static final long serialVersionUID = 6885639412244652087L;
#Override
protected Iterator<IModel<User>> getItemModels() {
return new ModelIteratorAdapter<User>(getUserService().getUsers().values()) {
#Override
protected IModel<User> model(User user) {
return new CompoundPropertyModel<User>(new DetachableUserModel(user));
}
};
}
#Override
protected void populateItem(final Item<User> item) {
final IModel<User> user = item.getModel();
item.add(new Label("id"));
item.add(new TextField<String>("name").add(new AjaxFormComponentUpdatingBehavior("onchange") {
#Override
protected void onUpdate(AjaxRequestTarget target) {
item.setDefaultModelObject(user);
}
}));
item.add(new TextField<String>("username").add(new AjaxFormComponentUpdatingBehavior("onchange") {
#Override
protected void onUpdate(AjaxRequestTarget target) {
item.setDefaultModelObject(user);
}
}));
item.add(new TextField<String>("role").add(new AjaxFormComponentUpdatingBehavior("onchange") {
#Override
protected void onUpdate(AjaxRequestTarget target) {
item.setDefaultModelObject(user);
}
}));
item.add(new CheckBox("deleted").add(new AjaxFormComponentUpdatingBehavior("onchange") {
#Override
protected void onUpdate(AjaxRequestTarget target) {
item.setDefaultModelObject(user);
}
}));
item.add(new ActionPanel("actions", user));
}
};
Your page is loading the users from the service once only:
Map<UUID, User> users = getUserService().getUsers();
List<User> usersList = new ArrayList<User>();
usersList.addAll(users.values());
addContactsGridForm(usersList);
You should get an up-to-date list from your service for each call to #getItemModels().
I have TreeTableView with 2 columns, so I want to be able something like that:
user double click in cell -> Someclass.getType() returns type of editing field ->in cell I see this type of editing field(TextField or ChoiceBox)
wnen I need to use TextField only, i can use someshing like that
TreeColumn1.setCellFactory(TextFieldTreeTableCell.forTreeTableColumn());
TreeColumn1.setOnEditCommit(firstColumnCommitHandler);
commitHandler:
private EventHandler<TreeTableColumn.CellEditEvent<SomeClass, String>> firstColumnCommitHandler = event -> {
final SomeClass item = event.getRowValue().getValue();
item.setVariable(event.getNewValue());
};
but i need different types, and have no idea howto do this
For this you need to implement the table cell yourself, and display the appropriate components when you go in and out of editing state. Here's a basic idea. The ChoiceBoxs look odd, you may need to work with some CSS to get them looking correct. In this example, if the box in the first column is checked, the second column will use a ChoiceBox for editing; otherwise it will use a TextField.
import java.util.function.Function;
import java.util.stream.IntStream;
import javafx.application.Application;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.value.ObservableValue;
import javafx.scene.Scene;
import javafx.scene.control.ChoiceBox;
import javafx.scene.control.ContentDisplay;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
import javafx.scene.control.cell.CheckBoxTableCell;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
public class TableWithVaryingEditor extends Application {
#Override
public void start(Stage primaryStage) {
TableView<Item> table = new TableView<>();
table.setEditable(true);
IntStream.rangeClosed(1, 20).mapToObj(i -> new Item("Item "+i)).forEach(table.getItems()::add);
TableColumn<Item, Boolean> fixedCol = column("Fixed", Item::fixedProperty);
table.getColumns().add(fixedCol);
fixedCol.setCellFactory(CheckBoxTableCell.forTableColumn(fixedCol));
TableColumn<Item, String> nameCol = column("Name", Item::nameProperty);
table.getColumns().add(nameCol);
nameCol.setCellFactory(col -> new TableCell<Item, String>() {
private TextField textField = new TextField();
private ChoiceBox<String> choice = new ChoiceBox<>();
private boolean ignoreChoiceBoxChange = false ;
// anonymous constructor:
{
choice.valueProperty().addListener((obs, oldValue, newValue) -> {
if (! ignoreChoiceBoxChange) {
commitEdit(newValue);
}
});
choice.focusedProperty().addListener((obs, wasFocused, isNowFocused) -> {
if (! isNowFocused) {
cancelEdit();
}
});
choice.showingProperty().addListener((obs, wasShowing, isNowShowing) -> {
if (! isNowShowing) {
cancelEdit();
}
});
textField.setOnAction(e -> commitEdit(textField.getText()));
textField.focusedProperty().addListener((obs, wasFocused, isNowFocused) -> {
if (! isNowFocused) {
cancelEdit();
}
});
}
#Override
public void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (isEditing()) {
updateEditor();
} else {
updateText();
}
}
#Override
public void startEdit() {
super.startEdit();
updateEditor();
}
#Override
public void cancelEdit() {
super.cancelEdit();
updateText();
}
#Override
public void commitEdit(String item) {
super.commitEdit(item);
updateText();
}
private void updateEditor() {
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
int index = getIndex();
Item item = getTableView().getItems().get(index);
if (item.isFixed()) {
ignoreChoiceBoxChange = true ;
choice.getItems().setAll(getItem(), "Choice 1", "Choice 2");
choice.getSelectionModel().select(getItem());
setGraphic(choice);
choice.show();
ignoreChoiceBoxChange = false ;
} else {
textField.setText(getItem());
setGraphic(textField);
}
}
private void updateText() {
setContentDisplay(ContentDisplay.TEXT_ONLY);
if (isEmpty()) {
setText(null);
} else {
setText(getItem());
}
}
});
primaryStage.setScene(new Scene(new BorderPane(table), 600, 400));
primaryStage.show();
}
private <S,T> TableColumn<S,T> column(String title, Function<S, ObservableValue<T>> property) {
TableColumn<S,T> col = new TableColumn<>(title);
col.setCellValueFactory(cellData -> property.apply(cellData.getValue()));
return col ;
}
public static class Item {
private final BooleanProperty fixed = new SimpleBooleanProperty();
private final StringProperty name = new SimpleStringProperty();
public Item(String name) {
setName(name);
}
public final BooleanProperty fixedProperty() {
return this.fixed;
}
public final boolean isFixed() {
return this.fixedProperty().get();
}
public final void setFixed(final boolean fixed) {
this.fixedProperty().set(fixed);
}
public final StringProperty nameProperty() {
return this.name;
}
public final String getName() {
return this.nameProperty().get();
}
public final void setName(final String name) {
this.nameProperty().set(name);
}
}
public static void main(String[] args) {
launch(args);
}
}
Using this example I want to create TreeView with Nodes that I can drag in order to change their position.
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.TreeCell;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeItem.TreeModificationEvent;
import javafx.scene.control.TreeView;
import javafx.scene.input.ClipboardContent;
import javafx.scene.input.DataFormat;
import javafx.scene.input.DragEvent;
import javafx.scene.input.Dragboard;
import javafx.scene.input.MouseEvent;
import javafx.scene.input.TransferMode;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.Callback;
public class MainApp extends Application {
private EventHandler<TreeModificationEvent<DynamicTreeNodeModel>> branchExpandedEventHandler;
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
Application.launch(MainApp.class, args);
}
#Override
public void start(Stage primaryStage) {
primaryStage.setTitle("Example Dynamic Tree");
primaryStage.setResizable(true);
final VBox box = new VBox();
box.setFillWidth(false);
Scene scene = new Scene(box);
primaryStage.setScene(scene);
box.getChildren().add(this.getExampleTree());
primaryStage.show();
}
private TreeView<DynamicTreeNodeModel> getExampleTree() {
DynamicTreeNodeModel rootNode = new RandomDynamicTreeNodeModel(null, "Root Node");
TreeView<DynamicTreeNodeModel> treeView = new TreeView<DynamicTreeNodeModel>();
treeView.setCellFactory(new Callback<TreeView<DynamicTreeNodeModel>, TreeCell<DynamicTreeNodeModel>>() {
#Override
public TreeCell call(TreeView<DynamicTreeNodeModel> param) {
return new DnDCell(param);
}
});
treeView.setPrefSize(1000, 750);
TreeItem rootItem = new TreeItem(rootNode);
branchExpandedEventHandler = new EventHandler<TreeModificationEvent<DynamicTreeNodeModel>>() {
public void handle(TreeModificationEvent<DynamicTreeNodeModel> event) {
// System.out.println("handling event " + event);
TreeItem<DynamicTreeNodeModel> item = event.getTreeItem();
populateTreeItem(item);
}
};
rootItem.addEventHandler(TreeItem.branchExpandedEvent(), branchExpandedEventHandler);
treeView.setShowRoot(true);
treeView.setRoot(rootItem);
populateTreeItem(rootItem);
rootItem.setExpanded(true);
// treeView.setCellFactory(new LearningTreeCellFactory());
return treeView;
}
private void populateTreeItem(TreeItem<DynamicTreeNodeModel> item) {
DynamicTreeNodeModel node = item.getValue();
boolean isPopulated = node.isPopulated();
boolean areGrandChildrenPopulated = node.areChildenPopulated();
node.populateToDepth(2);
if (!isPopulated) {
for (DynamicTreeNodeModel childNode : node.getChildren()) {
TreeItem childItem = new TreeItem(childNode);
childItem.addEventHandler(TreeItem.branchExpandedEvent(), branchExpandedEventHandler);
item.getChildren().add(childItem);
}
}
if (!areGrandChildrenPopulated) {
int i = 0;
for (TreeItem childItem : item.getChildren()) {
// get cooresponding node in the model
DynamicTreeNodeModel childNode = node.getChildren().get(i);
i++;
for (DynamicTreeNodeModel grandChildNode : childNode.getChildren()) {
TreeItem grandChildItem = new TreeItem(grandChildNode);
grandChildItem.addEventHandler(TreeItem.branchExpandedEvent(), branchExpandedEventHandler);
childItem.getChildren().add(grandChildItem);
}
}
}
}
private static interface DynamicTreeNodeModel {
public String getName();
public void setName(String name);
public boolean isPopulated();
public boolean areChildenPopulated();
public List<DynamicTreeNodeModel> getChildren();
public void setChildren(List<DynamicTreeNodeModel> children);
public DynamicTreeNodeModel getParent();
public void setParent(DynamicTreeNodeModel parent);
public void populateToDepth(int depth);
#Override
public String toString();
}
private static class RandomDynamicTreeNodeModel implements DynamicTreeNodeModel {
private DynamicTreeNodeModel parent;
private String name;
private List<DynamicTreeNodeModel> children = null;
public RandomDynamicTreeNodeModel(DynamicTreeNodeModel parent, String name) {
this.parent = parent;
this.name = name;
}
#Override
public String getName() {
return name;
}
#Override
public void setName(String name) {
this.name = name;
}
#Override
public boolean isPopulated() {
if (children == null) {
return false;
}
return true;
}
#Override
public boolean areChildenPopulated() {
if (!this.isPopulated()) {
return false;
}
for (DynamicTreeNodeModel child : this.children) {
if (!child.isPopulated()) {
return false;
}
}
return true;
}
#Override
public List<DynamicTreeNodeModel> getChildren() {
return children;
}
#Override
public void setChildren(List<DynamicTreeNodeModel> children) {
this.children = children;
}
#Override
public DynamicTreeNodeModel getParent() {
return parent;
}
#Override
public void setParent(DynamicTreeNodeModel parent) {
this.parent = parent;
}
private static Random random = new Random();
#Override
public void populateToDepth(int depth) {
if (depth <= 0) {
return;
}
if (children == null) {
int num = random.nextInt(5);
System.out.println("got a random number " + num);
children = new ArrayList(num);
for (int i = 0; i < num; i++) {
children.add(new RandomDynamicTreeNodeModel(this, "child " + i));
}
}
int childDepth = depth - 1;
for (DynamicTreeNodeModel child : children) {
child.populateToDepth(childDepth);
}
}
#Override
public String toString() {
return this.name;
}
}
public class DnDCell extends TreeCell<DynamicTreeNodeModel> {
private TreeView<DynamicTreeNodeModel> parentTree;
public DnDCell(final TreeView<DynamicTreeNodeModel> parentTree) {
this.parentTree = parentTree;
// ON SOURCE NODE.
setOnDragDetected(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
System.out.println("Drag detected on " + item);
if (item == null) {
return;
}
Dragboard dragBoard = startDragAndDrop(TransferMode.MOVE);
ClipboardContent content = new ClipboardContent();
content.put(DataFormat.PLAIN_TEXT, item.toString());
dragBoard.setContent(content);
event.consume();
}
});
setOnDragDone(new EventHandler<DragEvent>() {
#Override
public void handle(DragEvent dragEvent) {
System.out.println("Drag done on " + item);
dragEvent.consume();
}
});
// ON TARGET NODE.
// setOnDragEntered(new EventHandler<DragEvent>() {
// #Override
// public void handle(DragEvent dragEvent) {
// System.out.println("Drag entered on " + item);
// dragEvent.consume();
// }
// });
setOnDragOver(new EventHandler<DragEvent>() {
#Override
public void handle(DragEvent dragEvent) {
System.out.println("Drag over on " + item);
if (dragEvent.getDragboard().hasString()) {
int valueToMove = Integer.parseInt(dragEvent.getDragboard().getString());
if (valueToMove != item) {
// We accept the transfer!!!!!
dragEvent.acceptTransferModes(TransferMode.MOVE);
}
}
dragEvent.consume();
}
});
// setOnDragExited(new EventHandler<DragEvent>() {
// #Override
// public void handle(DragEvent dragEvent) {
// System.out.println("Drag exited on " + item);
// dragEvent.consume();
// }
// });
setOnDragDropped(new EventHandler<DragEvent>() {
#Override
public void handle(DragEvent dragEvent) {
System.out.println("Drag dropped on " + item);
int valueToMove = Integer.parseInt(dragEvent.getDragboard().getString());
TreeItem<DynamicTreeNodeModel> itemToMove = search(parentTree.getRoot(), valueToMove);
TreeItem<DynamicTreeNodeModel> newParent = search(parentTree.getRoot(), item);
// Remove from former parent.
itemToMove.getParent().getChildren().remove(itemToMove);
// Add to new parent.
newParent.getChildren().add(itemToMove);
newParent.setExpanded(true);
dragEvent.consume();
}
});
}
private TreeItem<DynamicTreeNodeModel> search(final TreeItem<DynamicTreeNodeModel> currentNode, final int valueToSearch) {
TreeItem<DynamicTreeNodeModel> result = null;
if (currentNode.getValue() == valueToSearch) {
result = currentNode;
} else if (!currentNode.isLeaf()) {
for (TreeItem<DynamicTreeNodeModel> child : currentNode.getChildren()) {
result = search(child, valueToSearch);
if (result != null) {
break;
}
}
}
return result;
}
private DynamicTreeNodeModel item;
#Override
protected void updateItem(DynamicTreeNodeModel item, boolean empty) {
super.updateItem(item, empty);
this.item = item;
String text = (item == null) ? null : item.toString();
setText(text);
}
}
}
I tried to implement the example but I'm facing a problem here:
#Controller
public class FileUploadController {
if (valueToMove != item) {
// We accept the transfer!!!!!
dragEvent.acceptTransferModes(TransferMode.MOVE);
}
...............
if (currentNode.getValue() == valueToSearch) {
I'm getting incomparable types: DynamicTreeNodeModel and int
Can you help me to fix this issue?
please try this:
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package test;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.logging.Level;
import java.util.logging.Logger;
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.TreeCell;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeItem.TreeModificationEvent;
import javafx.scene.control.TreeView;
import javafx.scene.input.ClipboardContent;
import javafx.scene.input.DataFormat;
import javafx.scene.input.DragEvent;
import javafx.scene.input.Dragboard;
import javafx.scene.input.MouseEvent;
import javafx.scene.input.TransferMode;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.Callback;
public class MainApp extends Application {
private EventHandler<TreeModificationEvent<DynamicTreeNodeModel>> branchExpandedEventHandler;
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
Application.launch(MainApp.class, args);
}
#Override
public void start(Stage primaryStage) {
primaryStage.setTitle("Example Dynamic Tree");
primaryStage.setResizable(true);
final VBox box = new VBox();
box.setFillWidth(false);
Scene scene = new Scene(box);
primaryStage.setScene(scene);
box.getChildren().add(this.getExampleTree());
primaryStage.show();
}
private TreeView<DynamicTreeNodeModel> getExampleTree() {
DynamicTreeNodeModel rootNode = new RandomDynamicTreeNodeModel(null, "Root Node");
TreeView<DynamicTreeNodeModel> treeView = new TreeView<DynamicTreeNodeModel>();
treeView.setCellFactory(new Callback<TreeView<DynamicTreeNodeModel>, TreeCell<DynamicTreeNodeModel>>() {
#Override
public TreeCell call(TreeView<DynamicTreeNodeModel> param) {
return new DnDCell(param);
}
});
treeView.setPrefSize(1000, 750);
TreeItem rootItem = new TreeItem(rootNode);
branchExpandedEventHandler = new EventHandler<TreeModificationEvent<DynamicTreeNodeModel>>() {
public void handle(TreeModificationEvent<DynamicTreeNodeModel> event) {
// System.out.println("handling event " + event);
TreeItem<DynamicTreeNodeModel> item = event.getTreeItem();
populateTreeItem(item);
}
};
rootItem.addEventHandler(TreeItem.branchExpandedEvent(), branchExpandedEventHandler);
treeView.setShowRoot(true);
treeView.setRoot(rootItem);
populateTreeItem(rootItem);
rootItem.setExpanded(true);
// treeView.setCellFactory(new LearningTreeCellFactory());
return treeView;
}
private void populateTreeItem(TreeItem<DynamicTreeNodeModel> item) {
DynamicTreeNodeModel node = item.getValue();
boolean isPopulated = node.isPopulated();
boolean areGrandChildrenPopulated = node.areChildenPopulated();
node.populateToDepth(2);
if (!isPopulated) {
for (DynamicTreeNodeModel childNode : node.getChildren()) {
TreeItem childItem = new TreeItem(childNode);
childItem.addEventHandler(TreeItem.branchExpandedEvent(), branchExpandedEventHandler);
item.getChildren().add(childItem);
}
}
if (!areGrandChildrenPopulated) {
int i = 0;
int size = node.getChildren().size();
for (TreeItem childItem : item.getChildren()) {
// get cooresponding node in the model
if (i < size) {
DynamicTreeNodeModel childNode = node.getChildren().get(i);
i++;
for (DynamicTreeNodeModel grandChildNode : childNode.getChildren()) {
TreeItem grandChildItem = new TreeItem(grandChildNode);
grandChildItem.addEventHandler(TreeItem.branchExpandedEvent(), branchExpandedEventHandler);
childItem.getChildren().add(grandChildItem);
}
}
}
}
}
private static interface DynamicTreeNodeModel {
public String getName();
public void setName(String name);
public boolean isPopulated();
public boolean areChildenPopulated();
public List<DynamicTreeNodeModel> getChildren();
public void setChildren(List<DynamicTreeNodeModel> children);
public DynamicTreeNodeModel getParent();
public void setParent(DynamicTreeNodeModel parent);
public void populateToDepth(int depth);
#Override
public String toString();
}
private static class RandomDynamicTreeNodeModel implements DynamicTreeNodeModel {
private DynamicTreeNodeModel parent;
private String name;
private List<DynamicTreeNodeModel> children = null;
public RandomDynamicTreeNodeModel(DynamicTreeNodeModel parent, String name) {
this.parent = parent;
this.name = name;
}
#Override
public String getName() {
return name;
}
#Override
public void setName(String name) {
this.name = name;
}
#Override
public boolean isPopulated() {
if (children == null) {
return false;
}
return true;
}
#Override
public boolean areChildenPopulated() {
if (!this.isPopulated()) {
return false;
}
for (DynamicTreeNodeModel child : this.children) {
if (!child.isPopulated()) {
return false;
}
}
return true;
}
#Override
public List<DynamicTreeNodeModel> getChildren() {
return children;
}
#Override
public void setChildren(List<DynamicTreeNodeModel> children) {
this.children = children;
}
#Override
public DynamicTreeNodeModel getParent() {
return parent;
}
#Override
public void setParent(DynamicTreeNodeModel parent) {
this.parent = parent;
}
private static Random random = new Random();
#Override
public void populateToDepth(int depth) {
if (depth <= 0) {
return;
}
if (children == null) {
int num = random.nextInt(5);
System.out.println("got a random number " + num);
children = new ArrayList(num);
for (int i = 0; i < num; i++) {
// children.add(new RandomDynamicTreeNodeModel(this, "child " + i));
children.add(new RandomDynamicTreeNodeModel(this, "child " + System.currentTimeMillis()));
try {
Thread.sleep(100);
} catch (InterruptedException ex) {
Logger.getLogger(MainApp.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
int childDepth = depth - 1;
for (DynamicTreeNodeModel child : children) {
child.populateToDepth(childDepth);
}
}
#Override
public String toString() {
return this.name;
}
}
public class DnDCell extends TreeCell<DynamicTreeNodeModel> {
private TreeView<DynamicTreeNodeModel> parentTree;
public DnDCell(final TreeView<DynamicTreeNodeModel> parentTree) {
this.parentTree = parentTree;
// ON SOURCE NODE.
setOnDragDetected(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
System.out.println("Drag detected on " + item);
if (item == null) {
return;
}
Dragboard dragBoard = startDragAndDrop(TransferMode.MOVE);
ClipboardContent content = new ClipboardContent();
content.put(DataFormat.PLAIN_TEXT, item.toString());
dragBoard.setContent(content);
event.consume();
}
});
setOnDragDone(new EventHandler<DragEvent>() {
#Override
public void handle(DragEvent dragEvent) {
System.out.println("Drag done on " + item);
dragEvent.consume();
}
});
// ON TARGET NODE.
// setOnDragEntered(new EventHandler<DragEvent>() {
// #Override
// public void handle(DragEvent dragEvent) {
// System.out.println("Drag entered on " + item);
// dragEvent.consume();
// }
// });
setOnDragOver(new EventHandler<DragEvent>() {
#Override
public void handle(DragEvent dragEvent) {
System.out.println("Drag over on " + item);
if (dragEvent.getDragboard().hasString()) {
String valueToMove = dragEvent.getDragboard().getString();
if (!valueToMove.matches(item.getName())) {
// We accept the transfer!!!!!
dragEvent.acceptTransferModes(TransferMode.MOVE);
}
}
dragEvent.consume();
}
});
// setOnDragExited(new EventHandler<DragEvent>() {
// #Override
// public void handle(DragEvent dragEvent) {
// System.out.println("Drag exited on " + item);
// dragEvent.consume();
// }
// });
setOnDragDropped(new EventHandler<DragEvent>() {
#Override
public void handle(DragEvent dragEvent) {
System.out.println("Drag dropped on " + item);
String valueToMove = dragEvent.getDragboard().getString();
TreeItem<DynamicTreeNodeModel> itemToMove = search(parentTree.getRoot(), valueToMove);
TreeItem<DynamicTreeNodeModel> newParent = search(parentTree.getRoot(), item.getName());
// Remove from former parent.
itemToMove.getParent().getChildren().remove(itemToMove);
// Add to new parent.
newParent.getChildren().add(itemToMove);
newParent.setExpanded(true);
dragEvent.consume();
}
});
}
private TreeItem<DynamicTreeNodeModel> search(final TreeItem<DynamicTreeNodeModel> currentNode, final String valueToSearch) {
TreeItem<DynamicTreeNodeModel> result = null;
if (currentNode.getValue().getName().matches(valueToSearch)) {
result = currentNode;
} else if (!currentNode.isLeaf()) {
for (TreeItem<DynamicTreeNodeModel> child : currentNode.getChildren()) {
result = search(child, valueToSearch);
if (result != null) {
break;
}
}
}
return result;
}
private DynamicTreeNodeModel item;
#Override
protected void updateItem(DynamicTreeNodeModel item, boolean empty) {
super.updateItem(item, empty);
this.item = item;
String text = (item == null) ? null : item.toString();
setText(text);
}
}
}
In here, I compare the names (same as Ithachi's opinion) and did some tweaks (check your populateTreeItem(..) function and modify it to suit your need - if currently it doesn't)..
to avoid duplicate names, I used system.currentTimemillis as part of the node's name..
hope it helps..