I just started to develop a JavaFX application. Maybe I didn't get how JavaFX uses the TableView and I should use something different instead.
Currently my TableView displays data in multiple columns an when I double-click a cell the background color changes (by setCellFactory(customFactory)).
Now I want to access different cells of the table by using indices (column,row) and checking the background color.
The cells with a changed background color should be stored after a certain button was clicked.
I would like to get every cell with changed background(get celltext) for each row and store this for later use in a data structure like a Map>.
Would be really nice if somebody can give me a hint. Thank for your Help.
I suppose, you are adding an EventHandler to the TableCell, which is returned by your customFactory. This EventHandler is handling the doubleclick-event and sets the background color, right?
This handler has access to the parameter which is passed to the Callbacks/CustomFactories call-method, which contains the model-bean of the current row. You could set a flag or the columns name in that model-bean when a doubleClickEvent occurs.
Then
after a certain button was clicked
you can get your info, by checking the tables items. The row-index of each item is equivalent to the index of this item in the List of TableView#getItems
Also have a look at http://controlsfx.bitbucket.org/org/controlsfx/control/spreadsheet/SpreadsheetView.html if you need more TableFunctions.
EDITED
This is a code-example:
The Model-Bean used in TableView:
class Model {
private String propertyA;
private String propertyB;
#lombok.Getter
private Set<String> propertiesClicked = new HashSet<>();
The javafx-controls, annotate them with #FXML if you use FXMLs:
private TableView<Model> tableView;
private TableColumn<Model, String> propertyAColumn;
private TableColumn<Model, String> propertyBColumn;
and the the CellFactory. Create a more generic CellFactory if you need it for multiple columns:
propertyAColumn.setCellFactory((value) -> {
TableCell<Model, String> tableCell = new TableCell<Model, String>() {
//Override the Methods which you need
};
tableCell.setOnMouseClicked((mouseEvent) -> {
if (mouseEvent.getButton().equals(MouseButton.PRIMARY)) {
if (mouseEvent.getClickCount() == 2 && !tableCell.getStyleClass().contains("buttonClicked")) {
tableCell.getStyleClass().add("buttonClicked");
tableView.getSelectionModel().getSelectedItem().getPropertiesClicked().add("propertyA");
}
}
});
return tableCell;
});
Related
I am making a dynamic UI using kotlin and Jetpack compose and storing the information in an object box database.
The aim is that i will have a composable that starts off with 1 initial item that is empty and when the contents of the textbox have been filled in would allow the red "+" button to be clicked and then another textfield would appear. These values will need to be able to be edited constantly all the way until the final composable value is stored. The button changes colour currently and the states are fine with the button so i can add and remove rows
The data comes in as a string and is converted into a Hashmap<Int, String>. The int is used to store the position in the map being edited and the string would be the text value.
Using log messages i see that the information is updated in the list and for recomp sake i instantly store the value of the edited list in a converted json string.
At the moment:
When i scroll past the composable it resets and looks like the initial state (even if i have added multiple rows)
Log messages show that my hashmap has the values from before e.g. {"0":"asdfdsa"} but the previous positions are ignored and as the previous information would still be present but not shown on the UI when i enter it into the first field again (the others are not visible at the time) {"0":"asdfdsa","0":"hello"}. This would later cause an error when trying to save new data to the list because of the duplicate key
In the composables my hashmap is called textFields and is defined like this. Number is used to determine how many textfields to draw on the screen
val textFields = remember { getDataStringToMap(data.dataItem.dataValue) }
val number = remember { mutableStateOf(textFields.size) }
the method to getDataStringToMap is created like this
private fun getDataMapToString(textFieldsMap: HashMap<Int, String>): String {
val gson = Gson()
val newMap = hashMapOf<Int, String>()
for (value in textFieldsMap){
if (value.value .isNotBlank()){
newMap[value.key] = value.value
}
}
return gson.toJson(newMap)
}
and the method to getDataStringToMap is created like this (I explicitly define the empty hashmap type because its more readable for me if i can see it)
private fun getDataStringToMap(textsFieldsString: String): HashMap<Int, String> {
val gson = Gson()
return if (textsFieldsString.isBlank()) {
hashMapOf<Int, String>(0 to "")
} else {
val mapType = HashMap<Int, String>().javaClass
gson.fromJson(textsFieldsString, mapType)
}
the composables for the textfields are called like this
items(number.value) { index ->
listItem(
itemValue = textFields[index].orEmpty(),
changeValue = {
textFields[index] = it
setDataValue(getDataMapToString(textFields))
},
addItem = {
columnHeight.value += itemHeight
scope.launch {
scrollState.animateScrollBy(itemHeight)
}
},
deleteItem = {
columnHeight.value -= itemHeight
scope.launch {
scrollState.animateScrollBy(-itemHeight)
}
},
lastItem = index == number.value - 1,
index = index
)
}
Edited 30/12/2022
Answer from #Arthur Kasparian solved issues. Change to rememberSaveable retains the UiState even on scroll and recomp.
Now just to sort out which specific elements are removed and shown after :D
The problem is that remember alone does not save values on configuration changes, whereas rememberSaveable does.
You can read more about this here.
I'm using two tables, the first one contains "Teams", the second one "Team members" and is populating based on the first table selection. I'm also showing various stats depending on the selection, be it a team or a specific member. If no member is selected, team stats are showed, otherwise member stats are showed.
I'm using ItemChangeListeners on the tables to redraw the stats, but this prevents me to click on an already selected team to "deselect" a selected member from that team, since no event is triggered in that circumstance. As a solution I'm also using a ClickListener on the Team table, but it seems to work only if I click on the word (instead of working on the whole cell).
teamsTable.setClickListener("name", new Table.CellClickListener() {
#Override
public void onClick(Entity item, String columnId) {
if (teamsDs.getItem() == item) {
teamsDs.setItem(null);
teamsDs.setItem((Team) item);
} else {
teamsDs.setItem((Team) item);
teamsTable.setSelected((Team) item);
}
}
});
Is there a better way to catch a click on a table cell? Or is there a better way to approach the problem altogether?
Since CUBA Table is a wrapper of Vaadin table you can use ItemClickListener from Vaadin with CUBA table:
public class DemoScreen extends AbstractWindow {
#Inject
private Table<User> usersTable;
#Override
public void init(Map<String, Object> params) {
super.init(params);
com.vaadin.ui.Table vTable = usersTable.unwrap(com.vaadin.ui.Table.class);
vTable.addItemClickListener((ItemClickEvent.ItemClickListener) event ->
showNotification("Item " + event.getItemId())
);
}
}
It will be fired each time you click on a table cell.
I have a problem in a Tableview created using javafx. I have set the edititable="true" on the fxml file of the tabel, then in the controller I execute
#FXML
private TableColumn<ARule,Object> rankCol;
rankCol.setCellValueFactory(new PropertyValueFactory<ARule, Object>("label")); rankCol.setCellFactory(ChoiceBoxTableCell.forTableColumn(Main.getlabelSample()));
rankCol.setOnEditCommit(e -> {System.out.println("something happens!");});
To create in the column rank, a choicebox to change te value of the property.
The ARule has a property field and the getter and setters:
private SimpleObjectProperty label;
public SimpleObjectProperty labelProperty() {
return label;
}
public void setLabel(Object label) {
this.label.set(label);
}
public Object getLabel(){
return this.label.getValue();
}
The function Main.getlabelSample() retrun this object filled with Strings or Integer
private static final ObservableList<Object> labelSample = FXCollections.observableArrayList();
The problem is that in the interface I can edit the column and it displays the correct value in the labelSample list, the problem is that it doesn't change the value of the ARule object, this is highlighted by the missing call of the setOnEditCommit handler. The value on the GUI is the new one selected but the value saved on the items in the table is the old one.
I have also a separated button to change the value of that column on the selected row and if I trigger that, the values changes for "real" (both on the GUI and on the model).
What could be the error in the code?
The default edit commit behavior of the column is set as the onEditCommit property. If you call
rankCol.setOnEditCommit(...);
then you set this property to something else, i.e. you remove the default behavior.
If you want to add additional behavior to the default, use addEventHandler(...) instead of setOnEditCommit(...):
rankCol.addEventHandler(TableColumn.editCommitEvent(), e -> {
System.out.println("Something happens");
});
Find the answer the line of code:
rankCol.setOnEditCommit(e -> {System.out.println("something happens!");});
for some reason overwrite the default behaviour of updating the cell changing the code into
rankCol.setOnEditCommit(e -> {
e.getTableView().getItems().get(e.getTablePosition().getRow()).setLabel(e.getNewValue());
System.out.println("Something happens!");});
Resolved the problem. At the moment I don't know why this is happening.
I am trying to customize the series labels of the X axis of a linear ultrachart using vb.net.
I looked into the documentation from Infragistics and found that I could use this code:
UltraChart.Axis.Y.Labels.SeriesLabels.FormatString = "<Item_Label>"
A description of the types of labels available can be seen here.
However, I'm not getting the result I expected. I get "Row#1" and I want to get only the "1".
I've tried the approach used in the first reply of this post in Infragistics forums, which consists of using an hashtable with the customized labels. The code used there is the following (in C#):
Hashtable labelHash = new Hashtable();
labelHash.Add("CUSTOM", new MyLabelRenderer());
ultraChart1.LabelHash = labelHash;
xAxis.Labels.ItemFormatString = "<CUSTOM>";
public class MyLabelRenderer : IRenderLabel
{
public string ToString(Hashtable context)
{
string label = (string)context["ITEM_LABEL"];
int row = (int)context["DATA_ROW"];
int col = (int)context["DATA_COLUMN"];
//use row, col, current label's text or other info inside the context to get the axis label.
//the string returned here will replace the current label.
return label;
}
}
This approach didn't work either.
I am using Infragistics NetAdvantage 2011.1.
Anyone has any idea how to customize these labels in order to obtain the number after "Row#"?
There are different approaches to solve this task. One possible solution could be if you are using FillSceneGraph event. By this way you could get your TEXT primitives and modify it. For example:
private void ultraChart1_FillSceneGraph(object sender, Infragistics.UltraChart.Shared.Events.FillSceneGraphEventArgs e)
{
foreach (Primitive pr in e.SceneGraph)
{
if (pr is Text &&((Text)pr).labelStyle.Orientation == TextOrientation.VerticalLeftFacing )
{
pr.PE.Fill = Color.Red;
((Text)pr).SetTextString("My custom labels");
}
}
}
OK. I`ll try to explain more deeply about FormatString property.
When you are using this property, you could determinate which information to be shown (for example: Items values or Data Values or Series Values). Of course there are option to use your custom FormatString.
For example:
axisX2.Labels.ItemFormat=AxisItemLabelFormat.Custom;
axisX2.Labels.ItemFormatString ="";
In this case we have labels which represent Date on your X axis, so if you are using these two properties, you are able to determinate the Date format (for example dd/MM/yyyy or MM/ddd/yy). In your scenario you have string values on your X axis. If you are not able to modify these strings values at lower level (for example in your database, through TableAdapters SQL query, DataSet, i.e. before to set your DataSource to our UltraChart), then you could use FillSceneGraph event and modify your Text primitives. More details about this event you could find at http://help.infragistics.com/Help/NetAdvantage/WinForms/2013.1/CLR4.0/html/Chart_Modify_Scene_Graph_Using_FillSceneGraph_Event.html If you need a sample or additional assistance, please do not hesitate to create a new forum thread in our web site - http://www.infragistics.com/community/forums/
I`ll be glad to help you.
I am trying to add an AbstractToolBar to a DefaultDataTable.
The toolbar has a button on click of which the selected rows should get deleted.
My table has a checkbox column, to select the rows.
AbstractToolBar implementation looks like this -
public class GridToolBar extends AbstractToolbar {
/**
*
*/
private static final long serialVersionUID = -2126515338632353253L;
Button btnDelete;
List<Contact> selected;
public GridToolBar(final DataTable<?> table) {
super(table);
// TODO Auto-generated constructor stub
btnDelete = new Button("delete",new Model("Delete"));
btnDelete.setOutputMarkupId(true);
btnDelete.add(new AjaxEventBehavior("onclick") {
private static final long serialVersionUID = 6720512493017210281L;
#Override
protected void onEvent(AjaxRequestTarget target) {
System.out.println(selected);
((UserProvider)table.getDataProvider()).remove(selected);
target.add(table);
}
});
add(btnDelete);
}
public void setSelected(List inList){
selected = inList;
}
}
The toolbar has been added to table as follows -
GridToolBar tb = new GridToolBar(table);
tb.setOutputMarkupId(true);
table.addTopToolbar(tb);
The code works fine, except on click of delete button it adds an additional delete button below the table. On inspecting it with firebug, the ids of both the buttons match exactly. On sorting the table though, the extra button is removed from the view.
Could someone help me how can I avoid creation of extra button on every click?
Why is it being created in the first place?
Any help is appreciated.
Thanks,
Sonam
You add the button directly to the table. This is incorrect, as you cannot have a button in a table. You need a <td> element. You can create one using a WebMarkupContainer. See also the source of for example NoRecordsToolbar