I am working on a project that does not need database connection. I have a trouble in filling value in a combo-box from jtable column. Please i need help.
public void Fillcombo1() {
DefaultTableModel model = (DefaultTableModel)jTable4.getModel();
cmbSearch2.addItem((String)model.getValueAt(jTable4.getSelectedColumn(), 0));
}
Related
I am building an application using JavaFX. What I am trying to do is generate a message according to the user input values. So there are one text-field and one combo-box and one check-box per row and there are many rows like the following.
Let's say I will generate three different messages according to the user values. So I need to check whether those fields are empty or not and check each field's value to generate a specific message. Checking fields are okay for just three rows like the above. But I have 10 fields. So I have to check each and generate or append my own message. And also if the user checked the check-box need to group all checked row values. So what I am asking is there any good way (best practice) to achieve what I need or an easy one also? I have tried with HashMap and ArrayList. But those are not working for this.
Really appreciate it if anybody can help me. Thanks in advance.
I would probably recommend a custom node that you create on your own like below. This example is not supposed to have the same functionality as your application but just to show how to create and use custom nodes. I kept your idea in mind when creating this example it has your textfield combobox and checkbox and they are a little easier to manage. Give it a run and let me know if you have any questions
public class Main extends Application {
#Override
public void start(Stage stage) {
VBox vBox = new VBox();
vBox.setAlignment(Pos.CENTER);
ArrayList<String> itemList = new ArrayList<>(Arrays.asList("Dog", "Cat", "Turkey"));
ArrayList<HBoxRow> hBoxRowArrayList = new ArrayList<>();
for (int i = 0; i<3; i++) {
HBoxRow hBoxRow = new HBoxRow();
hBoxRow.setComboBoxValues(FXCollections.observableList(itemList));
hBoxRowArrayList.add(hBoxRow);
vBox.getChildren().add(hBoxRow.gethBox());
}
Button printTextfieldsButton = new Button("Print Textfields");
printTextfieldsButton.setOnAction(event -> {
for (HBoxRow hBoxRow : hBoxRowArrayList) {
System.out.println("hBoxRow.getTextFieldInput() = " + hBoxRow.getTextFieldInput());
}
});
vBox.getChildren().add(printTextfieldsButton);
stage.setScene(new Scene(vBox));
stage.show();
}
//Below is the custom Node
public class HBoxRow {
HBox hBox = new HBox();
ComboBox<String> comboBox = new ComboBox<>();
TextField textField = new TextField();
CheckBox checkBox = new CheckBox();
public HBoxRow(){
hBox.setAlignment(Pos.CENTER);
textField.setPrefWidth(150);
comboBox.setPrefWidth(150);
checkBox.setOnAction(event -> {
textField.setDisable(!textField.isDisabled());
comboBox.setDisable(!comboBox.isDisabled());
});
hBox.getChildren().addAll(checkBox, textField, comboBox);
}
public void setComboBoxValues(ObservableList observableList) {
comboBox.setItems(observableList);
}
public HBox gethBox(){
return hBox;
}
public String getTextFieldInput(){
return textField.getText();
}
}
}
I am writing a JavaFX app where a series of messages appear in a TableView. When a new message appears, its row in the table should be highlighted, meaning its background color should be orange or something. Once the user clicks it, the background color should clear, acknowledging the message was read. Should be simple.
I've done enough research to realize that I need to use a rowFactory to set or clear a row's background. But I'm struggling with the mechanics of setRowFactory(). The documentation on Oracle is over my head, and every example I pull up online seems radically different than the last one.
Here's what I have:
public class Message {
private boolean readOnce;
private int date;
private String msg;
public Message(int date, String msg, String msg2){
this.readOnce = false;
this.date = date;
this.msg = msg;
}
public boolean isReadOnce() {
return readOnce;
}
public void setReadOnce(){
readOnce = true;
}
// ...and more standard getters & setters here...
}
The TableView is set up in the main controller:
#FXML TableView<Message> messageTable;
#FXML TableColumn<Message, Integer> Col1;
#FXML TableColumn<Message, String> Col2;
ObservableList<Message> tableItems;
// ...
// Setting up the Table:
PropertyValueFactory<Message, Integer> dateProperty = new PropertyValueFactory<Message, Integer>("date");
PropertyValueFactory<Message, String> msgProperty = new PropertyValueFactory<Message, String>("msg");
Col1.setCellValueFactory( dateProperty );
Col2.setCellValueFactory( msgProperty );
messageTable.setItems( tableItems );
// If we click an item in the table: messageTable.getSelectionModel().selectedItemProperty().addListener((obs, oldSelection, newSelection) -> {
if (newSelection != null) {
System.out.println("Troubleshoot: You clicked: "+newSelection.getMsg());
newSelection.setReadOnce(true);
}
});
And if I want to add a new message to the table, I just add it into the observable list:
public void addMsg(int num, String msg){
tableItems.add(new Message(num, msg));
}
So far, pretty easy. But I'm all thumbs when it comes to the rowFactory:
messageTable.setRowFactory(messageTable -> {
TableRow<Message> row = new TableRow<>();
ObjectProperty<Message> opMsg = row.itemProperty();
Message tmpMsg = opMsg.get();
if(!tmpMsg.isReadOnce()){
row.getStyleClass().add("highlight-message"); // defined in CSS
} else {
row.getStyleClass().add("clear-message"); // defined in CSS
}
return row;
});
To be very honest, I have no idea what I'm doing here. I understand that the rowFactory takes in the entire table and regenerates each row one-by-one. What I don't understand is how does the RowFactory code examine each Message in the table and how can I access them? Originally I thought these line might allow me to see the Message within the row:
TableRow<Message> row = new TableRow<>();
ObjectProperty<Message> opMsg = row.itemProperty();
Message tmpMsg = opMsg.get();
But when I debug the code, tmpMsg == NULL. So that's a big fat dead end.
Anyone see what I'm doing wrong? I've been researching this for about a week, getting absolutely no-where. Any help anyone can offer is wildly appreciated.
Many thanks,
-RAO
TableRows are created by TableView to fill it's viewport and contain TableCells. At the time they are created the item property still contains the default value null. You could register a listener to that property but usually I prefer overriding the updateItem method of a cell.
Also using PseudoClass is simpler than using style classes. New items can be assigned to a row; this could result in the same style class being added multiple times and even both style classes could be added to the same cell. PseudoClasses however can be switched on/of without the need to take care of removing other classes.
final PseudoClass highlightMessage = PseudoClass.getPseudoClass("highlight-message");
messageTable.setRowFactory(messageTable -> new TableRow<Message>() {
{
selectedProperty().addListener((o, oldVal, newVal) -> {
if (newVal) {
Message item = getItem();
if (item != null) {
item.setReadOnce();
pseudoClassStateChanged(highlightMessage, false);
}
}
});
}
#Override
protected void updateItem(Message item, boolean empty) {
super.updateItem(item, empty);
pseudoClassStateChanged(highlightMessage, item != null && !item.isReadOnce());
}
});
In a CSS stylesheet you could use rules like this:
.table-row-cell:filled {
/* style for non-highlighted rows */
}
.table-row-cell:filled:highlight-message {
/* style for highlighted rows */
}
Note that this does not allow you to programmatically alter the read state. It updates the state on selecting a cell. You could add a BooleanProperty to Message or use a ObservableSet to store the highlighted messages and update the state of cells from a listener if you need to programmatically update the readOnce property. In the latter case you do not need to store a readOnce property in the Message itself...
How can I prevent column width resizing? I have a table, and I'm wondering if I can simply disable the option to drag to resize width of the columns. Is there any way to do this, or will I have to manually set the max and min width of each column just so that it can't be manipulated by the user? The problem with this is that the text in the title/header of my table is smaller than what's in the row underneath it. Setting the width to the column width forces it smaller, and then you can't see the info in the cell underneath.
Also, if there's a way to disable rearranging the columns, that'd be nice too. I basically want what I have set to not be changed at all. If this can't be done, I might just replace each table with an image of itself, so that it can't be manipulated at all.
Prevent Resizing Columns
From here, we can know that setResizable(boolean) allows you to choose whether the user can resize a column. Setting the max and min width to the same value does prevents the user from resizing the column, but not a preferred method. Also, the user will see the resizing cursor but not the default cursor when attempting to resize the column.
Prevent Reordering Columns
JavaFX 9
For preventing the user from reordering the columns, there isn't a straight-forward solution until JavaFX 9, which introduces setReorderable(boolean), isReorderable(), reorderableProperty methods, and the reorderable field in the TableColumnBase class. Here is a snippet of the source code:
package javafx.scene.control;
//some imports and JavaDoc comments
#IDProperty("id")
public abstract class TableColumnBase<S,T> implements EventTarget, Styleable {
//some code
// --- Reorderable
/**
* A boolean property to toggle on and off the 'reorderability' of this column
* (with drag and drop - reordering by modifying the appropriate <code>columns</code>
* list is always allowed). When this property is true, this column can be reordered by
* users simply by dragging and dropping the columns into their desired positions.
* When this property is false, this ability to drag and drop columns is not available.
*
* #since 9
*/
private BooleanProperty reorderable;
public final BooleanProperty reorderableProperty() {
if (reorderable == null) {
reorderable = new SimpleBooleanProperty(this, "reorderable", true);
}
return reorderable;
}
public final void setReorderable(boolean value) {
reorderableProperty().set(value);
}
public final boolean isReorderable() {
return reorderable == null ? true : reorderable.get();
}
//some code
}
If your application bases on JavaFX 9, then you are lucky. Simply invoke setReorderable(false) on your desired table column and there you go.
JavaFX 8
If your application bases on JavaFX 8 or older versions, you can use com.sun.javafx.scene.control.skin.TableHeaderRow, which has isReordering, setReordering, reorderingProperty methods, and reorderingProperty field. Here is a snippet of the source code:
package com.sun.javafx.scene.control.skin;
//some imports and JavaDoc comments
public class TableHeaderRow extends StackPane {
//some code
private BooleanProperty reorderingProperty = new BooleanPropertyBase() {
#Override protected void invalidated() {
TableColumnHeader r = getReorderingRegion();
if (r != null) {
double dragHeaderHeight = r.getNestedColumnHeader() != null ?
r.getNestedColumnHeader().getHeight() :
getReorderingRegion().getHeight();
dragHeader.resize(dragHeader.getWidth(), dragHeaderHeight);
dragHeader.setTranslateY(getHeight() - dragHeaderHeight);
}
dragHeader.setVisible(isReordering());
}
#Override
public Object getBean() {
return TableHeaderRow.this;
}
#Override
public String getName() {
return "reordering";
}
};
public final void setReordering(boolean value) { reorderingProperty().set(value); }
public final boolean isReordering() { return reorderingProperty.get(); }
public final BooleanProperty reorderingProperty() { return reorderingProperty; }
//some code
}
The methods and fields work the same as the one in TableColumnBase in JavaFX 9, just with different names.
You want to obtain the TableHeaderRow object as a children of the skin of the TableView:
TableView<MyType> table = new TableView<MyType>();
//some code
//DISPLAY THE TABLE OR GETSKIN WILL RETURN NULL
com.sun.javafx.scene.control.skin.TableHeaderRow header = null;
for (Node node : table.getSkin().getChildren())
if (node instanceof com.sun.javafx.scene.control.skin.TableHeaderRow)
header = (com.sun.javafx.scene.control.skin.TableHeaderRow) node;
if (header == null); //Table not rendered
header.setReorderable(false);
You must have the TableView rendered before accessing the skin, because the TableViewSkin obtained from TableView.getSkin() is a visual representation of user interface controls. As the official JavaFx JavaDoc says, Skin is a
Base class for defining the visual representation of user interface
controls by defining a scene graph of nodes to represent the skin. A
user interface control is abstracted behind the Skinnable interface.
Therefore, the Skin will be null if the TableView is not rendered because there is nothing visual to represent.
Note that the second method cannot be used in Java 9 or later due to the modules in Java API block your access towards any sun packages.
EDIT:
In JavaFX 8 or older, there is a method called impl_setReorderable(boolean) which is deprecated, but works flawlessly, pretty much same as setReorderable(boolean) in JavaFX 9.
My TableView is not updating, do i need a listener ?
(m is my Model)
#FXML
private TableView<Mitarbeiter> mitarbeiter;
ObservableList<Mitarbeiter> data =
FXCollections.observableArrayList(m.getMitarbeiterListe()
);
mitarbeiter.setItems(data);
public ArrayList getMitarbeiterListe(){
return mitarbeiterliste;
}
In a new Stage i add some Mitarbeiter to the List in my Model
m.addMitarbeiterToList(mitarbeiter)
public void addMitarbeiterToList(Mitarbeiter mitarbeiter){
mitarbeiterliste.add(mitarbeiter);
}
But the TableView in the other Stage is not updating the new data.
In the end, is the ObservableList not pointed to the ArrayList from the Model ?
Instead of adding items to mitarbeiterliste (which is an ArrayList, and is not observable), add them to data, which is the ObservableList holding the items for the table. The TableView observes this list and automatically updates the view when the list contents changes.
The context of your code snippets is not very clear, but you would do something like
public ArrayList getMitarbeiterListe(){
return data;
}
or instead of
mitarbeiterliste.add(mitarbeiter);
do
data.add(mitarbeiter);
I have a requirement in my program that the object bound (from ViewModel) in a Combobox is updated as soon as an item is selected in the combobox. Currently, the object only updates once the edit is committed by either pressing Enter or leaving the cell. The user does not want the extra step.
My thought would be to have the act of selecting an item in the combobox trigger the CommitEdit() method and then CancelEdit(). However, I cannot seem to find a way to hook into the SelectionChanged event for the DataGridComboBoxColumn as it is not available.
Other suggestions have been to listen in the viewmodel for a property change event but the property is not changed until the Cell Edit is finished.
Can anyone think of a way to cause the selection of a new item (index) in a DataGridCombobox to close the edit of the cell as if the user pressed Enter or left the cell?
NOTE: I cannot use .NET 4.5 due to customer limitations.
I've had similar issue but i just found out the solution using attached property, This may not exactly fix your problem but it will help in datagrid selection changed issue.
Below is the attached property and handler methods
public static readonly DependencyProperty ComboBoxSelectionChangedProperty = DependencyProperty.RegisterAttached("ComboBoxSelectionChangedCommand",
typeof(ICommand),
typeof(SpDataGrid),
new PropertyMetadata(new PropertyChangedCallback(AttachOrRemoveDataGridEvent)));
public static void AttachOrRemoveDataGridEvent(DependencyObject obj, DependencyPropertyChangedEventArgs args)
{
DataGrid dataGrid = obj as DataGrid;
if (dataGrid != null)
{
if (args.Property == ComboBoxSelectionChangedProperty)
{
dataGrid.SelectionChanged += OnComboBoxSelectionChanged;
}
}
else if (args.OldValue != null && args.NewValue == null)
{ if (args.Property == ComboBoxSelectionChangedProperty)
{
dataGrid.SelectionChanged -= OnComboBoxSelectionChanged;
}
}
}
private static void OnComboBoxSelectionChanged(object sender, SelectionChangedEventArgs args)
{
DependencyObject obj = sender as DependencyObject;
ICommand cmd = (ICommand)obj.GetValue(ComboBoxSelectionChangedProperty);
DataGrid grid = sender as DataGrid;
if (args.OriginalSource is ComboBox)
{
if (grid.CurrentCell.Item != DependencyProperty.UnsetValue)
{
//grid.CommitEdit(DataGridEditingUnit.Row, true);
ExecuteCommand(cmd, grid.CurrentCell.Item);
}
}
}
SpDataGrid is the custom control that i inherited from data grid.
I added below style in generic.xaml as i use the resourcedictionary for style (you can certainly add inside the datagrid).
<Style TargetType="{x:Type Custom:SpDataGrid}">
<Setter Property="Custom:SpDataGrid.ComboBoxSelectionChangedCommand" Value="{Binding ComboBoxSelectionChanged}"/>
</Style>
ComboBoxSelectionChanged is the command in my viewmodel. OnComboBoxSelectionChanged i commented the commitedit because in my case the values were already updated.
Let me know if anything is not clear or any questions. Hope this helps.