Why is my TableView not allowing the ID column to be editable? The getPenaltyIdProperty() returns an IntegerProperty (a SimpleIntegerProperty to be exact) and yet I thought this would allow the edits to happen automatically via bindings. what am I missing?
public class PenaltyDashboardManager {
private final TableView<Penalty> penaltyTable = new TableView<Penalty>();
/* ... */
private void initializeTable() {
penaltyTable.setItems(Penalty.getPenaltyManager().getPenalties());
penaltyTable.setEditable(true);
TableColumn<Penalty,Number> penaltyId = new TableColumn<>("ID");
penaltyId.setCellValueFactory(c -> c.getValue().getPenaltyIdProperty());
penaltyId.setEditable(true);
/* ... */
penaltyTable.getColumns.add(penaltyId);
}
}
You need a cell factory to produce a cell that allows the user to edit the value. E.g.
penaltyId.setCellFactory(TextFieldTableCell.forTableColumn(new NumberStringConverter());
Related
I created a TableTree that contains object of class Component that has a boolean property "selected".
I want to hide the rows from the table where the rows component is not selected.
I tried this:
componentTree.setRowFactory(new Callback<TreeTableView<Component>, TreeTableRow<Component>>() {
#Override
public TreeTableRow<Component> call(TreeTableView<Component> param) {
TreeTableRow<Component> row = new TreeTableRow<Component>() {
#Override
protected void updateItem(Component component, boolean empty) {
if(!empty) {
if (!component.isSelected()) {
setVisible(false);
setManaged(false);
System.out.println("hide");
} else {
setVisible(true);
setManaged(true);
System.out.println("show");
}
}
}
};
return row;
}
});
On system.out I can see a lot of "show" and "hide" messages, but this doesn't affect the table, all rows are shown as before.
Any idea on this topic?
Thanks!
I used eclipse's fx.ui.controls library for the same achieve the same goal before.
<dependency>
<groupId>at.bestsolution.eclipse</groupId>
<artifactId>org.eclipse.fx.ui.controls</artifactId>
<version>2.2.0</version>
</dependency>
The library provides a class: FilterableTreeItem<T> under the tree package. This class was designed to be used in cases like yours. You can provide a Predicate to the root of the tree and the items will get hidden when the value given changes:
// Children
final FilterableTreeItem<Component> childNode1 = new FilterableTreeItem<>(component1);
final FilterableTreeItem<Component> childNode2 = new FilterableTreeItem<>(component2);
final FilterableTreeItem<Component> childNode3 = new FilterableTreeItem<>(component3);
// Root
final FilterableTreeItem<Component> root = new FilterableTreeItem<>(rootComponent);
root.getInternalChildren().setAll(childNode1, childNode2, childNode3);
root.setPredicate((parent, value) -> value.isSelected());
// TreeTableView
final TreeTableView<Component> treeTableView = new TreeTableView<>(root);
Note that you have to use getInternalChildren() to add children and the default getChildren().
FilterableTreeItem<T> also provides a predicateProperty() that you can bind to another property in case you need to update the how items are shown or hidden.
Another advatage of this class is that it shows the whole path up to the root of the items matching that predicate.
I understand that the question turned out to be huge, but otherwise it will not be clear to everyone.
I am writing a document manager application for myself, what is happening to me there now - I use the ObservableList collection (located in the main Main class) that operates on variables from the MainData class (it turns out that each instance of this class is a new row in the table). I display all the information through the controller class, that is, I have the first table, where I display all my documents (works), depending on the selected document, I display the necessary information in the corresponding labels (works), but I also there is a second table (it does not work correctly), where, according to my logic, depending on the selected document, I display file names that relate to the selected document (i.e., if one document is highlighted, the second table can display 3 files, and if you select another document, 5 files can be displayed). To check the functionality, I add a couple of rows to the SimpleListProperty collection (which is located in MainData respectively) to display them to the table, but at least my added data in the designer is displayed in the table, they all go in one line, and besides the second table creates the same number of rows as in the first (three identical rows are created in qwerty, asdfgh, since I have three documents created), how to get rid of this dependency? Those. I want to bring my application to the logic that I described above.
Class of all variables
public class MainData {
private final StringProperty numberContract;
private final ListProperty<String> nameFiles; //here I put the names of the files that relate to the document
// further variables for Label
public MainData(String numberContract) {
this.numberContract = new SimpleStringProperty(numberContract);
this.nameFiles = new SimpleListProperty<>(FXCollections.observableArrayList());
this.nameFiles.add("qwerty");
this.nameFiles.add("asdfgh"); //i want to add only two rows to the second table
}
//Further, all getters and setters
Class-controller
public class MainController {
#FXML
private TableView<MainData> contractTable;
#FXML
private TableColumn<MainData, String> numberContractColumn;
#FXML
private TableView<MainData> filesTable;
#FXML
private TableColumn<MainData, ObservableList<String>> nameFilesColumn;
//далее снова все для Label
// Link to main application.
private Main main;
public void setMain(Main main) {
this.main = main;
// Adding to the table data from the observed list
contractTable.setItems(main.getContractData());
filesTable.setItems(main.getContractData());
}
#FXML
private void initialize() {
// Initialize the table.
numberContractColumn.setCellValueFactory(cellData -> cellData.getValue().numberContractProperty());
nameFilesColumn.setCellValueFactory(cellData -> cellData.getValue().getNameFiles());
// Listen to the selection changes, and when changing, display
// additional information about the addressee.
contractTable.getSelectionModel().selectedItemProperty().addListener(
(observable, oldValue, newValue) -> showContractDetails(newValue));
Main class
public class Main extends Application {
private Stage menuBar;
private BorderPane mainWindow;
/**
* Data, in the form of an observable list of documents.
*/
private ObservableList<MainData> contractData = FXCollections.observableArrayList();
/**
* Returns data in the form of an observable list of documents.
* #return
*/
public ObservableList<MainData> getContractData() {
return contractData;
}
//further FXML display
I am building a preference page in Eclipse by extending the FieldEditorPreferencePage class. this page contains 2 fields : 1 BooleanFieldEditor (checkbox) and 1 FileFieldEditor. I would like to disable/enable the file field following the checkbox value.
I went up to something like this (some obvious code is not displayed):
public class PreferencePage extends FieldEditorPreferencePage implements IWorkbenchPreferencePage {
public static final String PREF_KEY_1 = "checkBoxPref";
public static final String PREF_KEY_2 = "filePref";
private FileFieldEditor pathField;
private BooleanFieldEditor yesOrNoField;
private Composite pathFieldParent;
#Override
protected void createFieldEditors() {
this.yesOrNoField = new BooleanFieldEditor(PREF_KEY_1, "Check this box!", getFieldEditorParent());
this.pathFieldParent = getFieldEditorParent();
this.pathField = new FileFieldEditor(PREF_KEY_2, "Path:", this.pathFieldParent);
addField(this.yesOrNoField);
addField(this.pathField);
boolean isChecked = getPreferenceStore().getBoolean(PREF_KEY_1);
updatePathFieldEnablement(! isChecked);
}
/**
* Updates the fields according to entered values
*/
private void updatePathFieldEnablement(boolean enabled) {
this.pathField.setEnabled(enabled, this.pathFieldParent);
}
#SuppressWarnings("boxing")
#Override
public void propertyChange(PropertyChangeEvent event) {
if (event.getProperty().equals(FieldEditor.VALUE) && event.getSource() == this.yesOrNoField) {
updatePathFieldEnablement(! (boolean) event.getNewValue());
}
super.propertyChange(event);
}
}
My question is about this second parameter in FieldEditor#setEnabled. This parameter is the parent composite of the FieldEditor's controls ("Used to create the controls if required" says the javadoc) . At first, I set the value with the return of getFieldEditorParent but then I got an exception "Different parent". So I ended storing it (cf. this.pathFieldParent) and give it back to setEnabled and it works (or it seems to work).
But I am not sure I am doing well, especially because I had to create a member in my class that means nothing to it (and I would have to create many of them if I had many fields to enable/disable).
Do you think I am doing well or is there a better way to provide this parent ? And could you explain to me why *setEnabled" needs it ?
Thanks.
You are using the default FLAT layout for the preference page. When this layout is used each call to getFieldEditorParent generates a new Composite so you have to make just one call and remember the correct parent. Using the GRID layout getFieldEditorParent always returns the same parent. This is the actual code:
protected Composite getFieldEditorParent() {
if (style == FLAT) {
// Create a new parent for each field editor
Composite parent = new Composite(fieldEditorParent, SWT.NULL);
parent.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
return parent;
}
// Just return the parent
return fieldEditorParent;
}
setEnabled does sometimes create a new Label control so it needs to know the correct parent Composite.
I have a tableview which have two columns.First column simply populated by observableList and Second column have choiceboxes in each cell.
My problem is that when I select value from choicebox and scroll down to select value from another choicebox and again scroll up then previous selected values of choiceboxe resets.
Below are my code:
#FXML
private TableColumn<FileHeaders, ChoiceBox<String>> fileHeaders;
public void setFileHeaders(ObservableList<String> fileHeadersObservableList) {
fileHeaders.setCellValueFactory(new Callback<TableColumn.CellDataFeatures<FileHeaders, ChoiceBox<String>>, ObservableValue<ChoiceBox<String>>>() {
#Override
public ObservableValue<ChoiceBox<String>> call(TableColumn.CellDataFeatures<FileHeaders, ChoiceBox<String>> rawUdrsList) {
ChoiceBox<String> choiceBox = new ChoiceBox<>();
System.out.println(choiceBox);//this value print again and again when I scroll the tableview
choiceBox.setItems(fileHeadersObservableList);
choiceBox.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<String>() {
#Override
public void changed(ObservableValue<? extends String> ov, String t, String valueFromChoiceBox) {
//write some code stuff
}
});
return new SimpleObjectProperty<ChoiceBox<String>>(choiceBox);
}
});
}
As far as I think that call method invoke everytime when i scroll the tableview,so new choicebox created and set into the tableview.
How can i solve this problem,
I really appreciate ifhelps from you guys.
Thanks
Your Error
A cell value factory should not return a node, it should just return the relevant data property backing the cell. Instead, the nodes should be returned in as part of the table cell implementation supplied by a cell factory.
Defining Cell Factories
The makery tutorials provide a nice example which shows the difference in usage between a cell value factory and a cell factory. Usually, the two are used in combination when you require a custom rendering of cell data.
However, JavaFX has predefined helper classes: look at How do you create a table cell factory in JavaFX to display a ChoiceBox?, which uses a ChoiceBoxTableCell. Perhaps your question just works out as a duplicate of that.
Background
To understand how ChoiceBoxTableCell works from first principles, you can look at its source code. A table cell is a Labeled. A Labeled can have both text and a graphic. The text label is used to display the chosen value when the cell is not being edited. Whenever the user double clicks on the cell to edit it, then the text is set to null and a graphic node for the label is displayed instead (in this case a choice box allowing the user to choose a new value for the cell being edited). Once the editing is complete, the new value for the choice is saved to the underlying backing data value property, the display switches back to plain text and the graphic for the cell is set back to null so that it no longer displays. For your use case, it will be easier just to use the pre-defined class rather than to write a custom implementation of the same functionality.
Sample
Output of the sample app below, after the user has twice clicked on a user state which is backed by a choice box, the first click highlighting the row and the second click bringing up the edit choice box for the item:
Sample code demoing use of a ChoiceBoxTableCell:
import javafx.application.Application;
import javafx.beans.property.*;
import javafx.collections.*;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.control.cell.*;
import javafx.stage.Stage;
public class TableChoices extends Application {
#Override
public void start(final Stage stage) throws Exception {
ObservableList<User> data = createTestData();
TableColumn<User, String> nameCol = new TableColumn<>("Name");
nameCol.setCellValueFactory(new PropertyValueFactory<>("name"));
TableColumn<User, UserState> stateCol = new TableColumn<>("State");
stateCol.setCellValueFactory(new PropertyValueFactory<>("state"));
stateCol.setCellFactory(
ChoiceBoxTableCell.forTableColumn(UserState.values())
);
stateCol.setEditable(true);
stateCol.setPrefWidth(100);
stateCol.setOnEditCommit(
(TableColumn.CellEditEvent<User, UserState> t) ->
t.getTableView()
.getItems()
.get(t.getTablePosition().getRow())
.setState(t.getNewValue())
);
TableView<User> tableView = new TableView<>(data);
//noinspection unchecked
tableView.getColumns().addAll(
nameCol,
stateCol
);
tableView.setPrefSize(170, 150);
tableView.setEditable(true);
stage.setScene(new Scene(tableView));
stage.show();
}
public static void main(String[] args) throws Exception {
launch(args);
}
public enum UserState {
ACTIVE,
LOCKED,
DELETED
}
public static class User {
private StringProperty name;
private ObjectProperty<UserState> state;
public User(String name, UserState state) {
this.name = new SimpleStringProperty(name);
this.state = new SimpleObjectProperty<>(state);
}
public String getName() {
return name.get();
}
public StringProperty nameProperty() {
return name;
}
public void setName(String name) {
this.name.set(name);
}
public UserState getState() {
return state.get();
}
public ObjectProperty<UserState> stateProperty() {
return state;
}
public void setState(UserState state) {
this.state.set(state);
}
}
private ObservableList<User> createTestData() {
return FXCollections.observableArrayList(
new User("Jack", UserState.ACTIVE),
new User("Jill", UserState.LOCKED),
new User("Tom", UserState.DELETED),
new User("Harry", UserState.ACTIVE)
);
}
}
I advise you to look over the sample code closely and note the use of a setOnEditCommit handler for the table column, which updates the backing data to reflect the edited value. Without code such as this, then the edit will not be committed back to the backing data (at least in Java 8, the JavaFX Table Tutorial notes that future JavaFX versions may make the implementation a bit less clunky).
I have a simple JFrame with several jtextfields inside, the text property of each jtextfield is bound with a field of an object through databinding (i used window builder to setup the binding), when the user change something on the JTextField the changes are automatically reflected to the bound object property, i have the needs that when the user press a JButton (Cancel Button) every changes done by the user will be discarded.
So i want that when the user start editing the field like a transaction will be started and depending on the user action (OK or Cancel Button) the transaction being Committed or RollBacked .
Is it possible with Swing Data Binding framework ? How ?
Here the code that initialize data bindings :
/**
* Data bindings initialization
*/
protected void initDataBindings() {
//Title field
BeanProperty<Script, String> scriptBeanProperty = BeanProperty.create("description");
BeanProperty<JTextField, String> jTextFieldBeanProperty = BeanProperty.create("text");
AutoBinding<Script, String, JTextField, String> autoBinding = Bindings.createAutoBinding(UpdateStrategy.READ_WRITE, script, scriptBeanProperty, textFieldName, jTextFieldBeanProperty, "ScriptTitleBinding");
autoBinding.bind();
//Id field
BeanProperty<Script, Long> scriptBeanProperty_1 = BeanProperty.create("id");
BeanProperty<JLabel, String> jLabelBeanProperty = BeanProperty.create("text");
AutoBinding<Script, Long, JLabel, String> autoBinding_1 = Bindings.createAutoBinding(UpdateStrategy.READ, script, scriptBeanProperty_1, labelScriptNo, jLabelBeanProperty, "ScriptIdBinding");
autoBinding_1.bind();
}
nothing out off the box, you have to implement the buffering logic yourself. An example is in my swinglabs incubator section, look at the AlbumModel. Basically
the bean is Album
AlbumModel is a wrapper (aka: buffer) around the bean with the same properties as the wrapped: the view is bound to the properties of this wrapper
internally, it uses a read-once binding to the wrappee properties
in addition, the wrapper has a property "buffering" which is true once any of its buffered properties is different from the wrappee. In this state, the changes can be either committed or canceled
Below is an excerpt of AlbumModel (nearly all minus validation) which might give you an idea. Note that BindingGroupBean is a slightly modified BindingGroup which maps internal state to a bean property "dirty" to allow binding of "buffering". You can find it in the incubator as well as a complete application BAlbumBrowser (an implementation of Fowler's classical example in terms of BeansBinding)
/**
* Buffered presentation model of Album.
*
*/
#SuppressWarnings("rawtypes")
public class AlbumModel extends Album {
#SuppressWarnings("unused")
private static final Logger LOG = Logger.getLogger(AlbumModel.class
.getName());
private Album wrappee;
private BindingGroupBean context;
private boolean buffering;
public AlbumModel() {
super();
initBinding();
}
#Action (enabledProperty = "buffering")
public void apply() {
if ((wrappee == null))
return;
context.saveAndNotify();
}
#Action (enabledProperty = "buffering")
public void discard() {
if (wrappee == null) return;
context.unbind();
context.bind();
}
private void initBinding() {
initPropertyBindings();
initBufferingControl();
}
private void initBufferingControl() {
BindingGroup bufferingContext = new BindingGroup();
// needs change-on-type in main binding to be effective
bufferingContext.addBinding(Bindings.createAutoBinding(UpdateStrategy.READ,
context, BeanProperty.create("dirty"),
this, BeanProperty.create("buffering")));
bufferingContext.bind();
}
/**
* Buffer wrappee's properties to this.
*/
private void initPropertyBindings() {
context = new BindingGroupBean(true);
context.addBinding(Bindings.createAutoBinding(UpdateStrategy.READ_ONCE,
wrappee, BeanProperty.create("artist"),
this, BeanProperty.create("artist")));
context.addBinding(Bindings.createAutoBinding(UpdateStrategy.READ_ONCE,
wrappee, BeanProperty.create("title"),
this, BeanProperty.create("title")));
// binding ... hmm .. was some problem with context cleanup
// still a problem in revised binding? Yes - because
// it has the side-effect of changing the composer property
// need to bind th composer later
context.addBinding(Bindings.createAutoBinding(UpdateStrategy.READ_ONCE,
wrappee, BeanProperty.create("classical"),
this, BeanProperty.create("classical")));
context.addBinding(Bindings.createAutoBinding(UpdateStrategy.READ_ONCE,
wrappee, BeanProperty.create("composer"),
this, BeanProperty.create("composer")));
context.bind();
}
public void setAlbum(Album wrappee) {
Object old = getAlbum();
boolean oldEditEnabled = isEditEnabled();
this.wrappee = wrappee;
context.setSourceObject(wrappee);
firePropertyChange("album", old, getAlbum());
firePropertyChange("editEnabled", oldEditEnabled, isEditEnabled());
}
public boolean isEditEnabled() {
return (wrappee != null); // && (wrappee != nullWrappee);
}
public boolean isComposerEnabled() {
return isClassical();
}
/**
* Overridden to fire a composerEnabled for the sake of the view.
*/
#Override
public void setClassical(boolean classical) {
boolean old = isComposerEnabled();
super.setClassical(classical);
firePropertyChange("composerEnabled", old, isComposerEnabled());
}
public boolean isBuffering() {
return buffering;
}
public void setBuffering(boolean buffering) {
boolean old = isBuffering();
this.buffering = buffering;
firePropertyChange("buffering", old, isBuffering());
}
/**
* Public as an implementation artefact - binding cannot handle
* write-only properrties? fixed in post-0.61
* #return
*/
public Album getAlbum() {
return wrappee;
}
}