In JavaFX, binding comboBox items and table Column sort - javafx-2

I have a combo Box and a table-view. ComboBox items are filled with tables column-names. I want to bind comboBox item selection and the table column sort.
Example: If I select item say "Name" from comboBox which is at index 0 of comboBox, the 0th column of table is sorted.
Again if I sort a column in the table, comboBox selected item should update with the corresponding column name.
Right now i am achieving the table column sort based on comboBox item slection with below code.
private void bindComboBoxAndTableColumnSort() {
ComboBox combo = topComboBarController.getSortCombo();
combo.getSelectionModel().selectedIndexProperty().addListener(new ChangeListener<Number>() {
#Override
public void changed(ObservableValue<? extends Number> arg0,
Number oldVal, Number newVal) {
System.out.println("oldVal = "+ oldVal + " and newVal = "+ newVal);
TableColumn sortColumn = null;
SortType st = null ;
sortColumn = table.getColumns().get( newVal.intValue() ) ;
st = table.getColumns().get( newVal.intValue() ).getSortType() ;
table.getSortOrder().clear();
if(sortColumn != null){
table.getSortOrder().add(sortColumn);
sortColumn.setSortType(SortType.ASCENDING);
}
}
});
}
If someone can share some demo code, it will be helpful.

You need a second Listener which listens to the change to the changeorder of your TableView. Notice the need of the while loop to listen to the paramChange. Replace the ... with your binding to your ComboBox
tableView.getSortOrder().addListener(new ListChangeListener<TableColumn<ColumnClass, ?>>() {
#Override public void onChanged(Change<? extends TableColumn<ColumnClass, ?>> paramChange) {
while(paramChange.next()) {
if (paramChange.wasPermutated()) {
final TableColumn<ColumnClass, ?> first = paramChange.getList().get(0);
final String tableColumnName = first.getText();
...
}
}
}
});
Edit
According to the request some other approach
final ComboBox<String> box = new ComboBox<>();
table.getSortOrder().get(0).textProperty().bindBidirectional(box.valueProperty());

With below code, i am able to achieve what #thatslch suggested.
table.getSortOrder().addListener(new ListChangeListener<TableColumn<Person, ?>>(){
#Override
//public void onChanged( javafx.collections.ListChangeListener.Change<? extends TableColumn<Person, ?>> paramChange) {
public void onChanged( Change<? extends TableColumn<Person, ?>> paramChange) {
// TODO Auto-generated method stub
while(paramChange.next()) {
if (paramChange.wasAdded()) {
System.out.println("paramChanged.wasAdded() ");
ComboBox combo = topComboBarController.getSortCombo();
combo.valueProperty().bind( paramChange.getList().get(0).textProperty() );
}
}
}

Related

ObservableCollection - Changes on the dataset do not get updated

I am new to programming for winrt. I am using VS 2015, trying to do some testing on ObservableCollection to see how the change in the collection got reflected on the UI. I must have done something incorrectly. Just do not know what.
This is my model:
class MyItems
{
public int ItemID { get; set; }
public string ItemDescription { get; set; }
}
class MyItemList:List<MyItems>
{
public MyItemList()
{
Random r = new Random(DateTime.Now.Day);
for (int i = 0; i < r.Next(10)+1; i++)
{
this.Add(new MyItems() { ItemID = i + 1,
ItemDescription = string.Format("Item_{0}", i + 1) });
}
}
public ObservableCollection<MyItems> getEven()
{
return new ObservableCollection<MyItems>(this.Where(x=>x.ItemID%2==0).ToList());
}
public void AddMoreItems(int v)
{
int total = this.Count;
for (int i = 0 ; i < v; i++)
{
this.Add(new MyItems() { ItemID = total + i, ItemDescription = string.Format("Item_{0}", total+i) });
}
}
}
On MainPage.xaml, I have a button to add items to the list. I have created a listview programmatically and binding to the dataset two ways.
public sealed partial class MainPage : Page
{
static MyItemList myItems = new MyItemList();
public MainPage()
{
this.InitializeComponent();
var t = myItems.getEven();
ListView myListView = new ListView() { ItemTemplate = (DataTemplate)Resources["myItemTemplate"] };
myListView.DataContext = t;
var binding = new Binding();
binding.Source = t;
binding.Mode = BindingMode.TwoWay;
myListView.SetBinding(ListView.ItemsSourceProperty, binding);
MyGrid.Children.Add(myListView);
}
private void AddItems_Click(object sender, RoutedEventArgs e)
{
myItems.AddMoreItems(3);
}
}
When I clicked on the button, 3 more items are added but they are not reflected in my listview. Something else needs to be done beside using the ObservableCollection and set binding to twoWays?
Your problem, essentially, is here:
public ObservableCollection<MyItems> getEven()
{
return new ObservableCollection<MyItems>(this.Where(x=>x.ItemID%2==0).ToList());
}
When you query this and call ToList(), you are creating a new list that is independent of the original. If you add items to the original, it's not going to be reflected in your derived list. Think of it this way:
public ObservableCollection<MyItems> getEven()
{
var filteredList = this.Where(x=>x.ItemID%2==0).ToList()
return new ObservableCollection<MyItems>(filteredList);
}
Adding to this is not going to change the contents filteredList at all.
Additionally, creating a new ObservableCollection every time you access getEven instead of modifying an existing one means that the events when adding and deleting to the observable collection will never fire.
You are using observable collections in a fundamentally incorrect way. Why not just have MyItemList derive from ObservableCollection<T> instead of List<T>?
Collection that inherits from ObservableCollection - What are the benefits?
Also, if you're trying to filter by even/odd, you should look into ICollectionView
WPF Binding filtered ObservableCollection ICollectionView to Combobox

UIPickerViewModel GetView called infinite times for UIPickerView monotouch

I have a UIPickerView that is using a custom UIPickerViewModel to overide the view that is shown in the UIPicker View.
When I First open the UIPickerView, all goes well, I am able to select an item and what not.
If the Item selected is the first or 2nd item in the UIPicker, I don't run into any issues. However, if I select say the 3rd item, dismiss the Picker then try to select another option again, the UIPickerView never shows on the screen.
What I found out is that the GetView method on my PickerViewModel is getting called over and over again for the selected row as well as the row after. Is there anything I have done that might cause this behavior? Below is my PickerViewModel
public class AddressPickerViewModel : UIPickerViewModel
{
private ShippingView view;
public AddressPickerViewModel(ShippingView view)
{
this.view = view;
}
private AddressPickerCell currentSelectedCell;
public override UIView GetView(UIPickerView picker, int row, int component, UIView view)
{
if (view == null)
{
view = new AddressPickerCell();
var views = NSBundle.MainBundle.LoadNib(AddressPickerCell.Key, view, null);
view = Runtime.GetNSObject(views.ValueAt(0)) as AddressPickerCell;
}
var pickerCell = (AddressPickerCell)view;
pickerCell.Selected = false;
pickerCell.ShippingAddress = this.view.ViewModel.Addresses.ToArray()[row].Address;
return view;
}
public override int GetComponentCount(UIPickerView v)
{
return 1;
}
public override int GetRowsInComponent(UIPickerView pickerView, int component)
{
return this.view.ViewModel.Addresses.Count;
}
public override void Selected(UIPickerView picker, int row, int component)
{
if (this.currentSelectedCell != null)
{
this.currentSelectedCell.Selected = false;
}
view.ViewModel.SelectedAddress=
this.view.ViewModel.Addresses.ElementAt(picker.SelectedRowInComponent(0));
var cell = picker.ViewFor(row, component) as AddressPickerCell;
if (cell != null)
{
this.currentSelectedCell = cell;
this.currentSelectedCell.Selected = true;
}
}
public override float GetComponentWidth(UIPickerView picker, int component)
{
if (component == 0)
return 300f;
else
return 40f;
}
public override float GetRowHeight(UIPickerView picker, int component)
{
return 140f;
}
}

Extra elements in table are always visible, even without underlying data

Here is my problem. I have table with normal text columns and 2 columns with dropdowns and one with checkboxes. This is my callback for cell factory for dropdown columns :
Callback<TableColumn<Person, String>, TableCell<Person, String>> dropdownConditionCellFactory =
new Callback<TableColumn<Person, String>, TableCell<Person, String>>() {
#Override
public TableCell call(TableColumn p) {
Tools.Tables.ComboBoxCell<partCondition> cell = new Tools.Tables.ComboBoxCell<partCondition>(partConditionList)
return cell;
}
};
And class for this cell factory:
public static class ComboBoxCell extends TableCell {
private ComboBox combo;
public ComboBoxCell() {
combo = new ComboBox();
setGraphic(combo);
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
}
public ComboBoxCell(ObservableList items) {
combo = new ComboBox();
combo.setItems(items);
setGraphic(combo);
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
combo.getSelectionModel().selectFirst();
}
public T getSelectedItem()
{
return (T) combo.getSelectionModel().getSelectedItem();
}
public void setSelectedItem(T t)
{
combo.getSelectionModel().select(t);
}
}
My problem is that when Table is quite big and there is only 2 rows in it, dropdowns are produced anyway and it looks like this:
Is there a way to produce only as many dropdowns and checkboxes as many items there is in observable list that feeds this table?
While working with cells, please read the Cell API beforehand, to understand how they are handled under the hood. In short the cells are reused in different rows to render different items/records. Each time when the cell is reused its updateItem() method will be called to refresh the item the cell is rendering. Thus you need to override this method and control the graphic in there, instead of in constructor:
private ComboBox combo;
public ComboBoxCell() {
combo = new ComboBox();
}
public ComboBoxCell(ObservableList items) {
combo = new ComboBox();
combo.setItems(items);
}
#Override
public void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setText(null);
setGraphic(null);
} else {
combo.getSelectionModel().select(item);
setGraphic(combo);
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
}
}

Java FX: how to update ComboTableCell on change of another

im stuck into some problem, need guidance !
i have a TableView that has 2 ComboBoxTableCells, my requirement is to update the list in combobox of 2nd cell on change of the first.
i have tried it the following way,no luck so far.
public class Test{
private StringProperty name;
private StringProperty city;
public Test(String name, String city){
this.name = new SimpleStringProperty(name);
this.city = new SimpleStringProperty(city);
}
public String getName() {
return name.get();
}
public void setName(String name) {
this.name.setValue(name);
}
public String getCity() {
return city.get();
}
public void setCity(String city) {
this.city.setValue(city);
}
public StringProperty nameProperty() {return name;}
public StringProperty cityProperty() {return city;}
}
TableView _table= new TableView();
final ObservableList list = FXCollections.observableArrayList();
list.add("name 1");
list.add("name 2");
list.add("name 3");
list.add("name 4");
final ObservableList list2 = FXCollections.observableArrayList();
list2.add("city 1");
list2.add("city 2");
list2.add("city 3");
list2.add("city 4");
TableColumn firstNameCol = new TableColumn("First Name");
firstNameCol.setMinWidth(100);
firstNameCol.setCellValueFactory(new PropertyValueFactory<Test, String>("name"));
firstNameCol.setCellFactory(ComboBoxTableCell.forTableColumn(list));
firstNameCol.setOnEditCommit(
new EventHandler<CellEditEvent<Test, String>>() {
#Override
public void handle(CellEditEvent<Test, String> t) {
((Test) t.getTableView().getItems().get(t.getTablePosition().getRow())).setName(t.getNewValue());
System.out.println(t.getTableColumn().getCellData(t.getTablePosition().getRow()));
i guess have to do something here, tried the following line to see the impact on the respective cell
list2.clear();
it updated data for the whole column i just want it to be updated for the respective cell only.
}
}
);
TableColumn lastNameCol = new TableColumn("City");
lastNameCol.setMinWidth(100);
lastNameCol.setCellValueFactory(
new PropertyValueFactory<Test, String>("city"));
lastNameCol.setCellFactory(ComboBoxTableCell.forTableColumn(list2));
lastNameCol.setOnEditCommit(
new EventHandler<CellEditEvent<Test, String>>() {
#Override
public void handle(CellEditEvent<Test, String> t) {
((Test) t.getTableView().getItems().get(
t.getTablePosition().getRow())
).setName(t.getNewValue());
}
}
);
_table.setEditable(true);
_table.getColumns().addAll(firstNameCol,lastNameCol);
ObservableList listItems = FXCollections.observableArrayList();
listItems.add(new Test("name 4", "city 2"));
listItems.add(new Test("name 2", "city 3"));
table.getTableView().setItems(listItems);
_table.setItems(listItems);
any help will be highly appreciated. thanks
Here's a hacky approach that I haven't tested:
Add a dummy (boolean?) property on the data items that you will use to communicate between firstNameCol and lastNameCol
In the onEditCommit handler for firstNameCol, change the value of the dummy property. Be sure it changes.
Have lastNameCol be a column for the dummy property. Register a cell factory for lastNameCol that returns a TableCell with an overriden updateItem() method (pseudo-code below)
lastNameCol.setCellFactory(new Callback() {
#Override
public TableCell call(TableColumn col) {
return new TableCell() {
#Override
public void updateItem(Boolean item, boolean empty) {
if (!empty) {
// Don't care about the value of item
// Just look up the value of firstNameCol using
// getTablePosition(), then create and populate
// a ComboBox with the appropriate items and set
// it as the graphic for this cell via this.setGraphic()
// Add handler to ComboBox control to update data item when
// selection changes
}
}
};
}
});

Alternative for cloning in LWUIT Component object

Situation:-
In my code I have to use the LWUIT Component object for the listview controls. The controls are dynamic and hence can be in any number.
Right now I am creating Component objects according to the controls(in numbers) i.e.- for every control to be created first the Component object is creating.
This process slows down the rendering of the listview when the controls are increasing.
Solution:-
If I create the Component object and use it in a loop for all the controls it is taking the reference of the object and hence displays all the listview items(controls) with the same data.
Now I am able to think of one last option of Cloning my object and using it to create the controls.
Problem:-
But I can't find any way in LWUIT by which I can achieve the copying of object.
What can be the alternatives in LWUIT to solve this problem?
P.S.-The listview items are of same type, but with different data.
Use a List component and the Renderer design pattern to create a "rubber stamp" component where you can display a large number of elements easily. See an explanation of this in the Codename One blog.
Create these classes first :
public class ListUtil {
private Vector data = new Vector();
private Content[] contents;
public ListUtil(Vector vData)
{
data = vData;
contents = new Content[vData.size()];
}
public List createList(Display display, CListCell renderer, ActionListener listener)
{
CList theList;
for(int i = 0; i < data.size(); i++)
{
contents[i] = new Content(String.valueOf(data.elementAt(i)));
}
theList = new CList(display, contents, renderer, listener);
return theList;
}
}
public class Content
{
private String row;
public Content(String row)
{
this.row = row;
}
public String getRow()
{
return (row);
}
}
public class CListCell extends Container implements ListCellRenderer {
private Label focus = new Label("");
public CListCell()
{
super();
// create and add the components here among the components which will display data
}
public Component getListCellRendererComponent(List list, Object value, int index, boolean isSelected)
{
Content entry = null;
if (value instanceof Content)
entry = (Content)value;
componentDisplayingDataAddedIntoThisListCellRenderer.setText(entry.getRow());
return this;
}
public Component getListFocusComponent(List arg0)
{
return focus;
}
}
public class CList extends List {
private Display disp;
public CList(Display display, Object[] data, CListCell renderer, ActionListener actionListener)
{
super(data);
setListCellRenderer(renderer);
setIgnoreFocusComponentWhenUnfocused(true);
addActionListener(actionListener);
setScrollAnimationSpeed(getScrollAnimationSpeed()/4);
disp = display;
}
public void pointerReleased(int x,int y)
{
if (isEnabled() && hasFocus())
super.pointerReleased(x, y);
}
public void keyReleased(int keyCode)
{
if (isEnabled() && hasFocus())
{
if (disp.getGameAction(keyCode) == Display.GAME_FIRE)
pointerReleased(getX(),getY());
else
super.keyReleased(keyCode);
}
}
}
To create your List and add it to a Form :
public class myForm extends Form implements ActionListener
{
private Vector listSource = // your vector of data
private CListCell renderer = new CListCell();
private List theList = (new ListUtil(listSource)).createList(Display.getInstance(),renderer, this);
...
public void actionPerformed(ActionEvent evt)
{
if (evt.getSource() == theList)
doSomething();
}
}

Resources