I can't find a way to add a simple "String" row to my tableView.
In fact i can add a row but its content is not visible ...
Here is my code:
#FXML
private TableView<String> table;
#FXML
private TableColumn<String, String> table2;
public ObservableList<String> getLanes()
{
ObservableList<String> lanes=FXCollections.observableArrayList();
lanes.add("TEST");
return lanes;
}
Then:
table.setItems(getLanes()); //Not working
and
table.getItems().add("TEST"); //Not working
But without success.
I read that and that as well as other documentations but it did not help me to do it in this simple way.
EDIT:
Adding this line solved my problem:
table2.setCellValueFactory(param -> new ReadOnlyStringWrapper(param.getValue()));
Here is a simple application where we are trying to load a single value into a TableView column. It also shows how to set a cellValueFactory() on a table column.
tableColumn.setCellValueFactory(param -> new ReadOnlyStringWrapper(param.getValue()));
MCVE
import javafx.application.Application;
import javafx.beans.property.ReadOnlyStringWrapper;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class Main extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
TableView<String> tableView = new TableView<>();
TableColumn<String, String> tableColumn = new TableColumn<>("Name");
tableColumn.setCellValueFactory(param -> new ReadOnlyStringWrapper(param.getValue()));
tableView.getColumns().add(tableColumn);
ObservableList<String> items = FXCollections.observableArrayList("Itachi");
tableView.setItems(items);
VBox root = new VBox(tableView);
root.setAlignment(Pos.CENTER);
Scene scene = new Scene(root, 300, 275);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Related
I am trying to set the focus on the JTextPane so that when the window opens it can immediately be edited with the keyboard. However, nothing I've done seems to give the JTextPane focus on startup. Is this just an issue with using JavaFX with Swing?
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.embed.swing.SwingNode;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.ListView;
import javax.swing.*;
public class TestDialog {
#FXML
private ListView listView;
#FXML
private SwingNode node;
private ObservableList<Integer> obsList;
#FXML
public void initialize(){
JTextPane pane = new JTextPane();
SwingUtilities.invokeLater(() -> pane.requestFocusInWindow());
pane.setText("This issue is not reproducible in JDK 8 early-access build (8u172) which is yet to be released.");
node.setContent(pane);
obsList = FXCollections.observableArrayList();
for(int x = 0; x < 12; x++){
obsList.add(x);
}
listView.setItems(obsList);
node.setFocusTraversable(true);
node.requestFocus();
pane.requestFocus();
pane.grabFocus();
}
#FXML
private void removeItem(ActionEvent event) {
obsList.remove(0);
}
}
It's working now thanks to a solution from BWC_semaJ. Rather than using:
SwingUtilities.invokeLater(() -> pane.requestFocusInWindow());
I should have used:
Platform.runLater(() -> {swingNode.requestFocus();}); //Use this instead
I don't know if this helps but below is the demo program I made based on the sample code and for some reason it works for me:
import javafx.application.Application;
import javafx.application.Platform;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.embed.swing.SwingNode;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.Scene;
import javafx.scene.control.ListView;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import javax.swing.*;
public class Test extends Application {
#FXML
private ListView listView;
#FXML
private SwingNode node;
private ObservableList<Integer> obsList;
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage arg0) throws Exception {
initialize(arg0);
}
#FXML
public void initialize(Stage stage){
JTextPane pane = new JTextPane();
// The program runs the same no matter if one of the below two lines are used or if neither are used
//SwingUtilities.invokeLater(() -> pane.requestFocusInWindow());
//Platform.runLater(() -> {node.requestFocus();});
pane.setText("This issue is not reproducible in JDK 8 early-access build (8u172) which is yet to be released.");
node = new SwingNode();
node.setContent(pane);
obsList = FXCollections.observableArrayList();
for(int x = 0; x < 12; x++){
obsList.add(x);
}
listView = new ListView();
listView.setItems(obsList);
node.setFocusTraversable(true);
node.requestFocus();
pane.requestFocus();
pane.grabFocus();
StackPane root = new StackPane();
root.getChildren().add(node);
Scene scene = new Scene(root, 500, 500);
stage.setTitle("Test");
stage.setScene(scene);
stage.show();
}
#FXML
private void removeItem(ActionEvent event) {
obsList.remove(0);
}
}
I could not find any examples on the Web that show how to make hierarchical headers for JavaFX TableView, therefore, I do not provide any code. I will just post an image of what I want to achieve. Here it is:
It is highly required in many application to have such table grids. Unfortunately, I could not find any example for JavaFX. Thanks for help in advance!
It seems you are looking for nested columns.
You can add sub-columns to a main column to get hierarchical headers.
Here is a small example:
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.stage.Stage;
public class NestedColumns extends Application
{
#Override
public void start(Stage primaryStage) throws Exception
{
TableView<String> tableView = new TableView<String>();
TableColumn<String, String> nameColumn = new TableColumn<>("Name");
TableColumn<String, String> firstNameColumn = new TableColumn<>("First name");
TableColumn<String, String> lastNameColumn = new TableColumn<>("Last name");
nameColumn.getColumns().addAll(firstNameColumn, lastNameColumn);
tableView.getColumns().add(nameColumn);
Scene scene = new Scene(tableView);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
If you create a binding between a JavaFX TextField and a property, then this binding is invalidated on every keystroke, which causes a change to the text.
If you have a chain of bindings the default behavior could cause problems, because in the middle of the editing values may be not valid.
Ok, I know I could create an uni-directional binding from the property to the textfield and register a change listener to get informed when the cursor leaves the field and update the property manually if necessary.
Is there an easy, elegant way to change this behavior so that the binding is only invalidated when the editing is complete, e.g. when the cursor leaves the field?
Thanks
I think you've pretty much described the only way to do it. Here's about the cleanest way I can see to implement it (using Java 8, though it's easy enough to convert the lambdas back to be JavaFX 2.2 compatible if you need):
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.beans.binding.StringBinding;
import javafx.event.ActionEvent;
import javafx.scene.Scene;
import javafx.scene.control.TextField;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class CommitBoundTextField extends Application {
#Override
public void start(Stage primaryStage) {
TextField tf1 = new TextField();
createCommitBinding(tf1).addListener((obs, oldText, newText) ->
System.out.printf("Text 1 changed from \"%s\" to \"%s\"%n", oldText, newText));
TextField tf2 = new TextField();
createCommitBinding(tf2).addListener((obs, oldText, newText) ->
System.out.printf("Text 2 changed from \"%s\" to \"%s\"%n", oldText, newText));
VBox root = new VBox(5, tf1, tf2);
Scene scene = new Scene(root, 250, 100);
primaryStage.setScene(scene);
primaryStage.show();
}
private StringBinding createCommitBinding(TextField textField) {
StringBinding binding = Bindings.createStringBinding(() -> textField.getText());
textField.addEventHandler(ActionEvent.ACTION, evt -> binding.invalidate());
textField.focusedProperty().addListener((obs, wasFocused, isFocused)-> {
if (! isFocused) binding.invalidate();
});
return binding ;
}
public static void main(String[] args) {
launch(args);
}
}
I realize that I am a little late with a response, but thought this might be useful to someone.
When using TextFields, I often attach a TextFormatter to help validate entries. You can attach a listener to the formatters' valueProperty. That property is updated when the text is committed, rather than on every keystroke.
Here's an example of what I am talking about using a TextField specialized for integer inputs. When you make edits in the text field, the changes will be reflected in the Label when you tap Enter, lose focus by clicking the button, switch to a different window, and so on.
import javafx.application.Application;
import javafx.beans.binding.StringBinding;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.value.ObservableValue;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.control.TextFormatter;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.converter.IntegerStringConverter;
class IntTextField extends TextField {
private final IntegerProperty value;
TextFormatter<Integer> formatter;
public double getValue() {
return value.getValue();
}
public void setValue(int newValue) {
value.setValue(newValue);
}
public IntegerProperty valueProperty() {
return value;
}
public StringBinding getStringBinding () {
return value.asString();
}
IntTextField(int initValue) {
value = new SimpleIntegerProperty(initValue);
setText(initValue + "");
formatter = new TextFormatter(new IntegerStringConverter(), initValue);
formatter.valueProperty().addListener((ObservableValue<? extends Integer> obs,
Integer oldValue, Integer newValue) -> value.setValue(newValue));
setTextFormatter(formatter);
}
IntTextField() {
this(0);
}
}
public class TFBindingDemo extends Application {
#Override
public void start(Stage stage) {
stage.setTitle("TFBindingDemo");
IntTextField intTextField = new IntTextField(12345);
intTextField.setMaxWidth(150);
Label label = new Label("Type in the TextField");
label.textProperty().bind(intTextField.getStringBinding());
Button removeFocusButton = new Button("Click Here to Remove Focus");
VBox root = new VBox(20, intTextField, label, removeFocusButton);
root.setAlignment(Pos.CENTER);
root.setPadding(new Insets(20));
Scene scene = new Scene(root, 325, 200);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch();
}
}
When there is no record in any table it shows a message 'No content in table', which is by default functionality of TableView in JavaFx.
So here my question is, does the same can be possible with ListView in JavaFx ? Like, if there is no item in any ListView then it will show a message same as TableView, instead of a blank/empty fields.
You have to try this:-
listView.setPlaceholder(new Label("No Content In List"));
its 100% working....
JavaFX8 has a setPlaceholder(...) method for ListView.
In earlier versions, you need to roll your own somehow. This is a bit of a hack: it wraps the ListView in a stack pane, with a white rectangle and the placeholder displayed over the top of the list view. The placeholder and rectangle have their visible property bound, so they are only visible if the list is empty.
There may be easier ways that I'm not seeing right away...
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.ListView;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
public class ListViewPlaceholderTest extends Application {
#Override
public void start(Stage primaryStage) {
final ListView<String> listView = new ListView<>();
final IntegerProperty counter = new SimpleIntegerProperty();
final Button addButton = new Button("Add item");
addButton.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
counter.set(counter.get()+1);
listView.getItems().add("Item "+counter.get());
}
});
final Button removeButton = new Button("Remove");
removeButton.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
listView.getItems().remove(listView.getSelectionModel().getSelectedIndex());
}
});
removeButton.disableProperty().bind(Bindings.equal(listView.getSelectionModel().selectedIndexProperty(), -1));
final HBox buttons = new HBox(5);
buttons.setPadding(new Insets(10));
buttons.getChildren().addAll(addButton, removeButton);
final BorderPane root = new BorderPane();
root.setCenter(createPlaceholderForListView(listView, new Label("No content in List")));
root.setBottom(buttons);
final Scene scene = new Scene(root, 600, 400);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
private Node createPlaceholderForListView(ListView<?> listView, Node placeholder) {
final StackPane pane = new StackPane();
final Rectangle rect = new Rectangle(0, 0, Color.WHITE);
rect.widthProperty().bind(listView.widthProperty());
rect.heightProperty().bind(listView.heightProperty());
pane.getChildren().addAll(listView, rect, placeholder);
placeholder.visibleProperty().bind(Bindings.isEmpty(listView.getItems()));
rect.visibleProperty().bind(placeholder.visibleProperty());
rect.setMouseTransparent(true);
return pane ;
}
}
With fxml:
<ListView fx:id="foundContentList">
<placeholder>
<Label text="Nothing found" />
</placeholder>
</ListView>
Not entirely sure but I don't think there is a setPlaceholder method(to set the default message when no content in table) for ListView.
The workaround that I use is to create an Object in the list that indicate "No content" and show that on the listview and also disable it.
For example:
ObservableList noContent= FXCollections.observableArrayList("No content found");
ListView listView = new ListView(noContent);
listView.setDisable(true);
My question is simple: How do I iterate throught a javafx-combobox? I tried so much and nothing works. The foreach-construct does not work and combobox does not even have methods for size and single object retrieval, eliminating the possibility to use a simple-for-loop. Am I missing something?
Yes you are missing..or maybe not. But, nevertheless, just return ObservableList of objects from your ComboBox and use for each to iterate through that collection. Here is an example:
import javafx.application.Application;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ComboBox;
import javafx.scene.layout.GridPane;
import javafx.stage.Stage;
public class ComboBoxExample extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
GridPane gridPane = new GridPane();
Button button = new Button("Print items from ComboBox");
final ComboBox<String> comboBox = new ComboBox<String>();
//Add some items
comboBox.getItems().addAll("John","Jane","Laina");
button.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent actionEvent) {
//To iterate through ComboBox
ObservableList<String> items = comboBox.getItems();
for(String item : items){
System.out.println(item.toString());
}
}
});
gridPane.add(comboBox,0,0);
gridPane.add(button,0,1);
primaryStage.setScene(new Scene(gridPane,320,280));
primaryStage.show();
}
}