JavaFX Combobox row with ImageView+Label - combobox

I have a combobox which is fill whith a list of Ejercicio class objects (Ejercicio contains id, name..).
ComboBox lstPrueba=new ComboBox<Ejercicio>();
//I get a list of Ejercicio from DAO an add all to the combobox
lstPrueba.getItems().addAll(new EjercicioDAO().obtenListaEjercicios("something"));
I need that all combobox row´s have a ImageView (png Image names is base on Ejercicio id, because png name equals to Ejercicio id) plus a label (with Ejercicio´s name).
I try but not work:
class FormatoCelda extends ListCell<Ejercicio> {
#Override
public void updateItem(Ejercicio item, boolean empty) {
super.updateItem(item, empty);
Image img = new Image(getClass().getResourceAsStream("/img/"+String.valueOf(item.getId() + ".png")));
ImageView imgv = new ImageView(img);
if (item != null) {
setGraphic(imgv);
setText(item.getNombre());
} else {
setGraphic(null);
}
}
}
And in initializate method:
lstPrueba = new ComboBox<>();
lstPrueba.getItems().addAll(new EjercicioDAO().obtenListaEjercicios("something"));
lstPrueba.setCellFactory(new Callback<ListView<Ejercicio>, ListCell<Ejercicio>>() {
#Override
public ListCell<Ejercicio> call(ListView<Ejercicio> list) {
return new FormatoCelda();
}
}
);
I´m a newbie in JavaFX. I search over Internet and StackOverFlow but I can´t find a good example on filling a control list type like combobox with a image+label.
Sorry, my English isn’t very good.

Related

Vaadin-Unable to update grid container on combobox value change

I am using vaadin 7.7.7
In a grid i have a combobox as an edited item in one of the columns
as
grid.addColumn("columnProperty").setEditorField(combobox);
I need to update a property/cell in same row based on the combobox selection change
My issue is , the selection change event triggers twice, once when the combobox in clicked and second when the selection value is changed. But the updated value in next cell gets reflected on UI only first time.
Below is the code written . Any solutions?
Combobox.addValueChangeListener(new ValueChangeListener()
#Override
public void valueChange(ValueChangeEvent event) {
// below line works only first time when the combobox is clicked,but i want
//it when the item in the combobox is changed
gridContainer.getContainerProperty(editedRow,"editedColumProperty").setValue("ValueTobeUpdated");}
});
Need to update the unit column on combobox change in edited mode(before saving)
Refer below link for image
example image
You will get value change events even when the field gets value that it should show to the user. In order to get event that indicates that the user has accepted the input you should use field group (setEditorFieldGroup).
From Book of Vaadin example for grid editing:
grid.getColumn("name").setEditorField(nameEditor);
FieldGroup fieldGroup = new FieldGroup();
grid.setEditorFieldGroup(fieldGroup);
fieldGroup.addCommitHandler(new CommitHandler() {
private static final long serialVersionUID = -8378742499490422335L;
#Override
public void preCommit(CommitEvent commitEvent)
throws CommitException {
}
#Override
public void postCommit(CommitEvent commitEvent)
throws CommitException {
Notification.show("Saved successfully");
}
});
Edit
I assume that you want to connect Parameter and Unit comboboxes. I would do that with this kind of value change lister
BeanItemContainer container = new BeanItemContainer<>(
Measurement.class,
measurements);
Grid grid = new Grid(container);
grid.setEditorEnabled(true);
ComboBox parameterComboBox = new ComboBox();
ComboBox unitComboBox = new ComboBox();
parameterComboBox.addItems(Parameter.Pressure, Parameter.Temperature, Parameter.Time);
parameterComboBox.addValueChangeListener(v -> setUnits(parameterComboBox, unitComboBox));
grid.getColumn("parameter").setEditorField(parameterComboBox);
grid.getColumn("unit").setEditorField(unitComboBox);
Units could be updated like this. I think you need to preserve current value and set it back if you replace available items in the combobox.
private void setUnits(ComboBox parameterComboBox, ComboBox unitComboBox) {
Object currentValue = unitComboBox.getValue();
List<String> units = unitsForParameter(parameterComboBox.getValue());
unitComboBox.removeAllItems();
unitComboBox.addItems(units);
if (units.contains(currentValue)) {
unitComboBox.setValue(currentValue);
} else {
unitComboBox.setValue(null);
}
}
private List<String> unitsForParameter(Object value) {
if (value == null) {
return Collections.emptyList();
} else if (value == Parameter.Pressure) {
return asList("Pascal", "Bar");
} else if (value == Parameter.Temperature) {
return asList("Celcius", "Kelvin");
} else if (value == Parameter.Time) {
return asList("Second", "Minute");
} else {
throw new IllegalArgumentException("Unhandled value: " + value);
}
}

JavaFX 8: ComboBox button cell update behavior

I have a combo box which contains items of type Dog. If all items are replaced with new ones (via setAll on the ObservableList model) , the item renderer can cope with this update, while the button cell renderer cannot:
Here's a minimal example to reproduce the problem (full source incl. imports on GitHub):
public class ComboBoxRefresh extends Application {
private static final class Dog {
private final String name;
public Dog(String name) {
this.name = name;
}
}
private static final class DogListCell extends ListCell<Dog> {
#Override
public void updateItem(Dog item, boolean empty) {
super.updateItem(item, empty);
if (item == null || empty) {
setText("");
} else {
setText(item.name);
}
}
}
private static List<Dog> createThreeDogs() {
return range(0, 3).mapToObj(i -> new Dog("Buddy " + i)).collect(toList());
}
#Override
public void start(Stage stage) throws Exception {
ObservableList<Dog> items = observableArrayList(createThreeDogs());
ComboBox<Dog> comboBox = new ComboBox<>(items);
comboBox.setPrefWidth(400);
comboBox.setCellFactory(listView -> new DogListCell());
comboBox.setButtonCell(new DogListCell());
Button button = new Button("Refresh");
button.setOnAction(event -> {
List<Dog> newItems = createThreeDogs();
items.setAll(newItems);
});
VBox box = new VBox(10, comboBox, button);
box.setPadding(new Insets(10));
Scene scene = new Scene(box);
stage.setScene(scene);
stage.show();
}
}
If I add an equals implementation to the Dog class, everything works, but this is not an option in my real application.
Are there any work-arounds to enforce a proper refresh of the button cell?
It seems to be a bug. Workaround could be
button.setOnAction( event -> {
List<Dog> newItems = createThreeDogs();
items.clear();
items.addAll(newItems);
} );

JavaFx TreeView with CheckBox and ContextMenu

in the tutorial http://docs.oracle.com/javafx/2/ui_controls/tree-view.htm it explain how to create a TreeView with ContextMenu or CheckBox.
but is it possible to have both of them?
when I first copy-paste the code, I learned that I can have only one setCellFactory since they overwrite each other.
// the following two setCellFactory are copied from the tutorial
// this create TreeCell with ContextMenu
treeView.setCellFactory(new Callback<TreeView<String>,TreeCell<String>>(){
#Override
public TreeCell<String> call(TreeView<String> p) {
return new TextFieldTreeCellImpl();
//the class TextFieldTreeCellImp is a TreeCell with ContextMenu
}
});
// this create TreeCell with CheckBox
tree.setCellFactory(CheckBoxTreeCell.<String>forTreeView());
then i tried substituting TreeCell with CheckBoxTreeCell
//class TextFieldTreeCellImpl extends TreeCell<String> {
class TextFieldTreeCellImpl extends CheckBoxTreeCell<String> {
...
//TreeItem newTag = new TreeItem<String>("New tag");
CheckBoxTreeItem newTag = new CheckBoxTreeItem<String>("New tag");
but the checkbox didn't appear. it's still a normal treeview.
I think the fastest way to get a TreeView with CheckBoxes and a ContextMenu is adding the ContextMenu within the factory callback:
treeView.setCellFactory(new Callback<TreeView<String>,TreeCell<String>>() {
#Override
public TreeCell<String> call(TreeView<String> param) {
TreeCell<String> cell = CheckBoxTreeCell.<String>forTreeView().call(param);
ContextMenu menu = new ContextMenu();
MenuItem item1 = new MenuItem("Item 1");
MenuItem item2 = new MenuItem("Item 2");
EventHandler<ActionEvent> eh = new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
System.out.println("Item '" + event.getSource() +
"' of cell '" + cell + "' was clicked.");
}
};
item1.setOnAction(eh);
item2.setOnAction(eh);
menu.getItems().add(item1);
menu.getItems().add(item2);
cell.setContextMenu(menu);
return cell;
}
});
I call the usual TreeCell factory for the CheckBoxes. However, before returning the cell I add the ContextMenu.
Using the EventHandler, you can specify the clicking behaviour of the respective item.
If you have further questions or this is not a satisfying solution of you problem, feel free to contact me.

How do I create a JavaFX 8 ComboBox which can bind to a field on a seperate POJO

I have had a challenge finding anything on the following situation. Let's say I have a record called DatabaseRecord which is a POJO with several fields:
public class DatabaseRecord {
private Long recordId;
private StringProperty foreignKeyId;
private StringProperty otherValuesForMainRecord....
... setters/getters
public class LookUpDataValue {
private String recordId;
private String descriptiveText;
private String otherValues.....
....
I have a combo box which displays the selectable values for the foreignKeyId value something like this:
#FXML
ComboBox<LookUpDataValue> combobox;
combobox.setCellFactory
(new Callback<ListView<LookUpDataValue>, ListCell<LookUpDataValue>>() {
#Override
public ListCell<LookUpDataValue> call(ListView<LookUpDataValue> p) {
ListCell<LookUpDataValue> cell = new ListCell<LookUpDataValue>() {
#Override
protected void updateItem(LookUpDataValue item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setText("");
} else {
setText(item.getDescriptiveText());
}
}
};
return cell;
}
});
I would like to Bidirectionally bind the POJO DatabaseRecord.foreignKeyIdProperty to the combobox.valueProperty() - but this doesn't work since one is a string and one is a LookUpDataValue object.
I would like this combobox to be set correctly when the records are loaded, and conversely update the record when it's changed.
I would appreciate any pointers on where I can find an example of this...
I was not able to change my datamodel so I found the following solution which works.
// Handle ComboBox event.
myComboBox.setOnAction((event) -> {
LookUpDataValue selected = myComboBox.getSelectionModel().getSelectedItem();
if(selected != null){
dto.setForeignKeyId(selected.getId());
}
});
This works well. Thanks for your comments.

Winforms bind SelectedItems in listbox

Using the standard listbox I would like to bind it to a set of objects and update a bound items collection to include those selected.
So I have something like:
_pContext = new BindingSource();
_pContext.DataSource = _gpContext;
_pContext.DataMember = "ParentEntities";
_AllChildrenListBox.DataSource = _container.ChildEntities;
_AllChildrenListBox.DataBindings.Add("MySelectedItems", _pContext, "ChildEntities", false);
_allChildrenListBox is the listbox. I created a new listbox type inheriting from ListBox so I could create an alternative SelectedItems property which would then encapsulate the logic to set/unset the items.
So the question in a nutshell is: In the above, ChildEntities is a collection of "ChildEntity" objects. My list box contains all possible ChildEntity objects and I want the elements in ChildEntities to be selected, and updated when selection changed.
I have found a way around this. It's a little ugly at the moment, but works. I can refine it later.
So first bit was the binding itself.
_AllChildrenListBox.DataBindings.Add("SelectedItems2", _pContext, "ChildEntities2", true);
The key here seemed to be setting the formatting parameter to true.
Next I needed something in SelectedItems2 that would do my dirty work. This is a mess, but works. Here's the full class:
public class MyListBox : ListBox
{
private IEnumerable<object> _original;
public IEnumerable<object> SelectedItems2
{
get
{
return UpdateSet();
}
set
{
SelectItems(value);
}
}
private IEnumerable<object> UpdateSet()
{
var listSource = _original as IListSource;
IList list = null;
if (listSource != null)
{
list = listSource.GetList();
}
var iList = _original as IList;
if (list == null && iList != null)
{
list = iList;
}
if (list == null)
{
return _original;
}
foreach (var item in SelectedItems)
{
if (!list.Contains(item))
{
list.Add(item);
}
}
foreach (var item in _original.ToList())
{
if (!SelectedItems.Contains(item))
{
list.Remove(item);
}
}
return _original;
}
private void SelectItems(IEnumerable<object> items)
{
_original = items;
var hashset = new HashSet<object>();
foreach (var item in items)
{
hashset.Add(item);
}
for(var i=0;i<Items.Count;i++)
{
SetSelected(i, hashset.Contains(Items[i]));
}
}
}
Essentially I'm breaking all the rules and assuming IEnumerable hides a more tasty list based interface, if it does it uses that to changes the underlying set.
Finally, I'm using EF (Entity Framework) and you can't call the setter of a collection property, so my entity currently looks like this: Notice how I'm duplicating all the list change operations which is totally unnecessary, but I did say this was just the first working go.
public partial class ParentEntity
{
public IEnumerable<object> ChildEntities2
{
get
{
return new List<object>(ChildEntities);
}
set
{
if (value == null)
{
return;
}
foreach (var item in ChildEntities.ToList().Where(item => !value.Contains(item)))
{
ChildEntities.Remove(item);
}
foreach (var item in value)
{
var cItem = item as ChildEntity;
if (cItem != null)
{
if (!ChildEntities.Contains(item as ChildEntity))
{
ChildEntities.Add(item as ChildEntity);
}
}
}
}
}
}

Resources