WPF/MVVM: SelectedIndex Binding Property not updating when Combobox Selection Changed - wpf

I've got a very similar question to Jinesh's. I need to get the value of the SelectedIndex (or SelectedItem or SelectedValue) of a combo box in to my ViewModel code (i.e., when a user selects an item in the combo box, I need that index (or item or value) to update a property in the ViewModel code). I've followed the suggestions in Sheridan's answer and Felix C's answer but my property is not getting updated when the selected item changes. I'm trying to get the newly selected index value as part of my "Update" button code.
View:
<ComboBox Name="cboMonth"
ItemsSource="{Binding MonthsList, Mode=OneTime}"
DisplayMemberPath="month"
SelectedIndex="{Binding MonthIndex, Mode=TwoWay}"/>
<Button Name="btnUpdate"
Content="Update"
Command="{Binding processUpdateButton}" />
ViewModel:
public ObservableCollection<Month> MonthsList { get; set; }
private int _monthIndex;
public int MonthIndex
{
get
{
DateTime today = DateTime.Today;
_monthIndex = today.Month - 1;
return _monthIndex;
}
set
{
if (_monthIndex != value)
{
_monthIndex = value;
RaisePropertyChanged("MonthIndex");
}
}
}
public ICommand processUpdateButton
{
get
{
if (_setUpdateButton == null)
{
_setUpdateButton = new RelayCommand(param => validateOdometer());
}
return _setUpdateButton;
}
}
public void validateOdometer()
{
Console.WriteLine("Validating odometer...");
Console.WriteLine("Month Index: " + (_monthIndex));
}
When my page first renders, I have the combo box defaulting to index 5 (06-June) and the property in question, _monthIndex, reflects 5. When I select a new month in the combo box (e.g., October) then click my update button (btnUpdate), _monthIndex should reflect 9 but it still reflects 5. Why? Appreciate any/all help. Thanks.

The property getter ignores the previously set value and always returns the index of the current month.
The declaration should look like this:
private int monthIndex = DateTime.Today.Month - 1;
public int MonthIndex
{
get { return monthIndex; }
set
{
if (monthIndex != value)
{
monthIndex = value;
RaisePropertyChanged("MonthIndex");
}
}
}

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

WPF ComboBox SelectedItem when bound to an enum

I have a WPF ComboBox bound to a list of a class which contains an enum.
This all works fine, my question is at the end of this post, first the code:
Here is the class:
public class FILTER_TEST
{
public FilterType Filter { get; private set; }
public string Description { get; private set; }
public static List<FILTER_TEST> CreateFilters()
{
var list = new List<FILTER_TEST>();
list.Add(new FILTER_TEST() { Filter = FilterType.CheckNone, Description = "Uncheck all" });
list.Add(new FILTER_TEST() { Filter = FilterType.CheckAll, Description = "Check all" });
list.Add(new FILTER_TEST() { Filter = FilterType.CheckCustom, Description = "Custom check" });
return list;
}
}
Here is the enum FilterType:
public enum FilterType
{
CheckNone,
CheckAll,
CheckCustom
}
In my view model I have the following:
public List<FILTER_TEST> FilterNames { get { return FILTER_TEST.CreateFilters(); } }
public FILTER_TEST SelectedFilter
{
get { return selectedFilter; }
set
{
if (value != selectedFilter)
{
selectedFilter = value;
OnPropertyChanged("SelectedFilter");
}
}
}
Also in the view model, I set the SelectedItem of the ComboBox as follows:
SelectedFilter = FilterNames.Where(x => x.Filter == FilterType.CheckNone).FirstOrDefault();
Here is the xaml putting it all together:
<ComboBox DisplayMemberPath="Description" ItemsSource="{Binding FilterNames}"
SelectedItem="{Binding SelectedFilter, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" IsSynchronizedWithCurrentItem="True"/>
My problem is that although the changing of the SelectionItem works, the actual value displayed in the ComboBox doesn’t change.
The initial SelectedItem is “Uncheck all” as, when the window has been loaded, none of the corresponding CheckBox controls (bound to another class which contains a Boolean property) have been checked. What I would like is that when a CheckBox has been checked, then the SelectedItem changes to “Custom check”.
This does indeed change the value of the SelectedItem:
SelectedFilter = FilterNames.Where(x => x.Filter == FilterType.CheckCustom).FirstOrDefault();
But the text shown in the ComboBox is still “Uncheck all”.
Does anyone have an idea as to what I am missing? I am forced to use the 4.0 framework, I don’t know if this is relevant.
I've seen the hint to overwrite Equals() of the type in use as this:
public override bool Equals(object o)
{
if (o is FILTER_TEST)
{
var other = o as FILTER_TEST;
return this.Description == other.Description && this.Filter == other.Filter;
}
else
return false;
}
Now that makes your sample work. Let me come back for a reference on the why.

WPF ListView selection upon focus lost

I am currently learning and working on simple WPF (MVVM pattern) application, which allows to select items from one listview (available items) to another (order) and create an order class instance once 'buy' button is being pressed.
My problem is that once I click on first listview - item is selected and I am not able to deselect it once focus is lost.
I have learnt a lot about event and commands in MVVM, but things a really mixed in my head. Could you please guide me to the simple way how it's possible to 'refresh'/deselect all items once focus is lost on listview?
Thank you.
Here's a quick and dirty example using bindlings. You'll probably have to adjust some it for your needs:
The XAML
<Grid>
<ListBox Name="customerList1"
ItemsSource="{Binding List1Customers}"
SelectedItem="{Binding List1SelectedCustomer, Mode=TwoWay}"/>
<ListBox Name="customerList2"
ItemsSource="{Binding List2Customers}"
SelectedItem="{Binding List2SelectedCustomer, Mode=TwoWay}"/>
</Grid>
The ViewModel
public class Customer
{
public int id { get; set; }
}
public class MyViewModel
{
// This is the collection the first list gets its data from
private ObservableCollection<Customer> list1Customers;
public ObservableCollection<Customer> List1Customers
{
get { return list1Customers; }
set
{
if (list1Customers != value)
{
list1Customers = value;
}
}
}
// This is the customer selected in the first list
private Customer list1SelectedCustomer;
public Customer List1SelectedCustomer
{
get { return list1SelectedCustomer; }
set
{
if (list1SelectedCustomer != value)
{
list1SelectedCustomer = value;
}
}
}
// This is the collection the second list gets its data from
private ObservableCollection<Customer> list2Customers;
public ObservableCollection<Customer> List2Customers
{
get { return list2Customers; }
set
{
if (list2Customers != value)
{
list2Customers = value;
}
}
}
// This is the customer selected in the second list
private Customer list2SelectedCustomer;
public Customer List2SelectedCustomer
{
get { return list2SelectedCustomer; }
set
{
if (list2SelectedCustomer != value)
{
list2SelectedCustomer = value;
}
}
}
public void MoveCustomer(int id)
{
// Find the customer from list 1
var customerToMove = List1Customers.Where(x => x.id == id).FirstOrDefault();
// If it was found...
if (customerToMove != null)
{
// Make the first customer null, which un selects it in the list
List1SelectedCustomer = null;
// Remove the customer from list 1, or comment out if you dont want it removed
List1Customers.Remove(customerToMove);
// Add the customer to list 2
List2Customers.Add(customerToMove);
// Make the newly addec customer in list 2 selected
List2SelectedCustomer = List2Customers.Where(x => x.id == id).FirstOrDefault();
}
}
}

WPF/MVVM: Property Declaration Requiring Supporting Method/Function Logic

I have two properties I'm setting for two WPF combo boxes: one for Month and one for Day. The MonthIndex property looks like this:
private int monthIndex = DateTime.Today.Month - 1;
public int MonthIndex
{
get { return monthIndex; }
set
{
if (monthIndex != value)
{
monthIndex = value;
RaisePropertyChanged("MonthIndex");
}
}
}
I need to set the DayIndex property but unlike the Month property, it requires calculation - can't use simple declaration like
private int _dayIndex = DateTime.Today.Day - 1;
Each calendar day can have 0 or more events. e.g., if 6/30 had three events, such events would be stored as 30, 30.1, and 30.2 (in ObservableCollection DaysList and corresponding index for each event).
Below is the XAML, declaration, and method for DayIndex:
View:
<ComboBox Name="cboDay"
ItemsSource="{Binding DaysList, Mode=OneTime}"
DisplayMemberPath="fltDay"
SelectedIndex="{Binding DayIndex, Mode=TwoWay}"
IsEditable="True" />
ViewModel:
public ObservableCollection<Day> DaysList { get; set; }
private int _dayIndex;
public int DayIndex
{
get
{
// perform some calculation logic;
return _dayIndex;
}
set
{
if (_dayIndex != value)
{
_dayIndex = value;
RaisePropertyChanged("DayIndex");
}
}
}
How do I handle the declaration for dayIndex so it remains updated as the monthIndex does (so I can use its value with other code)?

Combobox in settingsflyout does not show selected values when opening it again

This seemed so simple but has turned into a nightmare for me. Everything works great, i can select a value and it's reported back to the view model.
Problem:
User opens the settings flyout and selects a value. User exits the flyout.
User reopens the settings flyout and there is no selected value in the combobox. The value exists in the view model though.
Scenario:
Combobox in a Settingsflyout.
<ComboBox x:Name="defaultComboBox" SelectedItem="{Binding UserSettings.DefaultAccount, Mode=TwoWay}" ItemsSource="{Binding UserAccounts}" DisplayMemberPath="CustomName">
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="Loaded">
<core:InvokeCommandAction Command="{Binding UserAccountComboboxLoadedCommand}" CommandParameter="{Binding ElementName=defaultAccountComboBox}"/>
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
</ComboBox>
ViewModelCode:
public void Open(object parameter, Action successAction)
{
logger.LogProgress("Opened UserSettingsFlyoutView.");
UserSettings.DefaultAccount = UserAccounts.FirstOrDefault(u => u.AccountID.ToString().Equals(userSettings.DefaultAccountGuid,StringComparison.CurrentCultureIgnoreCase));
}
public CrossThreadObservableCollection<UserAccount> UserAccounts
{
get
{
try
{
return dbContext.RetrieveAllUserAccounts();
}
catch(Exception e)
{
logger.LogError("Error happened when retrieving user-accounts from secure data store Error: " + e.Message, e.ToString());
return new CrossThreadObservableCollection<UserAccount>();
}
}
}
private IProvideUserSetting userSettings;
public IProvideUserSetting UserSettings
{
get { return userSettings; }
set { userSettings = value; OnPropertyChanged("UserSettings"); }
}
UserSettings class:
private string defaultAccountGuid;
[DataMember]
public string DefaultAccountGuid
{
get { return defaultAccountGuid; }
set { defaultAccountGuid = value; OnPropertyChanged("DefaultAccountGuid"); }
}
private UserAccount defaultAccount;
[IgnoreDataMember]
public UserAccount DefaultAccount
{
get { return defaultAccount; }
set {
defaultAccount = value;
if (defaultAccount != null)
DefaultAccountGuid = defaultAccount.AccountID.ToString();
OnPropertyChanged("DefaultAccount"); }
}
I tried a version of the code and could not reproduce the issue. Could you provide more code? Is there something else setting the selected item?
Anyway, the type of item in the ItemsSource is different than the type of item for selected item. I would try changing the selected item binding to the same class in the items source.
For example, instead of the viewmodel property UserSettings, make that object type UserAccount.
Something like
private UserAccount _selectedUserAccount { get; set; }
public UserAccount SelectedUserAccount
{
get { return _selectedUserAccount; }
set
{
if (_selectedUserAccount != value)
{
_selectedUserAccount = value;
OnPropertyChanged("SelectedUserAccount");
}
}
}
Edit:
You can add a loaded event handler to your combobox, then locate the viewmodel from the code behind and set the selected item property.
private void ComboBox_Loaded(object sender, RoutedEventArgs e)
{
ComboBox comboBox = sender as ComboBox;
comboBox.SelectedItem =
_viewModel.UserAccounts.Where(x => x.UserAccountString == _viewModel.SelectedUserAccount.UserAccountString);
}

Resources