Vaadin-Unable to update grid container on combobox value change - combobox

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);
}
}

Related

ObservableCollection deep cloning

I've implemented deep cloning of ObservableCollection in order to reset items to It's original state in editable Datagrid, via cancel button.
For this I have two collections - one ObservableCollection to bind Datagrid to It, and cloned List to re-initialize ObservableCollection to It's original state when needed.
My code works only first time I hit a cancel button, after that my cloned List has changes in It too.
Provided code is an example (mine is a bit longer), but It's 100% same as mine:
Model, which implements ICloneable:
public class EmployeeModel : ICloneable
{
public object Clone()
{
return MemberwiseClone();
}
public string NAME
{
get { return _name; }
set
{
if (_name != value)
{
CHANGE = true;
_name = value;
}
}
}
private string _name;
public string SURNAME
{
get { return _surname; }
set
{
if (_surname != value)
{
CHANGE = true;
_surname = value;
}
}
}
private string _surname;
///<summary>Property for tracking changes in model</summary>
public bool CHANGE { get; set; }
}
Viewmodel:
public ViewModel() : Base //Implements InotifyPropertyChanged
{
public ViewModel()
{
Task.Run(()=> GetData());
}
public ObservableCollection<EmployeeModel> Employees
{
get { return _employees; }
set { _employees = value; OnPropertyChanged();}
}
private ObservableCollection<EmployeeModel> _employees;
public List<EmployeeModel> Copy_employees
{
get { return _copy_employees; }
set { _copy_employees = value; OnPropertyChanged();}
}
private List<EmployeeModel> _copy_employees;
//Fetch data from DB
private async Task Get_data()
{
//Returns new ObservableCollection of type Employee
Employees = await _procedures.Get_employees();
if (Employees != null) //Now make a deep copy of Collection
{
Copy_employees = new List<EmployeeModel>();
Copy_employees = Employees.Select(s => (EmployeeModel)s.Clone()).ToList();
}
}
//My Command for canceling changes (reseting DataGrid)
//CanExecute happens, when model is changed - tracking via CHANGE property of EmployeeModel
public void Cancel_Execute(object parameter)
{
Employees.Clear(); //Tried with re-initializing too, but same result
foreach (var item in Copy_employees)// Reset binded ObservableCollection with old items
{
Employees.Add(item);
}
//Check if copied List really hasn't got any changes
foreach (EmployeeModel item in Copy_employees)
{
Console.WriteLine("Changes are " + item.CHANGES.ToString());
}
}
}
Output of cancel command:
1.) First time I hit cancel button:
// Changes are False
Every next time:
// Changes are True
So, as I see It from Console, my copied List get's updated when ObservableColection get's updated, even if It's not binded to DataGrid.
And It updates only a property which I changed, so List reflects ObservableCollection items.
How can I keep my original items of List<Employee>, and copy those into binded ObservableCollection anytime ?
When you return values, you do not return them, but write backing item references to the editable collection.
As a result, you have the same instances in both collections.
In the simplest case, when you return them, you also need to clone.
public void Cancel_Execute(object parameter)
{
Employees.Clear(); //Tried with re-initializing too, but same result
foreach (var item in Copy_employees)// Reset binded ObservableCollection with old items
{
Employees.Add((EmployeeModel)item.Clone());
}
//Check if copied List really hasn't got any changes
foreach (EmployeeModel item in Copy_employees)
{
Console.WriteLine("Changes are " + item.CHANGES.ToString());
}
}
Not relevant to the question, but I still advise you to use a slightly more user-friendly interface for cloneable:
public interface ICloneable<T> : ICloneable
{
new T Clone();
}

Selecting same item in 2 different Grid-Views

I have 2 RadGridView.
GridView 1: short list has items from GridView2.
GridView 2: Long list has telerik:RadGridView.GroupDescriptors for group the items.
I want if I select one row in first GridView takes me to the same Row in the second GridView.
So, I put in Xaml
//XAML
SelectedItem="{Binding SelectedDP}" For GridView1
SelectedItem="{Binding SelectedDP1}" For GridView2
//CS
private DataPermission mSelectedDP = new DataPermission();
public DataPermission SelectedDP
{
get { return mSelectedDP; }
set
{
mSelectedDP = value;
foreach (SecurityDataPermissions m in DisplayedDataPermissionsList)
{
if (m.DataPermission.DataPointName == SelectedDP.DataPointName)
SelectedDP1 = m;
}
OnPropertyChanged("SelectedDP");
}
}
private SecurityDataPermissions mSelectedDP1 = new SecurityDataPermissions();
public SecurityDataPermissions SelectedDP1
{
get { return mSelectedDP1; }
set
{
this.mSelectedDP1 = value;
OnPropertyChanged("SelectedDP1");
}
}
The row in GridView2 is getting selected but it doesn't scroll to it. I mean if manually scrolled to the item I can see it gray (selected but not focused) but If not, I can't recognize which row is selected.
What I want is when I select row in GridView1 takes me to the same row in GridView2
You could handle the SelectionChanged event and call the ScrollIntoViewAsync method in the event handler as suggested in the official documentation.
private void gridView_SelectionChanged(object sender, Telerik.Windows.Controls.SelectionChangeEventArgs e)
{
gridView.ScrollIntoViewAsync(gridView.SelectedItem, radgridView.Columns[0]);
}
You may of course wrap this functionality in an attached behaviour if you want to: https://www.telerik.com/forums/auto-scroll-to-the-selected-item

JavaFx 8: update the current selected item of a ComboBox if it is modified

I have a ComboBox with a ObservableList<Item> as model. I can update, externally to it, the elements of the list and I want to update the current selected item of ComboBox if it's modified.
I tried to do:
private final ObservableList<Item> list;
private ComboBox<Item> comboBox;
....
comboBox.setItems(list);
comboBox.getSelectionModel().selectFirst();
System.out.println("selected index: " + comboBox.getSelectionModel().getSelectedIndex(); );
ListChangeListener<Item> listener = new ListChangeListener<Item>() {
#Override
public void onChanged(ListChangeListener.Change<? extends Item> c) {
while (c.next()) {
for (int i = c.getFrom(); i < c.getTo(); ++i) {
int selectedIndex = comboBox.getSelectionModel().getSelectedIndex();
System.out.println("selected index: " + selectedIndex );
if (i == selectedIndex) {
comboBox.getSelectionModel().select(selectedIndex);
}
}
}
}
};
list.addListener(listener);
I added a ChangeListener to the list, so when some element in the list is updated, I can update the selected item in the comboBox. But this doesn't work 'cause comboBox.getSelectionModel().getSelectedIndex() inside onChange returns me -1 and I don't understand why (If I print the value out of onChange, for example the first System.out, the value is correct).
Can you explain me this behavior and if there is a better way to achieve what I want?
Thanks.

How to reload the ICollectionView binding to wpf combobox?

I have one textBox and one combobox in wpf usercontrol. ComboxBox is binded to ICollectionView (CurencyList) which populates Currency Pairs like GBP/EUR,CLP/EUR,USD/EUR,EUR/USD etc. Whenever the user writes in textbox e.g. EUR the combobox should get filtered and display the dropdownlist with EUR as the second currency.
For this I have used like:
public string Currency
{
get {
return _criteriaType.currency; }
set
{
if (_criteriaType.currency != value)
{
_criteriaType.currency = value.EmptyOrWhiteSpaceAsNull();
base.OnPropertyChanged("Currency");
CurrencyList.Filter = new Predicate<object>(Contains);
}
}
}
public bool Contains(object de)
{
CurrencyPair o = de as CurrencyPair;
if (Currency != null || Currency == string.Empty)
{
return (o.name.Substring(3, 4).ToLower().Contains(Currency.ToLower()));
}
else
{
IsOpen = false;
OnPropertyChanged("IsOpen");
return false;
}
}
CurrencyList is coming from a webservice:
private ICollectionView GetCurrencyPair()
{
strCurrencyPair.arg0 = (Currency != string.Empty && Currency != null) ? Convert.ToString(Currency).ToUpper() : string.Empty;
string[][] cPair = ServiceLocator.Resolve<IWebServiceRepository>().BusinessWebService.getCurrenyPairs(strCurrencyPair);
foreach (string[] item in cPair)
{
IList.Add(new CurrencyPair() { name = (Convert.ToString(item[0]).ToUpper() + "/" + Convert.ToString(item[1]).ToUpper()) });
}
return CurrencyList =CollectionViewSource.GetDefaultView(IList);
}
Filtering is working fine. But when the user deletes the currency from textbox with backward arrow key from keyboard, the combobox filtered to nothing i.e. dropdownlist is empty.
How to overcome this problem. kindly suggest?
you need to update your collection view through the text change event
private void OnTextChanged(object sender, Eventargs e) {
var vm = yourViewModelOrWhatEver;
((ICollectionView)vm.CurrencyList).Refresh();
}
or set the filter predicate once and fire only the refresh on currency change
public void ctor() {
CurrencyList.Filter = new Predicate<object>(Contains);
}
public string Currency {
get { return _criteriaType.currency; }
set {
if (_criteriaType.currency == value) {
return;
}
_criteriaType.currency = value.EmptyOrWhiteSpaceAsNull();
base.OnPropertyChanged("Currency");
CurrencyList.Refresh(); // refresh/filter the collection view
}
}
hope that helps
EDIT
you say
Filtering is working fine. But when the user deletes the currency from
textbox with backward arrow key from keyboard, the combobox filtered
to nothing i.e. dropdownlist is empty. How to overcome this problem.
kindly suggest?
then you must change your predicate function to this one
public bool Contains(object de)
{
CurrencyPair o = de as CurrencyPair;
if (Currency != null || Currency == string.Empty) {
// Currency == string.Empty should also true
return (Currency == string.Empty) || (o.name.Substring(3, 4).ToLower().Contains(Currency.ToLower()));
} else {
IsOpen = false;
OnPropertyChanged("IsOpen");
return false;
}
}
I would assume the issue lies with this statement: string[][] cPair =
ServiceLocator.Resolve().BusinessWebService.getCurrenyPairs(strCurrencyPair);
what is the return value from that service if you pass string.empty for strCurrencyPair as you will currently be doing once the textbox is empty from backspacing?

WPF MVVM: Add item not present in combobox

I'm using a MVVM approach with WPF to let the user select one item in a combobox. The model contains a set of possible options, the combobox is bound to this set, the current selection is again bound to a property of my model. This part works fine.
Now I'd like to allow the user to enter an arbitrary text into the combobox. If the text doesn't correspond to an existing item the program should ask him if he wants to add a new item. He should also be allowed to cancel the action and select another item.
How would I do that within the MVVM pattern?
You would check the "already existing" status of the text from your ViewModel's bound property setter. At that point, you need a mechanism to raise an event and decide what to do based on what happens.
An example:
enum Outcome { Add, Cancel }
class BlahEventArgs : EventArgs
{
Outcome Outcome { get; set; }
}
class ViewModel
{
private string name;
public EventHandler<BlahEventArgs> NotExistingNameSet;
public Name
{
get { return this.name; }
set
{
if (/* value is existing */) {
this.name = value;
return;
}
var handler = this.NotExistingNameSet;
if (handler == null) {
// you can't just return here, because the UI
// will desync from the data model.
throw new ArgumentOutOfRangeException("value");
}
var e = new BlahEventArgs { Outcome = Outcome.Add };
handler(this, e);
switch (e.Outcome) {
case Outcome.Add:
// Add the new data
this.name = value;
break;
case Outcome.Cancel:
throw new Exception("Cancelled property set");
}
}
}
}
Your View would add an event handler to NotExistingNameSet to present appropriate UI and set the value of e.Outcome accordingly.

Resources