I am developing an RCP Application, and am using Nebula's NatTable for that.
When it comes to selection, I am failing to understand how I am supposed to use it.
What I want is:
I want to have entire Rows selected. I was able do that using the RowOnlySelectionConfiguration and the RowOnlySelectionBindings.
If I select a row, I want the selection to stay there and not be cleared when some data in that row gets updated. How do I do that?
If a row is selected, and the position of the element in that row changes (e.g. one of the previous elements is removed, and the position changes to index - 1), I want the selection to change the position with the element, so that the same element is selected after the change. How do I do that?
I have seen that the documentation talks about a PreserveSelectionModel that can be used for that:
If you used the PreserveSelectionStructuralChangeEventHandler workaround in previous versions for not clearing the selection on structural changes, you will notice that this workaround will not work anymore. If you still need that behavior, you are now able to achieve the same by configuring and setting a SelectionModel instance like this:
SelectionModel model = new SelectionModel(selectionLayer);
// configure to not clear the selection on structural changes
model.setClearSelectionOnChange(false);
selectionLayer.setSelectionModel(model);
If you expect that the selection should update and move with structural changes (e.g. sorting), try to use the PreserveSelectionModel.
https://www.eclipse.org/nattable/nandn/nandn_120.php
So I guess I have to use the PreserveSelectionModel? But there I can't call setClearSelectionOnChange(false). Does it do that by default?
And how do I use the PreserveSelectionModel? What do I pass in the constructor?
I implement my own BodyLayerStack, in a class called TableBodyLayerStack, where I tried this in the constructor:
public TableBodyLayerStack(IUniqueIndexLayer underlyingLayer) {
super(underlyingLayer);
columnReorderLayer = new ColumnReorderLayer(underlyingLayer);
columnHideShowLayer = new ColumnHideShowLayer(columnReorderLayer);
selectionLayer = new SelectionLayer(columnHideShowLayer, null, true, false);
PreserveSelectionModel<?> selectionModel = new PreserveSelectionModel<>(
selectionLayer, null, null);
selectionLayer.setSelectionModel(selectionModel);
selectionLayer.registerEventHandler(new SelectEventHandler(selectionLayer));
viewportLayer = new ViewportLayer(selectionLayer);
setUnderlyingLayer(viewportLayer);
registerCommandHandler(new CopyDataCommandHandler(selectionLayer));
}
Then, in the contructor of my implementation of the GridLayer, I do this:
// ...
bodyLayer = new TableBodyLayerStack(eventLayer);
// register different selection move command handler that always moves by row
bodyLayer.getSelectionLayer().addConfiguration(new RowOnlySelectionConfiguration<T>());
// register selection bindings that will perform row selections instead of cell selections
// registering the bindings on a layer that is above the SelectionLayer will consume the
// commands before they are handled by the SelectionLayer
bodyLayer.addConfiguration(new RowOnlySelectionBindings());
// ...
But this is giving me NullPointerExceptions in the PreserveSelectionModel.
Error while painting table: null
java.lang.NullPointerException
at org.eclipse.nebula.widgets.nattable.selection.preserve.PreserveSelectionModel.getRowPositionByRowObject(PreserveSelectionModel.java:520)
at org.eclipse.nebula.widgets.nattable.selection.preserve.PreserveSelectionModel.createMarkerPoint(PreserveSelectionModel.java:559)
at org.eclipse.nebula.widgets.nattable.selection.preserve.PreserveSelectionModel.getSelectionAnchor(PreserveSelectionModel.java:531)
at org.eclipse.nebula.widgets.nattable.selection.SelectionLayer.getSelectionAnchor(SelectionLayer.java:276)
at org.eclipse.nebula.widgets.nattable.selection.SelectionLayer.getConfigLabelsByPosition(SelectionLayer.java:415)
at org.eclipse.nebula.widgets.nattable.layer.AbstractLayerTransform.getConfigLabelsByPosition(AbstractLayerTransform.java:316)
at org.eclipse.nebula.widgets.nattable.layer.AbstractIndexLayerTransform.getConfigLabelsByPosition(AbstractIndexLayerTransform.java:318)
at org.eclipse.nebula.widgets.nattable.layer.CompositeLayer.getConfigLabelsByPosition(CompositeLayer.java:553)
at org.eclipse.nebula.widgets.nattable.layer.cell.AbstractLayerCell.getConfigLabels(AbstractLayerCell.java:48)
at org.eclipse.nebula.widgets.nattable.layer.AbstractLayer.getCellPainter(AbstractLayer.java:354)
at org.eclipse.nebula.widgets.nattable.layer.AbstractLayerTransform.getCellPainter(AbstractLayerTransform.java:336)
at org.eclipse.nebula.widgets.nattable.layer.AbstractLayerTransform.getCellPainter(AbstractLayerTransform.java:336)
at org.eclipse.nebula.widgets.nattable.layer.AbstractLayerTransform.getCellPainter(AbstractLayerTransform.java:336)
at org.eclipse.nebula.widgets.nattable.layer.AbstractIndexLayerTransform.getCellPainter(AbstractIndexLayerTransform.java:340)
at org.eclipse.nebula.widgets.nattable.layer.AbstractLayerTransform.getCellPainter(AbstractLayerTransform.java:336)
at org.eclipse.nebula.widgets.nattable.layer.AbstractIndexLayerTransform.getCellPainter(AbstractIndexLayerTransform.java:340)
at org.eclipse.nebula.widgets.nattable.layer.CompositeLayer.getCellPainter(CompositeLayer.java:586)
at org.eclipse.nebula.widgets.nattable.painter.layer.CellLayerPainter.paintCell(CellLayerPainter.java:171)
at org.eclipse.nebula.widgets.nattable.painter.layer.CellLayerPainter.paintLayer(CellLayerPainter.java:81)
at org.eclipse.nebula.widgets.nattable.painter.layer.GridLineCellLayerPainter.paintLayer(GridLineCellLayerPainter.java:106)
at org.eclipse.nebula.widgets.nattable.selection.SelectionLayerPainter.paintLayer(SelectionLayerPainter.java:95)
at org.eclipse.nebula.widgets.nattable.layer.CompositeLayer$CompositeLayerPainter.paintLayer(CompositeLayer.java:913)
at org.eclipse.nebula.widgets.nattable.painter.layer.NatLayerPainter.paintLayer(NatLayerPainter.java:43)
at org.eclipse.nebula.widgets.nattable.NatTable.paintNatTable(NatTable.java:408)
at org.eclipse.nebula.widgets.nattable.NatTable.paintControl(NatTable.java:403)
...
I guess it is because I pass null values in the constructor of my PreserveSelectionModel. But how do I use it instead? What do I have to pass as arguments for the constructor? Where do I get the values from?
Any help is appreciated.
you are on the wrong track to achieve your goals. First I will answer your questions:
So I guess I have to use the PreserveSelectionModel?
No, the PreserveSelectionModel is intended to preserve the selection for cell selections. You want to preserve the selection for whole rows. So you need to use the RowSelectionModel.
But there I can't call setClearSelectionOnChange(false). Does it do that by default?
Yes
But this is giving me NullPointerExceptions in the PreserveSelectionModel. I guess it is because I pass null values in the constructor of my PreserveSelectionModel.
Yes
What do I have to pass as arguments for the constructor? Where do I get the values from?
The second parameter is IRowDataProvider<T>, so it is the IDataProvider for the body.
The third parameter is IRowIdAccessor<T>. You need to create an implementation that provides an unique id, so a row can be identified without knowing the position or index in the underlying collection.
So what you need to do is something like this:
selectionLayer.setSelectionModel(new RowSelectionModel<Person>(
selectionLayer, bodyDataProvider, new IRowIdAccessor<Person>() {
#Override
public Serializable getRowId(Person rowObject) {
return rowObject.getId();
}
}));
But of course you need to provide the IDataProvider and also the IRowIdAccessor to your TableBodyLayerStack if you want to keep it generic.
Also note that you don't have to call SelectionLayer#registerEventHandler() yourself! This is done internally by calling SelectionLayer#setSelectionModel().
You can find several examples in the NatTable Examples Application at https://www.eclipse.org/nattable/ (the Try it! button on the right side).
For your question the Tutorial Examples -> Layers -> Selection -> RowSelectionExample seems to be the one to look at.
Related
Im pretty pretty new to Dynamic-Jasper, but due to work i had to add a new feature to our already implemented solution.
My Problem
The Goal is to add a Column to a report that consists only out of a background-color based on some Information. I managed to do that, but while testing I stumbled upon a Problem. While all my Columns in the html and pdf view had the right color, the Excel one only colored the fields in the last Color.
While debugging i noticed, that the same colored Fields had the same templateId, but while all Views run through mostly the same Code the Excel one showed different behavior and had the same ID in all fields.
My Code where I manipulate the template
for(JRPrintElement elemt : jasperPrint.getPages().get(0).getElements()) {
if(elemt instanceof JRTemplatePrintText) {
JRTemplatePrintText text = (JRTemplatePrintText) elemt;
(...)
if (text.getFullText().startsWith("COLOR_IDENTIFIER")) {
String marker = text.getFullText().substring(text.getFullText().indexOf('#') + 1);
text.setText("ID = " + ((JRTemplatePrintText) elemt).getTemplate().getId());
int rgb = TypeConverter.string2int(Integer.parseInt(marker, 16) + "", 0);
((JRTemplatePrintText) elemt).getTemplate().setBackcolor(new Color(rgb));
}
}
}
The html view
The Excel view
Temporary Conclusion
The same styles uses the same Objects in the background and the JR-Excel export messes something up by assigning the same Object to all the Fields that I manipulated there. If anyone knows of a misstake by me or potential Solutions to change something different to result the same thing please let me know.
Something different I tried earlier, was trying to set the field in an evaluate Method that was called by Jasper. In that method we assign the textvalue of each field. It contained a map with JRFillFields, but unfortunatelly the Map-Implementation denied access to them and just retuned the Value of those. The map was provided by dj and couldn't be switched with a different one.
Edit
We are using JasperReports 6.7.1
I found a Solution, where I replaced each template with a new one that was supposed to look exactly alike. That way every Field has its own ID guaranteed and its not up to chance, how JasperReports handles its Data internaly.
JRTemplateElement custom =
new JRTemplateText(((JRTemplatePrintText) elemt).getTemplate().getOrigin(),
((JRTemplatePrintText) elemt).getTemplate().getDefaultStyleProvider());
custom.setBackcolor(new Color(rgb));
custom.setStyle(((JRTemplatePrintText) elemt).getTemplate().getStyle());
((JRTemplatePrintText) elemt).setTemplate(custom);
I want to insert a new JavaFX bean in a grid using an "insert" button. Everything is fine, except for a display problem. After insertion, a "ghost selection" is displayed lower in the grid, as shown in this screenshot. In this example, a fourth section bean was added and selected as requested. But a fake selection appears 10 lines under the last real bean, where no bean is set for this row.
Has anyone experienced this kind of behavior? Any clue how get rid of this ghost selection? Here is what the code for the insert button looks like:
#FXML
private Button insert;
...
insert.setOnAction(event -> {
JfxBean newBean = createBean();
tableView.getItems().add(newBean);
int index = tableView.getItems().indexOf(newBean);
tableView.getSelectionModel().clearSelection();
tableView.requestFocus();
tableView.scrollTo(index);
tableView.getSelectionModel().focus(index);
tableView.getSelectionModel().select(index);
};
According to javaFX-8 documentation, SelectionModel.java does not expose any focus() method. FocusModel.java does instead. Therefore JVM will fail to compile your presented code.
Below is a possible solution:
insert.setOnAction(event -> {
JfxBean newBean = createBean();
tableView.getItems().add(newBean);
int index = tableView.getItems().indexOf(newBean);
tableView.getSelectionModel().clearSelection();
tableView.requestFocus();
tableView.scrollTo(index);
// below line is the amendment
tableView.getFocusModel().focus(index);
tableView.getSelectionModel().select(index);
};
Finally, adding tableView.refresh() corrected this weird behavior. No more ghost selection.
My issue is I have a simple List of Strings say
List<String> names = List.of("Frank","Joe","Eva");
All I want is display it on the UI. With some simple code like
ListComponent lc = new ListComponent.setItems(names);
I have tried it with Table which seems to work but code behind it is a bit boilerplate for this simple task(7-8 line of code).
I have tried also the Grid component and it works well when I want to bind a POJO to it , but with String.class type its a nightmare.
Grid<String> listGrid= new Grid<>(String.class) ;
listGrid.setItems(names);
it doesnt work because I have to provide getters for the column, which String.class doesnt have for the value. So I did this:
Grid<String> listGrid= new Grid<>(String.class) ;
listGrid.setItems(names);
listGrid.addColumn(String::toString).setCaption("name");
It works! However unspecified columns also appear in the grid, so now I have 3 columns Byte,Empty,name. And I dont know why. Where are these comes from?
What are the requirements for displaying them? Just to get them on the screen? Is Label enough?
for(String name: names) {
mylayout.addComponent(new Label(name))
}
If you need selection, then maybe ListSelect or ComboBox are the go-to’s.
If you want to avoid the additional columns, one way is to do as was pointed out in a comment, i.e. do removeAllColumns() before you go on creating your own columns.
Another approach would be to do new Grid<>() instead of new Grid<>(String.class). The main difference is that the second constructor uses reflection on the provided class and automatically configures columns for anything that looks like regular Java bean properties.
I would highly prefer to use grid.removeColumnByKey rather than removeAllColumns()
You can also use grid.setColumns to specify order of columns.
I will add link to vaadin documentantion for grid with java examples which is realy helpfull. enter link description here
I did something similar to adding the Strings in TextAreas. Because I needed some formatting, I added the text using StringBuilder.
List<String> details = getDetails();
StringBuilder builder = new StringBuilder();
for (String detail : details) {
TextArea ta = new TextArea();
ta.setSizeFull();
ta.setMaxHeight("100px");
ta.setValue(builder.append(detail).toString());
((Span) content).add(ta);
((Span) content).add(new Hr());
}
The result is like this:
Try out this
final Grid<String> grid = new Grid<>();
grid.setItems(new ArrayList<String>());
grid.addColumn(item -> item).setHeader("Value");
I have a ComboBox in my Vaadin 8 code.
allAtts here is a Set of Attendant-s, and the field theAtt in there is of type String:
private ComboBox<Attendant> theCB = new ComboBox<Attendant>(null,allAtts);
theCB.setEmptySelectionAllowed(false);
Binder<SearchArgs> binder = new Binder<SearchArgs>(SearchArgs.class);
binder.setBean(sas);
binder.forField(theCB).bind("theAtt");
..
VerticalLayout vl = new VerticalLayout(theCB, rb, deleteBtn);
What's more - this exact flow of theCB is a copy-paste from another class i wrote, on the very same type Attendant.
The problem here is the drop-down click on theCB is not working. That is, clicking that little chevron-down icon on the ComboBox is producing no effect. What could this be!?
I tried removing the other items, rb, deleteBtn in vl. Still didn't work.
Nothing else happening to theCB anywhere else.
TIA.
EDIT:
theCB is otherwise functional. typing in the field is allowed, brings the choices and returns the selection accurately.
EDIT-2
https://github.com/vaadin/vaadin-combo-box/issues/680 is not the issue. nothing changing when i pass a non-null label value to the constructor. Besides, it's working fine with null label in that other place in the code.
EDIT-3
Attendant contains several fields, one of which is String att.
SearchArgs composes Attendant in a its field theAtt. Attendant.toString() returns att only.
Once again - this exact same logic is working, as i'm typing these, in another part in the code.
Also note: i since also tried the following without success:
binding theCB on a separate binder, i.e. declare a second binder on sas and use that one for binding theCB
cloning the Attendant objects that go into theCB as data-provider.
I am trying to add blank row with null value in ComboBoxItem of smartgwt which is already bind to data source coming from database.
I have tried changes in service layer, controller layer and database too, but that was not recommended by my senior.
All I need to do changes on UI layer only.
Take a look at the Special Values ComboBox SmartGWT showcase demo. I think it does what you want.
Here is an extract taken from that demo's code (although I recommend you to look at it and study the code to see what's best in your case):
LinkedHashMap<String,String> hashMap = new LinkedHashMap<String,String>();
hashMap.put("**EmptyValue**", "None");
hashMap.put("-1", "Not Applicable");
ComboBoxItem comboBoxItem = new ComboBoxItem();
comboBoxItem.setName("filteredCombo");
comboBoxItem.setTitle("Choose an item (ComboBox)");
comboBoxItem.setAddUnknownValues(false);
comboBoxItem.setOptionDataSource(ItemSupplyXmlDS.getInstance());
comboBoxItem.setDisplayField("itemName");
comboBoxItem.setValueField("itemID");
comboBoxItem.setPickListWidth(300);
comboBoxItem.setPickListFields(skuField, itemNameField);
comboBoxItem.setSpecialValues(hashMap);
comboBoxItem.setSeparateSpecialValues(true);