JavaFX ToggleGroup not functioning properly with accelerators (RadioMenuItem) - javafx

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.

Related

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>

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.

flex autocomplete change/textEvent not fired

I am trying to make flex autocomplete change its dataprovider at runtime based on user input.
<components:AutoComplete change="change()" id="ac" dataProvider="{_a1}" width="100" />
<s:Button label="change" click="change()" />
private function change():void
{
Alert.show("sdsd");
//Alert.show(event.text);
//ac.dataProvider = _a2;
}
when I click the button, the function get fired, but when i input the text, it doesnt fire.
I was trying to use both change and textEvent, however, none of them are fired.
I wonder if there is any way that i can do this?
Thanks in advance.

How add a global "IsReadOnly" style to all DataGridTextColumns

I currently have a ResourceDictionary file for my WPF application, which pretty much adds every style that I could possibly want throughout all of my application's DataGrids.
Except one.
How can I add a global "IsReadOnly" setter, for all of my DataGrid's DataGridTextColumn columns ?
Basically, I use a few DataGrids, and if I want to display read-only data in a particular column, I'll just display the data using a DataGridTextColumn:
<WPFtoolkit:DataGridTextColumn Binding="{Binding Path=DOB,StringFormat='dd/MMM/yyyy'}" Header="DOB" Width="120" />
However, if I have a column which has editable data, then I'll use a DataGridTemplateColumn instead.
<WPFtoolkit:DataGridTemplateColumn xHeader="Department Name" >
<WPFtoolkit:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox Width="175"
ItemsSource="{Binding Source={StaticResource AllDepartmentsDataSource}}"
SelectedValue="{Binding DepartmentID}" SelectedValuePath="DepartmentID" DisplayMemberPath="DepartmentName"
VerticalAlignment="Center"
>
</ComboBox>
</DataTemplate>
</WPFtoolkit::DataGridTemplateColumn.CellTemplate>
</tWPFtoolkit:DataGridTemplateColumn>
The problem is, for every one of my DataGridTextColumns, I specifically need to add the IsReadOnly parameter, otherwise the user can (incorrectly) edit that data:
<WPFtoolkit:DataGridTextColumn IsReadOnly="True" Binding="{Binding Path=DOB,StringFormat='dd/MMM/yyyy'}" Header="DOB" Width="120" />
Is there a way to add this "IsReadOnly" setting globally, in the ResourceDictionary file, to all of my DataGridTextColumns...?
I can add global styles to DataGrid, DataGridColumnHeader, DataGridRow, and DataGridCell, but if I try to define a style with a TargetType of DataGridTextColumn, then Visual Studio complains that DataGridTextColumn is not derived from a FrameworkElement or FrameworkContentElement.
<Style TargetType="{x:Type WPFToolkit:DataGridTextColumn}">
<Setter Property="IsReadOnly" Value="True"/>
</Style>
I can add IsReadOnly to the Grid style, but this makes all columns uneditable !
Can anyone think of an quick and easy way to add this simple property to the DataGridTextColumns ?
Update:
My solution has been (reluctantly) to add a Loaded handler to each of my DataGrids, which runs this code:
void grdGrid_Loaded(object sender, RoutedEventArgs e)
{
DataGrid dg = (DataGrid)sender;
foreach (DataGridColumn col in dg.Columns)
{
DataGridTextColumn textCol = col as DataGridTextColumn;
if (textCol != null)
{
textCol.IsReadOnly = true;
}
else
{
// This DataGridColumn isn't of type "DataGridTextColumn", so do nothing.
}
}
}
You could, of course, put this in your own DataGrid-inherited control, rather than repeating it for each of your DataGrids.
(Sigh.)
Why didn't MS make IsReadOnly an attachable property..? It would've made life so much easier!

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