I have a TableView named tableVerre and I want to have every row of it checked for a criteria ( stock column value ) and execute some code on them as I scroll so I wrote this code but it makes the program consume a lot of CPU time, I'm not familiar with Lambda expressions so is there a simpler way to write this ? :
tableVerre.addEventFilter(ScrollEvent.ANY, new EventHandler<ScrollEvent>() {
#Override
public void handle(ScrollEvent scrollEvent) {
stock.setCellFactory(column -> {
return new TableCell<VerreFX, Number>() {
#Override
protected void updateItem(Number item, boolean empty) {
super.updateItem(item, empty);
TableRow<VerreFX> currentRow = getTableRow();
if (empty || item == null) {
setText("");
setGraphic(null);
currentRow.setStyle(tableVerre.getStyle());
} else {
setText(getItem().toString());
}
if (!isEmpty()) {
if ((int) item == 0 && st.getVerresBOX()) currentRow.setStyle("-fx-background-color:lightcoral");
}
}
};
});
}
});
The table view will reuse cells as the user scrolls, and will automatically call updateItem on the cells when they are reused for new items. So you should set the cell factory only once, and then just let the table view take care of doing the job it is designed to do. You can set the cell factory in the initialize() method if you are using FXML, or just wherever you create the table and columns otherwise.
Your cell implementation isn't quite correct: because a cell may be reused to display any two different items arbitrarily, you need to account for all possible conditions. In your implementation, if a cell shows an item for which item.intValue()==0 and is then reused to show an item for which item.intValue() != 0, then the style will not be updated correctly.
Also note that you should "convert" a Number to an int by calling intValue().
TableColumn<VerreFX, Number> stock ;
// ...
stock.setCellFactory(column -> new TableCell<VerreFX, Number>() {
#Override
protected void updateItem(Number item, boolean empty) {
super.updateItem(item, empty);
TableRow<VerreFX> currentRow = getTableRow();
if (empty || item == null) {
setText("");
setGraphic(null);
currentRow.setStyle(tableVerre.getStyle());
} else {
setText(getItem().toString());
}
if (!isEmpty()) {
if (item.intValue() == 0 && st.getVerresBOX()) {
currentRow.setStyle("-fx-background-color:lightcoral");
} else {
currentRow.setStyle(tableVerre.getStyle());
}
}
}
});
You should be able to remove the scroll event handler entirely.
Firstly beside over CPU time using you didn't cover all the scrolling situations because if the user scrolls using the key Down/Up or using the scroll bar the scroll event will not be fired. So you have to add two more EventFilter, the first will handle the Scrolling using the Scrollbar.
tableVerre.addEventFilter(MouseEvent.MOUSE_CLICKED,(
MouseEvent event)->
{
if ((event.getTarget() instanceof TableColumnHeader) | event.isDragDetect()) {
System.err.println("Mouse Draged : " + event.toString());
stock.setCellFactory((TableColumn<VerreFX, Number> column) -> {
return new TableCell<VerreFX, Number>() {
#Override
protected void updateItem(Number item, boolean empty) {
super.updateItem(item, empty);
TableRow<VerreFX> currentRow = getTableRow();
if (empty || item == null) {
setText("");
setGraphic(null);
currentRow.setStyle(tableVerre.getStyle());
} else {
setText(getItem().toString());
}
if (!isEmpty()) {
if ((int) item == 0 && st.getVerresBOX()) {
currentRow.setStyle("-fx-background-color:lightcoral");
}
}
}
};
});
}
});
And the second one will handle the scrolling using the keyboard Key DOWN/UP.
tableVerre.addEventFilter(KeyEvent.KEY_PRESSED,new EventHandler<KeyEvent>(){
#Override
public void handle(KeyEvent event) {
if (event.getCode() == KeyCode.DOWN | event.getCode() == KeyCode.UP) {
stock.setCellFactory(column -> {
return new TableCell<VerreFX, Number>() {
#Override
protected void updateItem(Number item, boolean empty) {
super.updateItem(item, empty);
TableRow<VerreFX> currentRow = getTableRow();
if (empty || item == null) {
setText("");
setGraphic(null);
currentRow.setStyle(tableVerre.getStyle());
} else {
setText(getItem().toString());
}
if (!isEmpty()) {
if ((int) item == 0 && st.getVerresBOX()) {
currentRow.setStyle("-fx-background-color:lightcoral");
}
}
}
};
});
}
System.err.println("Key Pressed : " + event.toString());
}
});
Related
I'm trying to change the default value to Debit Memo when adding a new row in the Payments and Applications screen of the Accounts Receivable module. I've tried setting PXDefault(ARDocType.DebitMemo), but it doesn't appear to be working. Can anyone point me in the right direction?
The payments and applications page uses some interesting logic to determine the default value used, they define it in a call during the rowselected event for the header document.
protected virtual void ARPayment_RowSelected(PXCache cache, PXRowSelectedEventArgs e)
{
.....
SetDocTypeList(e.Row);
.....
}
public static void SetDocTypeList(PXCache cache, string docType)
{
string defValue = ARDocType.Invoice;
List<string> values = new List<string>();
List<string> labels = new List<string>();
if (docType == ARDocType.Refund)
{
defValue = ARDocType.CreditMemo;
values.AddRange(new string[] { ARDocType.CreditMemo, ARDocType.Payment, ARDocType.Prepayment });
labels.AddRange(new string[] { Messages.CreditMemo, Messages.Payment, Messages.Prepayment });
}
else if (docType == ARDocType.Payment || docType == ARDocType.VoidPayment)
{
values.AddRange(new string[] { ARDocType.Invoice, ARDocType.DebitMemo, ARDocType.CreditMemo, ARDocType.FinCharge });
labels.AddRange(new string[] { Messages.Invoice, Messages.DebitMemo, Messages.CreditMemo, Messages.FinCharge });
}
else
{
values.AddRange(new string[] { ARDocType.Invoice, ARDocType.DebitMemo, ARDocType.FinCharge });
labels.AddRange(new string[] { Messages.Invoice, Messages.DebitMemo, Messages.FinCharge });
}
if (!PXAccess.FeatureInstalled<FeaturesSet.overdueFinCharges>() && values.Contains(ARDocType.FinCharge) && labels.Contains(Messages.FinCharge))
{
values.Remove(ARDocType.FinCharge);
labels.Remove(Messages.FinCharge);
}
PXDefaultAttribute.SetDefault<ARAdjust.adjdDocType>(cache, defValue);
PXStringListAttribute.SetList<ARAdjust.adjdDocType>(cache, null, values.ToArray(), labels.ToArray());
}
private void SetDocTypeList(object Row)
{
ARPayment row = Row as ARPayment;
if (row != null)
{
SetDocTypeList(Adjustments.Cache, row.DocType);
}
}
To obtain the default you require you may implement the following code :
public class ARPaymentEntryExtension : PXGraphExtension<ARPaymentEntry>
{
protected virtual void ARPayment_RowSelected(PXCache cache, PXRowSelectedEventArgs e)
{
PXDefaultAttribute.SetDefault<ARAdjust.adjdDocType>(Base.Adjustments.Cache, ARDocType.DebitMemo);
}
}
I have 31 columns(day 1-day 31) and 12(monthnames) rows on my tableview. I want to search complete table (row wise search) for a data(day) and need to set a background color only for that matching cell.I could see that it is possible to set background color for a cell in a particular column using cellfactory function.
birthdayColumn.setCellFactory(column -> {
return new TableCell<Person, LocalDate>() {
#Override
protected void updateItem(LocalDate item, boolean empty) {
super.updateItem(item, empty);
if (item == null || empty) {
setText(null);
setStyle("");
}
// Style all dates in March with a different color.
if (item. == Month.MARCH) {
setTextFill(Color.CHOCOLATE);
setStyle("-fx-background-color: yellow");
} else {
setTextFill(Color.BLACK);
setStyle("");
}
};
});
Here it checking only one column named 'birthdayColumn'.But in my case it should check all the table cells(rowwise) in one single method and need to set a background color only for that matching cell.How i can do that.Thank you in advance
This should work:
LocalDate myBirthday = null; //initialize with the date you need to highlight
birthdayColumn.setCellFactory(column -> {
return new TableCell<Person, LocalDate>() {
#Override
protected void updateItem(LocalDate item, boolean empty) {
super.updateItem(item, empty);
if (item == null || empty) {
setText(null);
setGraphic(null);
}
else {
setText(item.toString());
setGraphic(null);
if (item.equals(myBirthday)) {
setTextFill(Color.CHOCOLATE);
setStyle("-fx-background-color: yellow");
}
else {
setTextFill(Color.BLACK);
setStyle("");
}
}
}};
});
I would like to be able to use a modal window to present the contents of a column to the user for editing. I am not able to make this work, and I am not sure where I am going wrong.
I have provided a button in the table which will indicate if there are additional details (in this case comments). When the user selects the button, I want to open a modal dialog to enter the data and when it closes, update the field.
I have gotten the majority of this wired up, but the data is not making it back to my model. I have tried several things, and all without results. It appears that the commit edit call I am making is not seeing the field as in "edit mode" and just skips.
This is my code for my custom table cell:
public class CommentTableCell<T> extends TableCell<T, String> {
private Button actionBtn;
private TextArea textArea;
public CommentTableCell(TableColumn<T, String> column) {
super();
actionBtn = new Button("my action");
actionBtn.setTooltip(new Tooltip("Select to add/edit comments..."));
actionBtn.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
startEdit();
System.out.println("Action: "+getItem());
Stage commentStage = new Stage();
AnchorPane ap = new AnchorPane();
textArea = new TextArea();
AnchorPane.setTopAnchor(textArea, 5.0);
AnchorPane.setBottomAnchor(textArea, 5.0);
AnchorPane.setLeftAnchor(textArea, 5.0);
AnchorPane.setRightAnchor(textArea, 5.0);
ap.getChildren().add(textArea);
Scene commentScene = new Scene (ap, 200, 200);
commentStage.setScene(commentScene);
commentStage.show();
commentStage.setOnCloseRequest(a -> {
commitEdit(textArea.getText());
});
// I have tried with an column.setOnEditCommit() as well as what is noted below which I found here, passing in the column.
final TableView<T> tableView = getTableView();
tableView.getSelectionModel().select(getTableRow().getIndex());
tableView.edit(tableView.getSelectionModel().getSelectedIndex(), column);
}
});
setText(null);
}
#Override
public void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
actionBtn.getStyleClass().clear();
setEditable(false);
if (item != null && item.length() > 0) {
actionBtn.getStyleClass().add(CSSConstants.GRID_BUTTON_EDIT_COMMNET);
setGraphic(actionBtn);
} else if (!empty) {
actionBtn.getStyleClass().add(CSSConstants.GRID_BUTTON_ADD_COMMNET);
setGraphic(actionBtn);
} else {
setGraphic(null);
}
}
}
During the execution it hits the commitEdit() call and the following has isEditing in the TableCell as null:
#Override public void commitEdit(T newValue) {
if (! isEditing()) return;
My table looks basically like this:
TableView<SomeDTO> addressTableView = new TableView()
addressTableView.setItems(sortedItems);
addressTableView.setEditable(true);
commentsColumn.setCellValueFactory(cellValue -> cellValue.getValue().commentsProperty());
commentsColumn.setCellFactory(tc -> new CommentTableCell<SomeDTO>(commentsColumn));
I have found a solution to my issue - though I am not sure it is the best way or not.
I have changed my CommentTableCell as follows and it seems to work like a charm..
public class CommentTableCell<T> extends TableCell<T, String> {
private Button actionBtn;
public CommentTableCell() {
super();
actionBtn = new Button("my action");
actionBtn.setTooltip(new Tooltip("Select to add/edit comments..."));
actionBtn.setOnAction(event ->
{
Stage commentStage = new Stage();
AnchorPane ap = new AnchorPane();
TextArea textArea = new TextArea();
AnchorPane.setTopAnchor(textArea, 5.0);
AnchorPane.setBottomAnchor(textArea, 5.0);
AnchorPane.setLeftAnchor(textArea, 5.0);
AnchorPane.setRightAnchor(textArea, 5.0);
ap.getChildren().add(textArea);
Scene commentScene = new Scene (ap, 200, 200);
commentStage.setScene(commentScene);
if(getItem() != null) {
String myValue = getItem();
textArea.setText(myValue);
textArea.selectAll();
}
commentStage.show();
commentStage.setOnCloseRequest(a -> {
commitEdit(textArea.getText());
});
});
}
#Override
#SuppressWarnings({"unchecked", "rawtypes"})
public void commitEdit(String item) {
if (isEditing()) {
super.commitEdit(item);
} else {
final TableView table = getTableView();
if (table != null) {
TablePosition position = new TablePosition(getTableView(),
getTableRow().getIndex(), getTableColumn());
CellEditEvent editEvent = new CellEditEvent(table, position,
TableColumn.editCommitEvent(), item);
Event.fireEvent(getTableColumn(), editEvent);
}
updateItem(item, false);
if (table != null) {
table.edit(-1, null);
}
}
}
#Override
public void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
actionBtn.getStyleClass().clear();
setEditable(false);
if (item != null && item.length() > 0) {
actionBtn.getStyleClass().add(CSSConstants.GRID_BUTTON_EDIT_COMMNET);
setGraphic(actionBtn);
} else if (!empty) {
actionBtn.getStyleClass().add(CSSConstants.GRID_BUTTON_ADD_COMMNET);
setGraphic(actionBtn);
} else {
setGraphic(null);
}
}
}
I got a method which evaluates whats in the string with what was set by user in TableView cells. (string have values like "343288709789" and each cell contains null or single digit number).
It works, however now I would like TableView to highlight(change background or text color) certain cells where user set wrong value. How can I achive this?
PS. Ive read similiar questions to this but I dont think I can achieve this in TableCell class implementation, because cells should change color only after uses press "Check" option.
private void compareAndEvaluate(String source, NewTableView newTableView){
ObservableList<MyData> data = newTableView.getData();
source = source.replaceAll("\\D+","");
System.out.println("data size: " +data.size() + "\n\n" + source);
int numOfValid = 0,
numOfInvalid = 0;
ObservableList<ObjectProperty<Integer>> rowData;
for(int i=0, n=0; i < data.size(); i++){ //rows(Y)
rowData = data.get(i).returnCellsData();
for(int j = 1; j < rowData.size(); ++j, ++n){ //columns(X)
Integer iNext = Integer.valueOf(String.valueOf(source.charAt(n)));
if( iNext == rowData.get(j).get() )
++numOfValid;
else
++numOfInvalid;
}
}
Dialogs.create().title("Results").masthead(null).message("Correct: " + numOfValid + ", Invalid: " + numOfInvalid).showInformation();
}
If that helps, here is implementation of TableCell used by TableView:
public class EditingCellNumbers extends TableCell<MyData, Integer>{
private TextField textField;
private TableView<MyData> parentTableView;
public EditingCellNumbers(TableView<MyData> parent) {
this.parentTableView = parent;
}
#Override
public void startEdit(){
if (!isEmpty()) {
super.startEdit();
createTextField();
setText(null);
setGraphic(textField);
textField.selectAll();
textField.requestFocus();
}
}
#Override
public void cancelEdit() {
super.cancelEdit();
if(getItem() != null){
setText(String.valueOf(getItem()));
}else{
setText(null);
commitEdit(null);
}
setGraphic(null);
}
#Override
public void updateItem(Integer item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setText(null);
setGraphic(null);
} else {
if (isEditing()) {
if (textField != null) {
textField.setText(getString());
}
setText(null);
setGraphic(textField);
} else {
setText(getString());
setGraphic(null);
if(getTableColumn().getText() == "#"){
setStyle("-fx-font-weight: bold;"
+ "-fx-background-color: linear-gradient( from -100.0% 150.0% to 120.0% 100.0%, rgb(128,128,128) 0.0, rgb(255,255,255) 100.0);");
}else{
if(getItem() == null)
setStyle("-fx-border-color: lavender; -fx-border-width: 0 1 0 0;");
else
setStyle("-fx-border-color: palegreen; -fx-border-width: 0 1 1 0;");
}
}
}
}
private void createTextField() {
textField = new TextField(getString());
textField.setStyle("-fx-background-color: ivory; -fx-border-color: red;");
textField.setMinWidth(this.getWidth() - this.getGraphicTextGap()* 2);
textField.focusedProperty().addListener(
(ObservableValue<? extends Boolean> arg0, Boolean arg1, Boolean arg2) -> {
if (!arg2) {
if(getItem() != null){
try{
commitEdit(Integer.valueOf(textField.getText()));
}catch(NumberFormatException f){
commitEdit(null);
}
}else
commitEdit(null);
}
});
textField.setOnKeyReleased(new EventHandler<KeyEvent>() {
#Override
public void handle(KeyEvent event) {
if(event.getCode() == KeyCode.BACK_SPACE){
if(getItem() != null){
numberOfEmptyCells.set(numberOfEmptyCells.get() + 1);
numberOfFilledCells.set(numberOfFilledCells.get() - 1);
}
commitEdit(null);
}else{
try{
int i = Integer.valueOf(textField.getText());
//digit given...
if( (i>=0) && (i<10) ){//making sure cell is filled with just one digit
if(getItem() == null){
numberOfEmptyCells.set(numberOfEmptyCells.get() - 1);
numberOfFilledCells.set(numberOfFilledCells.get() + 1);
}
commitEdit(Integer.valueOf(textField.getText()));
int selectedColumn = parentTableView.getSelectionModel().getSelectedCells().get(0).getColumn(); // gets the number of selected column
int selectedRow = parentTableView.getSelectionModel().getSelectedCells().get(0).getRow();
//moving to another cell editing
if(selectedColumn < numberOfColumns-1){
parentTableView.getSelectionModel().selectNext();
parentTableView.edit(selectedRow, parentTableView.getColumns().get(selectedColumn+1));
}else{
parentTableView.getSelectionModel().select(selectedRow+1, parentTableView.getColumns().get(1));
parentTableView.edit(selectedRow+1, parentTableView.getColumns().get(1));
}
}else
textField.clear();
}catch(NumberFormatException e){
textField.clear();
}
}
}
});
}
private String getString() {
return getItem() == null ? "" : getItem().toString();
}
}
}
Instead of making your columns in your data model Integer, make them some kind of an object that stores both the integer and the evaluation result. Use the evaluation result to determine the colour of the cell in your customized TableCell.
I create a new CellTable.
Override the onBrowserEvent2 to handle mouse events.
Add a new element to the table.
Sadly the event listener is not working on the added element. It's only working on the initial elements.
table = new CellTable<Contact>() {
public void onBrowserEvent2(Event event) {
Element tr = Element.as(event.getEventTarget());
boolean gotIt = false;
while (!gotIt && tr != null) {
if (!tr.getTagName().equals("TR")) {
tr = tr.getParentElement();
continue;
}
gotIt = true;
}
if (tr == null)
return;
tr = Element.as(tr.getChild(1));
Element out = DOM.createDiv();
out.setInnerHTML("<a href='xxx'>Edit contact</a> | <a href='xxx'>Add visit plan</a> | <a href='xxx'>New visit</a>");
switch (DOM.eventGetType(event)) {
case Event.ONMOUSEOVER:
tr.appendChild(out);
break;
case Event.ONMOUSEOUT:
tr.getChild(1).removeFromParent();
break;
}
}
};
If you are fine with it you can have all your handlers declared separately.
You can always add Mouse Handlers directly like this:
//code depicting a MouseOverHandler. Use this for any type of GWT Event Handler
CellTable<String> cellTable = new CellTable<String>();
cellTable.addDomHandler(new MouseOverHandler() {
#Override
public void onMouseOver(MouseOverEvent event) {
// Your logic
}
}, MouseOverEvent.getType());
That should help