SetCellValueFactory to data object in JavaFX TableColumn - java

I have a tablecolumn with custom cell render this cell render takes an object and renders its properties as Labels. The problem is that I can't find a way to pass the same object in the arraylist to a column. Here is my code:
//I want to render this object in a column as well as use it in the rest of columns
CustomerCreationFlow cflow=new CustomerCreationFlow();
cflow.setId(10L);
cflow.setFirstName("Feras");
cflow.setLastName("Odeh");
cflow.setCustomerType("type");
ObservableList<CustomerCreationFlow> data = FXCollections.observableArrayList(cflow);
idclm.setCellValueFactory(new PropertyValueFactory<CustomerCreationFlow, String>("id"));
//I tried this but it didn't work
flowclm.setCellValueFactory(new PropertyValueFactory<CustomerCreationFlow, CustomerCreationFlow>("this"));
typeclm.setCellValueFactory(new PropertyValueFactory<CustomerCreationFlow, String>("customerType"));
flowTable.setItems(data);
Any Suggestion?

You should implement your custom CellFactory by extending TableCell.
In your custom TableCell, you can get the value of the line of the table (logically CustomerCreationFlow) by getting the TableRow of the current TableCell.
That gives:
class MyTableCell<S,T> extends TableCell<S, T>
#Override
public void updateItem(final T item, final boolean empty) {
super.updateItem(item, empty);
if (empty) {
this.setText(null);
this.setGraphic(null);
} else {
S item = (S) this.getTableRow().getItem();
// DO STUFF HERE
}
}
}
T is the type of the data defined by CellValueFactory. S is the type of the data representing a row.

Related

Get current rowindex from RowFilter include

I have filtered a JTable with following RowFilter and it works fine:
RowFilter<AbstractTableModel, Object> rowFilter = new RowFilter<AbstractTableModel, Object>(){
public boolean include(RowFilter.Entry<? extends AbstractTableModel, ? extends Object> entry){
String cellValue = entry.getValue(0).toString();
boolean isRowIncluded = cellValue.startsWith(filterText) ? true : false;
return isRowIncluded;
}
};
But I am facing an issue. I have to apply the same filter logic to TreeTable. I have a custom table and table model extended from AbstractTableModel. I need to filter the rows and show its parents rows (parent rows may not satisfy isRowIncluded).
How can I achieve the functionality with my existing row filter?

Inserting integer (not String) data into a JavaFX2 TableView

so I've got a table working properly and grabbing data from an ObservableList with the code here:
public void setMainTableData(ObservableList<FileMP3> list)
{
artistCol.setCellValueFactory(new PropertyValueFactory<FileMP3, String>("artist"));
albumCol.setCellValueFactory(new PropertyValueFactory<FileMP3, String>("album"));
titleCol.setCellValueFactory(new PropertyValueFactory<FileMP3, String>("title"));
trackCol.setCellValueFactory(new PropertyValueFactory<FileMP3, String>("track"));
yearCol.setCellValueFactory(new PropertyValueFactory<FileMP3, String>("year"));
mainTable.setItems(list);
}
These columns, however do not ALL contain string data - I need to able to insert an int, and potentially other types like Duration. The track and year entries are stored as integers, and there is a (not shown) entry called length. This is stored in my FileMP3 object as a Duration, and I don't see any obvious way to manipulate the data stored there before inserting it into the table. I'd like to be able to use Duration.getMillis() and then perform some math on that to get it into a displayable int format, but I want to keep it stored in the FileMP3 as Duration.
All the tutorials I've read on the topic all use the constructor as such:
new PropertyValueFactory<FileMP3, String>("genre")
All in all, I'd like to be able to insert something other than a String into the table.
You can just replace String with any (reference, not primitive) type. For example:
TableColumn<FileMP3, Integer> yearCol = new TableColumn<>("Year");
yearCol.setCellValueFatory(new PropertyValueFactory<FileMP3, Integer>("year"));
Similarly with Duration (instead of Integer).
By default, the value in the cell will be displayed by calling toString() on the value in the cell. If you want the value to be displayed differently, you can create a custom cell factory (different to a cell value factory):
TableColumn<FileMP3, Integer> durationCol = new TableColumn<>("Duration");
durationCol.setCellValueFactory(new PropertyValueFactory<FileMP3, Duration>("duration"));
durationCol.setCellFactory(new Callback<TableColumn<FileMP3, Duration>, TableCell<FileMP3, Duration>>() {
#Override
public TableCell<FileMP3, Duration> call(TableColumn<FileMP3, Duration> col) {
return new TableCell<FileMP3, Duration>() {
#Override
protected void updateItem(Duration duration, boolean empty) {
super.updateItem(duration, empty);
if (empty) {
setText(null);
} else {
setText(Double.toString(duration.toMillis());
}
}
};
}
});
You can provide a custom cell value factory:
duration.setCellValueFactory(new Callback<CellDataFeatures<FileMP3, Integer>, ObservableValue<Integer>>() {
#Override public ObservableValue<Integer> call(CellDataFeatures<FileMP3, Integer> c) {
return new SimpleIntegerProperty(c.getValue().getDurationAsInt()));
}
});

GWT DataGrid Headers, filterable and sortable

I am trying to extends GWT's DataGrid capabilities for my own project and would like to add the ability to filer columns. I have successfully rendered a filter box in the Header, but it is not responding to events.
Following is the relvant part of my code, which has been adapted from the code given here: CellTable with custom Header containing SearchBox and Focus Problem
The question above does not quite fit my needs, as it does not work if the columns are sortable.
Instead, I have developed a header consisted of 2 table rows (TR's), the top row containing filter boxes, the 2nd row containing column titles and responding to Sort events. The Sort events work OK, but the filter boxes to not respond to any events. Here's the code:
class HeaderBuilder extends AbstractHeaderOrFooterBuilder<Record> {
//HTML to render an Input Box
private InputBoxHTML inputBox = GWT.create(InputBoxHTML.class);
//List of columns in the table
private List<ListGridColumn<?>> columns = new ArrayList<ListGridColumn<?>>();
//Constructor. ListGrid is the outer class extending DataGrid
private HeaderBuilder() {
super(ListGrid.this, false);
}
#Override
protected boolean buildHeaderOrFooterImpl() {
TableRowBuilder tr = startRow();
tr.startTH().endTH(); //extra column
//Create top row of column headers - filter boxes for filterable columns, empty cells for non-filerable
for (ListGridColumn<?> column : this.columns) {
TableCellBuilder th = tr.startTH();
Header<String> header;
//If this column is filterable...
if (column.filter) {
//Create a new Cell containing an Input Box
AbstractCell<String> cell = new AbstractCell<String>("click","keydown","keyup") {
public void render(Context context, String value, SafeHtmlBuilder sb) {
sb.append(inputBox.input(""));
}
public void onBrowserEvent(Context context, Element parent, String value, NativeEvent event, ValueUpdater<String> valueUpdater) {
//These events never fire!
Window.alert("event");
}
};
header = new Header<String>(cell) {
public String getValue() {
return "value";
}
};
} else {
//Empty cell for non-filterable columns
header = new TextHeader("");
}
Context context = new Context(0, 0, header.getKey());
renderHeader(th, context, header);
th.endTH();
}
tr.endTR();
//Bottom row : header captions & sorting. This all works OK
tr = startRow();
tr.startTH().endTH(); //extra column
for (ListGridColumn<?> column : this.columns) {
TableCellBuilder th = tr.startTH();
enableColumnHandlers(th, column);
Header<String> header = new TextHeader(column.headerStr);
Context context = new Context(0, 0, header.getKey());
if (column.sortKey!=null) {
this.renderSortableHeader(th, context, header, true, true);
} else {
this.renderHeader(th, context, header);
}
th.endTH();
}
tr.endTR();
return true;
}
}
If you looks in the source code of insertColumn(int beforeIndex, Column col, Header header, Header footer) method in AbstractCellTable class (which is extended by DataGrid and CellTable), when a column is inserted (or added) all the events for the header (cell or footer) are sinked in order to propagate it from the table to the corresponding cell:
if (header != null) {
Set<String> headerEvents = header.getCell().getConsumedEvents();
if (headerEvents != null) {
consumedEvents.addAll(headerEvents);
}
}
...
CellBasedWidgetImpl.get().sinkEvents(this, consumedEvents);
You're declaring the Headers in the builder but not "registering" it, therefore events are not propagated to the header cell. You should find a way to register it. I don't see any clean solution because DataGrid can not be easily extended.
I can purpose you two dirty ones:
Create your version of DataGrid (you need to copy and paste the code and declared it in the same package of DataGrid) and modify it in order to register two header for column.
Create a new Header capable of propagate the events to the correct instance of the two headers in the column.
I will go for 2, creating an header with two cell inside, you can use these two cell in the builder.

How to handle empty selection in a JFace bound combobox?

I am developing a search dialog in my eclipse-rcp application.
In the search dialog I have a combobox as follows:
comboImp = new CCombo(grpColSpet, SWT.BORDER | SWT.READ_ONLY);
comboImp.setBounds(556, 46, 184, 27);
comboImpViewer = new ComboViewer(comboImp);
comboImpViewer.setContentProvider(new ArrayContentProvider());
comboImpViewer.setInput(ImpContentProvider.getInstance().getImps());
comboImpViewer.setLabelProvider(new LabelProvider() {
#Override
public String getText(Object element) {
return ((Imp)element).getImpName();
}
});
Imp is a database entity, ManyToOne to the main entity which is searched, and ImpContentProvider is the model class which speaks to embedded sqlite database via jpa/hibernate.
This combobox is supposed to contain all instances of Imp, but to also let empty selection; it's value is bound to a service bean as follows:
IObservableValue comboImpSelectionObserveWidget =
ViewersObservables.observeSingleSelection(comboImpViewer);
IObservableValue filterByImpObserveValue =
BeansObservables.observeValue(searchPrep, "imp");
bindingContext.bindValue(comboImpSelectionObserveWidget, filterByImpObserveValue
, null, null);
As soon as the user clicks on the combo, a selection (first element) is made: I can see the call to a selectionlistener i added on the viewer. My question is:
after a selection has been made, how do I let the user change his mind and have an empty selection in the combobox? should I add a "fake" empty instance of Imp to the List returned by the ImpContentProvider? or should I implement an alternative to ArrayContentProvider?
and one additional related question is:
why calling deselectAll() and clearSelection() on the combo does NOT set a null value to the bound bean?
ComboViewer.setSelection(StructuredSelection.EMPTY) will fire selection event and set "imp" to null. Combo widget selection event is only triggered when manually selected from dropdown list i think.
Use Converter
Define empty selection obj, let us say EMPTYEnum ( display empty string in label provider)
You can define UpdateValueStrategy for target-to-model and set IConverter when you bind observables.
In the converter, you can convert EMPTYEnum to null.
IConverter:
fromType: Object.class
toType: Object.class
public Object convert(Object fromObject)
{
if(fromObject instanceof EMPTYEnum)
{
return null;
}
return fromObject;
}
make LabelProvider and handle null value
public String getText(Object element) {
if (element == null) {
return "Choose one";
}
return super.getText(element);
}
insert null value at index 0 and handle empty selection
combo.setInput(yourItems);
combo.insert(null, 0);
combo.getCCombo().select(0);
combo.addPostSelectionChangedListener(new ISelectionChangedListener() {
#Override
public void selectionChanged(SelectionChangedEvent event) {
if (combo.getCCombo().getSelectionIndex() == -1) {
combo.getCCombo().select(0);
}
}
});
bind ComboViewer ...

How to make a jtable cell listen to changes from another cell

Please help. I have two cells from a jtable, one ID and one Description. The ID and Description are both custom combobox. What I am trying to do is when the ID loses its focus or changes its value, the Description will update based on the value on the ID. How do I do that?
Here is my code for the implementation of both cells:
TableColumn subAccountCol = jTable1.getColumnModel().getColumn(table.findColumn("SubAccount"));
javax.swing.JComboBox accountCbx = new javax.swing.JComboBox(Account.toArray());
javax.swing.JComboBox accountDescCbx = new javax.swing.JComboBox(AccountDesc.toArray());
CompleteText.enable(accountCbx);
CompleteText.enable(accountDescCbx);
jTable1.getColumnModel().getColumn(table.findColumn("Account")).setCellEditor(new ComboBoxCellEditor(accountCbx));
jTable1.getColumnModel().getColumn(table.findColumn("Account Description")).setCellEditor(new ComboBoxCellEditor(accountDescCbx));
The cell editor will ultmately call the method setValueAt() on your table model. In this table model, simply update the linked cell value in addition to the edited celle value, and fire the appropriate change event for both cells.
public MyTableModel extends AbstractTableModel() {
// ...
// modifies the value for the given cell
#Override
public void setValueAt(Object value, int row, int column) {
Foo foo = this.list.get(row);
if (column == INDEX_OF_ID_COLUMN) {
foo.setId(value); // change the ID
fireTableCellUpdated(row, column); // signal the the ID has changed
// and now also change the description
String newDescription = createNewDescription(value);
foo.setDescription(newDescription);
fireTableCellUpdated(row, INDEX_OF_DESCRIPTION_COLUMN); // signal the the description has changed
}
// ...
}
}

Categories