I'm using GXT 3.0 and I want to develop a grid table in it. In table, a cell assigned to be have multiple jobs, like save, delete, update. So I need to develop a grid table which has multiple buttons in a cell. To visualize the problem I'm sharing this image :
I tried to add just a cell via
ColumnConfig.setCell()
method, and It's succeeded. But I must add multiple buttons, or cells to handle events. In short form I need multiple Cells inside a Cell.
I know there is a method called ColumnConfig.setWidget(), but it didn't helped. It just added toolbar(or any widget element) to top(header part).
Remember that I use GXT 3.0
Thanks for any help.
You must use a CompositeCell :
private CompositeCell<ObjectRow> createCompositeCell(){
HasCell<ObjectRow, String> button1 = new HasCell<ObjectRow, String>() {
public Cell<String> getCell() {
return new ButtonCell();
}
public FieldUpdater<ObjectRow, String> getFieldUpdater() {
return null;
}
public String getValue(ObjectRow object) {
return "Button 1";
}};
HasCell<ObjectRow, String> button2 = new HasCell<ObjectRow,String>(){
public Cell<String> getCell() {
return new ButtonCell();
}
public FieldUpdater<ObjectRow, String> getFieldUpdater() {
return null;
}
public String getValue(ObjectRow object) {
return "Button 2";
}
};
List<HasCell<ObjectRow, ?>> cells = new ArrayList<HasCell<ObjectRow, ?>>();
cells.add(buton1);
cells.add(button2);
CompositeCell<ObjectRow> compositeCell = new CompositeCell<ObjectRow>(cells);
return compositeCell;
}
You can set a different FieldUpdater for handle button click.
Related
i have a data model "Rule"
A Rule consists of 1-x String parts saved as a List and an boolean value weather the rule is active or not.
To show this in my UI i want to add a TableView with 2 Columns.
Column 1 should display the Rule Text as a whole, but heavily customized. In the cell i add a textfield for each rule part which then get binded to the StrinProperty (Thats why i need a List of String Properties.
The 2. column should display a checkbox to activate or deactivate the rule (this is no problem an works fine)
Before my rule Model had the boolean isActive flag i used a Listview which had the whole Rule model class as Object. I made my own ListCell implementation and overrode updateItem(Object item, boolean isEmpty) to customize the cell to look like this:
I want the tablecell in column 1 to look exactly how the listcell in my listview looked.
Because ListCell and Tablecell both inherit from IndexedCell i saw no problem in my way of changing the visual of the cell.
My problem is to bind the new datamodel to the table:
private TableView<Rule> tvRules;
this.tvRules = new TableView<Rule>();
this.tvRules.setPrefSize(GuiCore.prefWidth * 0.32, GuiCore.prefHeight * 0.32);
this.tvRules.setEditable(true);
headerBoxLbl = new Label("Active");
headerBox = new CheckBox();
headerBoxLbl.setGraphic(headerBox);
headerBoxLbl.setContentDisplay(ContentDisplay.RIGHT);
headerBox.setOnAction(e -> this.changeAllActiveBoxes());
rulePartsColumn = new TableColumn<Rule, List<SimpleStringProperty>>("Rule");
rulePartsColumn.setCellFactory((callback) -> new RuleTableCell());
rulePartsColumn.setCellValueFactory(cellData -> cellData.getValue().getRulePartsProperty());
rulePartsColumn.setResizable(false);
rulePartsColumn.prefWidthProperty().bind(this.widthProperty().multiply(0.8));
isActiveColumn = new TableColumn<Rule, Boolean>();
isActiveColumn.setCellValueFactory(cellData -> cellData.getValue().getIsActiveProperty());
isActiveColumn.setCellFactory(cellData -> new CheckBoxTableCell<>());
isActiveColumn.setResizable(false);
isActiveColumn.prefWidthProperty().bind(this.widthProperty().multiply(0.2));
isActiveColumn.setStyle( "-fx-alignment: CENTER;");
isActiveColumn.setGraphic(headerBoxLbl);
this.tvRules.getColumns().addAll(rulePartsColumn, isActiveColumn);
As you see i create 2 Columns with the TableDataType Rule, one with Boolean type and one with the List as Data type.
The problem ist that i dont get the binding of the rulePartsColumn to the rule Model to work:
I really dont know how to bind this so in the cell i can work with a List of StringProperties (or SimpleStringProperties).
For reference my Model class Rule:
public class Rule {
private SimpleListProperty<SimpleStringProperty> ruleParts;
private SimpleBooleanProperty isActive;
public Rule() {
this(true, Arrays.asList("", "=", ""));
}
public Rule(final boolean isActive, final List<String> ruleParts) {
this.isActive = new SimpleBooleanProperty(isActive);
this.ruleParts = new SimpleListProperty<SimpleStringProperty>(FXCollections.observableArrayList());
for(int i = 0; i < ruleParts.size(); i++) {
this.ruleParts.add(new SimpleStringProperty(ruleParts.get(i)));
}
}
public SimpleListProperty<SimpleStringProperty> getRulePartsProperty() {
return this.ruleParts;
}
public List<SimpleStringProperty> getRulePartsProperties() {
return (List<SimpleStringProperty>)this.ruleParts;
}
public List<String> getRuleParts() {
List<String> parts = new ArrayList<String>();
for(int i = 0; i < this.ruleParts.size(); i++) {
parts.add(this.ruleParts.get(i).get());
}
return parts;
}
public SimpleBooleanProperty getIsActiveProperty() {
return this.isActive;
}
public boolean isActive() {
return isActive.get();
}
public void setActive(boolean isActive) {
this.isActive.set(isActive);
}
}
Thanks in advance
Basically, I wanted to know if I could create a tree and custom it on javaFX...
I tried to do it, but couldn't do anything so far with this code...
public class Main{
......
public Main() throws Exception{
......
// TreeView created
TreeView tv = (TreeView) fxmlLoader.getNamespace().get("treeview");
TreeItem<String> rootItem = new TreeItem<String>("liss");
rootItem.setExpanded(true);
tv.setRoot(rootItem);
/*for (int i = 1; i < 6; i++) {
TreeItem<String> item = new TreeItem<String> ("Message" + i);
rootItem.getChildren().add(item);
}
TreeItem<String> item = new TreeItem<String> ("MessageWoot");
rootItem.getChildren().add(item);
*/
//tv.setEditable(true);
tv.setCellFactory(new Callback<TreeView<String>, TreeCell<String>>() {
#Override
public TreeCell<String> call(TreeView<String> arg0) {
// custom tree cell that defines a context menu for the root tree item
return new MyTreeCell();
}
});
stage.show();
}
//
private static class MyTreeCell extends TextFieldTreeCell<String> {
private ContextMenu addMenu = new ContextMenu();
public boolean clickedFirstTime = false;
public MyTreeCell() {
// instantiate the root context menu
MenuItem addMenuItem = new MenuItem("Expand");
addMenu.getItems().add(addMenuItem);
addMenuItem.setOnAction(new EventHandler() {
public void handle(Event t) {
TreeItem n0 =
new TreeItem<String>("'program'");
TreeItem n1 =
new TreeItem<String>("<identifier>");
TreeItem n2 =
new TreeItem<String>("body");
getTreeItem().getChildren().add(n0);
getTreeItem().getChildren().add(n1);
getTreeItem().getChildren().add(n2);
}
});
}
#Override
public void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
// if the item is not empty and is a root...
//if (!empty && getTreeItem().getParent() == null && this.clickedFirstTime) {
System.out.println("UPDATEITEM -> clickedFirstTime : "+this.clickedFirstTime);
if (!this.clickedFirstTime) {
System.out.println("WOOT");
setContextMenu(addMenu);
this.clickedFirstTime = true;
}
}
}
And I'm questioning myself if this is the right "technology" which will solve what I'm trying to do...
What's my objective in this?
Firstly, I'm looking to add or delete a treeItem. I must say that a certain treeItem may be added only once or any N times, like a restriction (for example: treeItem < 6 for a certain level scope and a certain path of the root of tree view).
Secondly, make some treeItem editable and others not editable! When it is Editable, you may pop up something for the user in order to insert some input for example!
Is it possible ?
I saw the tutorial from https://docs.oracle.com/javafx/2/ui_controls/tree-view.htm#BABJGGGF but I'm really confused with this tutorial ... I don't really understand the cell factory mechanism... The fact that he does apply to TreeView when i want only a certain TreeItem... Or how could I control that effect/behaviour ?
I mean, I'm really really lost with TreeView. Probably, TreeView isn't what I'm looking for ...
P.S.: I know that I cannot apply any visual effect or add menus to a tree items and that i use a cell factory mechanism to overcome this obstacle. Just I don't understand the idea and how could I do it !
Sure this is the right "technology", if you want to use JavaFX. You should probably use a more complex type parameter for TreeItem however. You can use your a custom TreeCell to allow the desired user interaction.
This example allows adding children and removing nodes via context menu (unless the content is "nocontext") as well as editing the content (as long as the content is not "noedit"); on the root node the delete option is disabled:
tv.setEditable(true);
tv.setCellFactory(new Callback<TreeView<String>, TreeCell<String>>() {
private final MyContextMenu contextMenu = new MyContextMenu();
private final StringConverter converter = new DefaultStringConverter();
#Override
public TreeCell<String> call(TreeView<String> param) {
return new CustomTreeCell(contextMenu, converter);
}
});
public class CustomTreeCell extends TextFieldTreeCell<String> {
private final MyContextMenu contextMenu;
public CustomTreeCell(MyContextMenu contextMenu, StringConverter<String> converter) {
super(converter);
if (contextMenu == null) {
throw new NullPointerException();
}
this.contextMenu = contextMenu;
this.setOnContextMenuRequested(evt -> {
prepareContextMenu(getTreeItem());
evt.consume();
});
}
private void prepareContextMenu(TreeItem<String> item) {
MenuItem delete = contextMenu.getDelete();
boolean root = item.getParent() == null;
if (!root) {
delete.setOnAction(evt -> {
item.getParent().getChildren().remove(item);
contextMenu.freeActionListeners();
});
}
delete.setDisable(root);
contextMenu.getAdd().setOnAction(evt -> {
item.getChildren().add(new TreeItem<>("new item"));
contextMenu.freeActionListeners();
});
}
#Override
public void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (!empty) {
setContextMenu("nocontext".equals(item) ? null : contextMenu.getContextMenu());
setEditable(!"noedit".equals(item));
}
}
}
public class MyContextMenu {
private final ContextMenu contextMenu;
private final MenuItem add;
private final MenuItem delete;
public MyContextMenu() {
this.add = new MenuItem("add child");
this.delete = new MenuItem("delete");
this.contextMenu = new ContextMenu(add, delete);
}
public ContextMenu getContextMenu() {
return contextMenu;
}
public MenuItem getAdd() {
return add;
}
public MenuItem getDelete() {
return delete;
}
/**
* This method prevents memory leak by setting all actionListeners to null.
*/
public void freeActionListeners() {
this.add.setOnAction(null);
this.delete.setOnAction(null);
}
}
Of course more complex checks can be done in the updateItem and prepareContextMenu and different user interactions can be supported (TextFieldTreeCell may not be the appropriate superclass for you; You could use a "normal" TreeCell and show a different stage/dialog to edit the item when the user selects a MenuItem in the context menu).
Some clarification about cell factories
Cell factories are used to create the cells in a class that displays data (e.g. TableColumn, TreeView, ListView). When such a class needs to display content, it uses it's cell factory to create the Cells that it uses to display the data. The content displayed in such a cell may be changed (see updateItem method).
Example
(I'm not 100% sure this is exactly the way it's done, but it should be sufficiently close)
A TreeView is created to display a expanded root node with 2 non expanded children.
The TreeView determines that it needs to display 3 items for the root node and it's 2 children. The TreeView therefore uses it's cell factory to creates 3 cells and adds them to it's layout and assigns the displayed items.
Now the user expands the first child, which has 2 children of it's own. The TreeView determines that it needs 2 more cells to display the items. The new cells are added at the end of the layout for efficiency and the items of the cells are updated:
The cell that previously contained the last child is updated and now contains the first child of the first item.
The 2 newly added cells are updated to contain the second child of the first child and the second child of the root respecitvely.
So I decided to eliminate TreeView (because the documentation is so trash...) and, instead, I decided to implement a Webview !
Why ?
Like that, I could create an HTML document and use jstree (jquery plugin - https://www.jstree.com ) in there. It is a plugin which will create the treeview basically.
And the documentation is ten time better than treeview oracle documentation unfortunately.
Also, the possibility in creating/editing the tree with jstree is better.
Which concludes me that it was the best solution that I could figure out for me.
Also, whoever who will read me, I did a bridge with the webview and my javafx application ! It is a connection between my HTML document and the java application (Read more here - https://blogs.oracle.com/javafx/entry/communicating_between_javascript_and_javafx).
Hope It will help more people.
I have problem with adding of column to TableView dynamically. The problem is, that it adds data for one cell into entire row. I think the problem could be in my callback, or in the part where I add data in tableView.
This is my callback method:
Callback<CellDataFeatures<String,String> ,ObservableValue<String>> cb;
cb = new Callback<CellDataFeatures<String,String> ,ObservableValue<String>>() {
#Override
public ObservableValue<String> call(CellDataFeatures<String, String> param) {
return new SimpleStringProperty(param.getValue());
}
};
and this is where I add data, listOfNewValues is ArrayList which contains new String values:
ObservableList<String> addData = FXCollections.observableArrayList();
addData = aktualTable.getItems();
for(String data : listOfNewValues) {
addData.add(data);
}
aktualTable.setItems(addData);
So I have this piece of code which is almost exactly the same on the GWT Showcase
selectionModel = new SingleSelectionModel<T>(keyProvider);
cellTable.setSelectionModel(selectionModel);
selectionModel.addSelectionChangeHandler(new SelectionChangeEvent.Handler() {
public void onSelectionChange(SelectionChangeEvent event) {
selectedRow = ((SingleSelectionModel<T>).selectionModel)
.getSelectedObject();
});
Column<T, Boolean> checkColumn = new Column<T, Boolean>(new CheckboxCell(true, false)) {
#Override
public Boolean getValue(T object) {
return cellTable.getSelectionModel().isSelected(object);
}
};
cellTable.addColumn(checkColumn, SafeHtmlUtils.fromSafeConstant("<br/>"));
The problem is, when I uncheck the checkbox the SelectionChangeEvent doesn't handle it.
The only instance the onSelectionChange is being called, is when I select another record, but deselecting a record doesn't invoke this method.
any help?
You forgot to add DefaultSelectionEventManager i guess.
Change this line
cellTable.setSelectionModel(selectionModel);
to
cellTable.setSelectionModel(selectionModel,
DefaultSelectionEventManager.<T> createCheckboxManager());
I use the org.eclipse.core.databinding framework to bind some Text fields in an SWT application. I add an update strategy to validate the data and to set the value on the model only when the user click on the save button:
UpdateValueStrategy toModel = new UpdateValueStrategy(UpdateValueStrategy.POLICY_CONVERT);
if (validator != null) {
toModel.setAfterGetValidator(validator);
}
UpdateValueStrategy fromModel = new UpdateValueStrategy(UpdateValueStrategy.POLICY_UPDATE);
binding = bindingContext.bindValue(SWTObservables.observeText(this, SWT.Modify),
BeansObservables.observeValue(pVO, propertyName), toModel, fromModel);
This piece of code works really well.
But how can I do the same on a TableViewer?
I want it to work so that when I add something in the IHM, the model stay unchanged until I call getBindingContext().updateModels();
You do not need use the JFace Databinding Framework in TableViewer. Manipulation the structured data is simpler then SWT controls, such TableViewer, ListViewer and TreeViewer. You can use those viewer in the same way:
create viewer
set content provider
set label provider (suggested)
set filter (optional)
set sorter (optional)
After the viewer created, just invoke viewer.setInput(data) to put all the things to your viewer.
There are a list of model:
TableViewer tableViewer = new TableViewer(parent);
Table table = tableViewer.getTable();
table.setHeaderVisible(true);
table.setLinesVisible(true);`
for (int i = 0; i < COLUMN_NAMES.length; i++) {
TableColumn tableColumn = new TableColumn(table, SWT.LEFT);
tableColumn.setText(COLUMN_NAMES[i]);
tableColumn.setWidth(COLUMN_WIDTHS[i]);
}
tableViewer.setContentProvider(new ModelContentProvider());
tableViewer.setLabelProvider(new ModelLabelProvider());
tableViewer.setInput(models);
The magic happens in the content provider:
class ModelContentProvider implements IStructuredContentProvider {
#SuppressWarnings("unchecked")
#Override
public Object[] getElements(Object inputElement) {
// The inputElement comes from view.setInput()
if (inputElement instanceof List) {
List models = (List) inputElement;
return models.toArray();
}
return new Object[0];
}
/* ... other methods */
}
Each model will become a TableItem and the model in the TableItem(item.getData()).
However, a table composed by many columns, you need the LabelProvider to help you mapping the property of model to the TableItem:
class ModelLabelProvider extends LabelProvider implements
ITableLabelProvider {
#Override
public Image getColumnImage(Object element, int columnIndex) {
// no image to show
return null;
}
#Override
public String getColumnText(Object element, int columnIndex) {
// each element comes from the ContentProvider.getElements(Object)
if (!(element instanceof Model)) {
return "";
}
Model model = (Model) element;
switch (columnIndex) {
case 0:
return model.getFoo();
case 1:
return model.getBar();
default:
break;
}
return "";
}
}
The propagation of models to viewer is easy. If you will propagate viewer to the binded model, using the CellEditor is simple as well.
To use CellEditor, you need set the column properties, cell editors and cell modifier to TableViewer:
tableViewer.setColumnProperties(COLUMNS_PROPERTIES);
tableViewer.setCellEditors(new CellEditor[] {
new TextCellEditor(table), new TextCellEditor(table) });
tableViewer.setCellModifier(new ModelCellModifier(tableViewer));
The CellModifier likes this:
class ModelCellModifier implements ICellModifier {
TableViewer viewer;
public ModelCellModifier(TableViewer viewer) {
this.viewer = viewer;
}
#Override
public boolean canModify(Object element, String property) {
// property is defined by viewer.setColumnProperties()
// allow the FOO column can be modified.
return "foo_prop".equals(property);
}
#Override
public Object getValue(Object element, String property) {
if ("foo_prop".equals(property)) {
return ((Model) element).getFoo();
}
if ("bar_prop".equals(property)) {
return ((Model) element).getBar();
}
return "";
}
#Override
public void modify(Object element, String property, Object value) {
if ("foo_prop".equals(property)) {
TableItem item = (TableItem) element;
((Model) item.getData()).setFoo("" + value);
// refresh the viewer to show the changes to our user.
viewer.refresh();
}
}
}
Everything is simple but there are many steps to make all together.
Use ViewerSupport:
TableViewer tableViewer = ...
IObservableList tableElements = ...
IValueProperty[] columnProperties = ...
ViewerSupport.bind(tableViewer, tableElements, columnProperties);
i agree with qualidafial.
Snippet017TableViewerWithDerivedColumns from the jface.databinding snippets is a full example of this.