JavaFX bidirectional binding of Array<String> to multiple TextFields - javafx

I need your help. In my entity model class I have currently
protected ObservableList<Double> marketSize = FXCollections.observableArrayList();
ListProperty<Double> marketSizeProperty = new SimpleListProperty<Priority>(marketSize);
In the UI I have a fixed number of 5 TextFields like so
In the fxml file
<TextField fx:id="sizeLYTextField" GridPane.columnIndex="2" GridPane.rowIndex="3">
<TextField fx:id="sizeLY1TextField" GridPane.columnIndex="3" GridPane.rowIndex="3">
<TextField fx:id="sizeLY2TextField" GridPane.columnIndex="4" GridPane.rowIndex="3">
and so on.
Is there a way to bind each of the TextFields in the UI to a different element of the array? Like
sizeLYTextField.textProperty().bindBidirectional(model.marketSizeProperty.get(0));
what is obviously not working.
I thought about an array of StringProperties, but since I have a number of rows beneath each other, each having 5 TextFields I would have quite a number of single StringProperty boiler plate code. Maybe TextFields are not the ideal UI element?! Let me know what you think.
Thank you in advance.

Related

JavaFX ToggleGroup not functioning properly with accelerators (RadioMenuItem)

I am working with a JavaFX Menu with several RadioMenuItems. These items are in a toggle group, and work correctly when clicked with a mouse. I need hotkeys on these items, and would like them displayed in the dropdown, so added accelerators. These work as well, but if the hotkey combination for an already selected item is used again, that item is deselected, leaving no selected options in the ToggleGroup. The desired behavior would be to leave the item selected if the same combination is used again.
I've tried several versions of trying to catch when the ToggleGroup selected value is null, but since the deselect call always fires before the select call, there is no way to know whether the deselect call is valid or not for manually overriding the behavior.
FXML snippit
<Menu >
<RadioMenuItem fx:id="item1" text="item 1" >
<toggleGroup>
<ToggleGroup fx:id="theToggleGroup" />
</toggleGroup>
<accelerator>
<KeyCodeCombination alt="UP" code="d" control="DOWN" meta="UP" shift="UP" shortcut="UP" />
</accelerator>
</RadioMenuItem>
<RadioMenuItem fx:id="item2" text="item 2" toggleGroup="$theToggleGroup">
<accelerator>
<KeyCodeCombination alt="UP" code="b" control="DOWN" meta="UP" shift="UP" shortcut="UP" />
</accelerator>
</RadioMenuItem>
</Menu>
Java
theToggleGroup.selectedToggleProperty().addListener((observable, oldValue, newValue) -> doStuffBasedOnSelectedToggle(newValue));
// null pointer on newValue when nothing is selected (should never happen in a ToggleGroup)
Alternatively, I tried to set the
onAction="#doStuffBasedOnSelectedToggle"
in the FXML, but the problem remains that if you select the same one twice, it is deselected instead.
Mimic this behavior with the above code by going back and forth between hotkey 'Ctrl+B' and 'Ctrl+D' with no issues. Issue arises with 'Ctrl+D' followed by 'Ctrl+D' again, or 'Ctrl+B' followed by 'Ctrl+B' etc.
If there's no fix for this / not supported operation in Java for some reason, I can change to CheckMenuItem and do a manual implementation of Toggle functionality, but that seems unreasonable.
The workaround that I found to not have issues is a change to the onAction of each RadioMenuItem. This needs to happen in the Java, not in the FXML.
item1.setOnAction(event -> {
item1.setSelected(true);
doStuffBasedOnSelectedToggle();
}
item2.setOnAction(event -> {
item2.setSelected(true);
doStuffBasedOnSelectedToggle();
}
With this code, they appropriately toggle back and forth, and will not toggle off if the same one is selected twice.
The root cause of the original problem seems to be buried in the Java library
com.sun.javafx.scene.control.ContralAcceleratorSupport.java -> doAcceleratorInstall(...)
This method handles RadioMenuItems and CheckMenuItems identically by toggling them. The correct behavior would've been to toggle CheckMenuItem and set RadioMenuItem to true.

A button that (when pressed) creates an combobox in its place and moves itself down to the row below

Long story short:
I have an assignment that is going to interact with older people (patients) for medical purposes.
I have designed everything in scenebuilder and I am stuck at the moment.
The patients are going to fyll in some medical data, some days is going to require more input than others. Input such as heart rate, blood sugar, how they slept that night and so on.
I'm going to need a system that is adaptable since each day can vary on how much they want to fyll in. (depending on them and/or their doctor)
This is how the program is right now:
The first 2-3 rows in the gridpane is my focus right now.
what i want is that when i press the ADD button, it will create an combobox in its plane and movie itself down a row in the gridpane.
I dont actually know if it good to do this with a gridpane or if anyone has any other recomendations ( all appreciated).
you can see that in the rows 6-9 are hardcoded what input is what. Since these options may change with time i will need to make it adjustable. That is why my first combobox reads its different inputpotions from the database.
My attempt so far:
#FXML
GridPane gridPane;
#FXML
Button addButton;
#FXML
ComboBox<String> addedCombo;
#FXML
String amountCombo;
int count = 0;
#FXML
public void addComboBoxButton() {
amountCombo = "addedCombo";
addedCombo = new ComboBox();
addedCombo.setName(amountCombo + count);
count++;
}
Im trying to create a function that created an combobox evertyime the ADDbutton is pressed and then add count to it.
For example if you press it 3 times you should've created 3 comboboxes that are named:
addedCombo0, addedCombo1, addedCombo2
The error i get is shown on setName() and it says:
The method setName(String) is undefined for the type ComboBox
This is how it should look like after the ADDbutton is pressed
Thank you for your time!
I would recommend to use a separate GridPane(or VBox) for handling the ComboBoxes within the grid. This way everytime when you add a comboBox, you dont need to alter the row numbers of all the rows down the grid.
Something like..
<GridPane>
<children>
<VBox GridPane.rowIndex="0" GridPane.columnIndex="0" GridPane.columnSpan="2" alignment="CENTER_LEFT">
<!-- Keep adding your combo boxes here -->
</VBox>
<Button text="Add" GridPane.rowIndex="1" GridPane.columnIndex="0" />
<Label text="Heart Rate" GridPane.rowIndex="2" GridPane.columnIndex="0" />
<TextField GridPane.rowIndex="2" GridPane.columnIndex="1" />
<!-- Followed by your other rows.....>
</children>
</GridPane>

JavaFX TabPane : Why is the content not refreshed after selecting different tab?

So I am currently working with tabpane in JavaFx for the first time.
After it didn't work as expected I reduced the tabpanes content one bit at a time until it looked like this:
<ScrollPane prefWidth="Infinity" prefHeight="Infinity" fitToWidth="true" xmlns:fx="http://javafx.com/fxml"
fx:controller="de.Cos4U.ui.scene.control.CosplayView">
<VBox alignment="top_left" minWidth="200" prefHeight="200" spacing="5">
<TabPane prefHeight="Infinity" minHeight="Infinity">
<tabs>
<Tab text="Tab1">
<content>
<Label text="test"/>
</content>
</Tab>
<Tab text="Tab2">
<content>
<Label text="tests2"/>
</content>
</Tab>
</tabs>
</TabPane>
</VBox>
</ScrollPane>
After selecting "Tab2", the content area still shows the label "test". It is refreshed as soon as you scroll. It's a this simple tabpane, so why doesn't it refresh its content?
Edit: I am using 1.8u74 right now. I edited the fxml so now its as complete as needet but as reduced as possible.
The controller doesn't affect the tab pane in any way so. So far it handles simple user inputs. After removing anything i don't need for this example, it is reduced to 0.
I used to have the same problem. I don't have the exact reason why it doesn't update correctly, but if you want to force a refresh of the Layout, you can request it instead of using a workaround like changing the size.
requestLayout();
Suppose you want to set the content of a tab inside your controller.
class Panel extends TabPane {
private Tab tab;
public void setTabContent(Node container) {
tab.setContent(container);
getSelectionModel().select(tab);
requestLayout(); // to force refresh the layout
}
}
You then call this method from your controller.

Adding RadioMenuItem to ToggleGroup in FXML

Somewhere inside my FXML, I have this:
<fx:define>
<ToggleGroup fx:id="toggleGroup1"/>
</fx:define>
<Menu fx:id="toggleMyView" text="%MyView">
<items>
<RadioMenuItem text="%hide"
selected="true"
onAction="#handleLoadComponent"
toggleGroup="toggleGroup1"/>`
Somehow I get the error:
Unable to coerce toggleGroup1 to class javafx.scene.control.ToggleGroup
But why?
What I'm trying to do is to create a Menu containing several of RadioMenuItem which are all in the same ToggleGroup. How can I add them to the toggle group in my FXML file?
You have to write
toggleGroup="$toggleGroup1"
If you are using SceneBuilder then set the field ToggleGroup in properties to the name of the group.

SL4 Binding problem with WCF RIA Services

Basic problem: How do I bind a textbox to the selected item of a combobox who's itemsource is the result of a LINQ query on a WCF RIA Services Domain context.
Additional requirement: When the selected item is changed the binding should update.
Additional requirement: The binding should be two way.
My solution so far:
After the user clicks to create a new item, new items are created and added to the object set but not persisted yet (one for each language). After that this code runs. The combobox is supposed to allow the user to select one of the created items, which is displayed as it's corresponding language. The bound textboxes should allow the user to edit the item.
Code behind:
//Setup the combobox
LanguageComboBox.ItemsSource = dc.GeneralStatistics.Where(g => g.RelatedResourceId.Equals(guid));
LanguageComboBox.DisplayMemberPath = "Language.LanguageName";
LanguageComboBox.SelectedItem = dc.GeneralStatistics.First(g => g.Language.LanguageName.Equals("English"));
//Setup the textboxes
this.StatisticsText.DataContext = (LanguageComboBox.SelectedItem as GeneralStatistics).Text;
this.ShortDescriptionText.DataContext = (LanguageComboBox.SelectedItem as GeneralStatistics).ShortDescription;
XAML CODE:
<ComboBox x:Name="LanguageComboBox" />
<TextBox x:Name="ShortDescriptionText" Text="{Binding}" />
<TextBox x:Name="StatisticsText" Text="{Binding}" />
The problem with my solution:
It does not work, because when I change the selection in the combobox the textboxes do not update. I could implement the selection changed event handler and manually update the textboxes, but that would defeat the purpose of binding the textboxes. What is the best practice here?
You can simplify the code by doing the following.
Code behind:
LanguageComboBox.DataContext = dc.GeneralStatistics.Where(g => g.RelatedResourceId.Equals(guid));
XAML:
<ComboBox x:Name="LanguageComboBox" />
<TextBox x:Name="ShortDescriptionText" Text="{Binding ElementName=LanguageComboBox, Path=SelectedItem.ShortDescription}" />
<TextBox x:Name="StatisticsText" Text="{Binding ElementName=LanguageComboBox, Path=SelectedItem.LongDescription}" />

Resources