Set default value in a combo box using MVVM - c#

I'm trying to set a default value into a combo box when the application is first loading using the MVVM pattern and it looks like this is all the time unset, combo box being all the time empty when the page loads.
This is my xaml:
<ComboBox Grid.Row="0" Margin="10,0,0,0" Grid.Column="1"
SelectedItem="{Binding Path=JuiceOperations.SelectedItemOption, Mode=TwoWay}"
SelectedIndex="{Binding Path=JuiceOperations.SelectedComboBoxOptionIndex, Mode=TwoWay}"
SelectedValue="{Binding Path=JuiceOperations.SelectedComboBoxOptionIndex, Mode=TwoWay}"
ItemsSource="{Binding Path=JuiceOperations.JuiceOptions}" />
This is the view model code, with its default constructor:
public JuiceViewModel()
{
juiceOperations.SelectedComboBoxOptionIndex = 0;
juiceOperations.SelectedItemOption = "Cola";
}
where I am trying to set the default value of the combo box.
And this is how the properties looks like:
private List<string> juiceOptions = new List<string> { "Cola", "Sprite", "Fanta", "Pepsi" };
private string selectedItemOption = string.Empty;
private int selectedComboBoxOptionIndex = 0;
public int SelectedComboBoxOptionIndex
{
get
{
return this.selectedComboBoxOptionIndex;
}
set
{
this.selectedComboBoxOptionIndex = value;
this.OnPropertyChanged("SelectedComboBoxOptionIndex");
}
}
public List<string> JuiceOptions
{
get
{
return this.juiceOptions;
}
set
{
this.juiceOptions = value;
}
}
public string SelectedItemOption
{
get
{
return this.selectedItemOption;
}
set
{
this.selectedItemOption = value;
this.OnPropertyChanged("SelectedItemOption");
}
}
When selecting an item from combo box the selection is updated into the model and also into the view, so it is working as expected but when the page is first loaded even if the "SelectedComboBoxOptionIndex" and "SelectedItemOption" are being called and their value updated the view of the page is not updated and the empty string is being shown into the combo box where I was expected to see the "Cola" value, instead of the empty string.
Can someone explain me what I am doing wrong and how should I set the default "Cola" value into the combo box ?

Only bind the SelectedItem property of the ComboBox to the SelectedItemOption source property and set the latter to the string "Cola" in the view model. This should work:
<ComboBox Grid.Row="0" Margin="10,0,0,0" Grid.Column="1"
SelectedItem="{Binding Path=JuiceOperations.SelectedItemOption}"
ItemsSource="{Binding Path=JuiceOperations.JuiceOptions}" />
public JuiceViewModel()
{
juiceOperations.SelectedItemOption = "Cola";
}
Don't mix SelectedItem, SelectedIndex and SelectedValue. You only need one.

mm8 above absolutely right, that should fix your issue.
On a side note, what you have there will work for a static selection list, but consider using an ObservableCollection<string> instead of a List<string>. The former implements INotifyCollectionChanged, which allows the view to be notified if there has been a change in the collection. When you bind an Observable Collection to the view, the view automatically subscribes to the CollectionChanged event. You will need this if you ever need to add or remove options at run time. Side note, OnCollectionChanged will not fire if you simply modify an item, for that you would still need to callOnPropertyChanged("JuiceOptions") in the setter.
something like this (with the appropriate private backing field):
public ObservableCollection<string> JuiceOptions
{
get
{
return this.juiceOptions;
}
set
{
this.juiceOptions = value;
this.OnPropertyChanged("JuiceOptions");
}
}

The value of juiceOperations.SelectedItemOption, that is, "Cola", is not the same "Cola" stored in the ItemsSource. You would need to do something like juiceOperations.SelectedItemOption = juiceOperations.JuiceOptions.First().

Related

Can I store SelectedItem property value of a Picker under a ListView in an array?

I am working with Xamarin ListView and Picker, trying to create an android app that calculates a student's GPA in one page(view).
I have a class GPADetails that takes care of the Picker properties. This class contains a "List"...
public ObservableRangeCollection<string> UnitList{get;set;}
...of units binded to the ItemSource of the Picker. it also contains a "property field"...
private string selectedUnit=null;
public string SelectedUnit { get => selectedUnit;
set => SetProperty(ref selectedUnit, value); }
...that is binded to the SelectedItem property of the picker.
The ListView is being populated by binding a "List"...
public ObservableRangeCollection<GPADetails> GPADetailsList {get;set;}
...of multiple objects of type GPADetails class to the ItemSource of the ListView.(so that the user can pick different units for different subjects)
Here's the function that populates the listView
public void DisplayTemplates()
{
GPADetailsList.Clear();
//Instantiates template object equivalent to the number of courses
for (int i = 0; i < int.Parse(NumberOfCourses); i++)
{
GPADetailsList.Add(
new GPADetails
{
//initializing picker properties with picker items
UnitList = UnitItems,
SelectedUnit = null,
TemplateID = i,
});
}
}
Heres the Xaml of the ListView and the picker...
...
<ListView x:Name="listView" ItemsSource="{Binding GPADetailsList}" SelectionMode="None" >
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout >
<Editor Placeholder="Course Code" PlaceholderColor="White" HorizontalOptions="StartAndExpand">
</Editor>
<Picker x:Name="PickerUnit" Title="Pick a Unit" TitleColor="white"
HorizontalOptions="EndAndExpand"
VerticalOptions="Fill" HorizontalTextAlignment="Center" VerticalTextAlignment="Center" FontSize="15"
ItemsSource="{Binding UnitList}"
SelectedItem="{Binding SelectedUnit, Mode=TwoWay }"
>
</Picker>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
...
Now here's where I'm having problems. Each time the user selects a unit on the page, the selectedItem property of the picker is triggered. And the SelectedUnit property of the GPADetails class detects the property changed.
I want so store the SelectedItem of each Picker that is under the ListView in an array.
Using a property TemplateID of class GPADetails, I'm able to track which picker has been selected.
So I use TemplateID as the index of my array.
But I keep having problems because my C# is weak.
I tried doing this in the SelectedUnit property in class GPADetails by using a condition in the set accessor and initializing the array at the selected index with the selectedUnit. Heres the code..
private string selectedUnit;
public string SelectedUnit
{
get { return selectedUnit; }
set
{
SetProperty(ref selectedUnit, value);
if (selectedUnit != null)
SelectedUnitList[TemplateID] = selectedUnit;
}
}
But with that, i can only assign one value to the array, if i try to assign another, the previously assigned index goes back to the
default value, null.
P.S. I don't know if I asked this right, but any help would be appreciated, thanks dev fam...
I delete my origin post and the following is my new answer.
Each instance created in DisplayTemplate is separated. That means each instance has a different SelectedUnitList. That's the reason why you can only set one picker value in SelectedUnitList.
Here i give you some suggestions.
In your GPADetails.cs, add a new property, that's a dictionary named SelectedUnitDict.(Compare to SelectedUnitList, I prefer to use a dictionary as index of an array easily causes troubles):
public Dictionary<int,string> SelectedUnitDict { get; set; } //The key(int) is *TemplateID* and the value(string) is *SelectedItem*.
In your GPACalculationViewmodel, also create a property (i just use the same name for convenience):
public Dictionary<int, string> SelelctedUnitDict { get; set; }
and add a new line in the DisplayTemplates methods, that makes each instance of GPADetails get the same SelectedUnitDict in GPACalculationViewmodel.
for (int i = 0; i < int.Parse(NumberOfCourses); i++)
{
GPADetailsList.Add(
new GPADetails
{
//initializing picker properties with picker items
UnitList = UnitItems,
SelectedUnit = null,
TemplateID = i,
// Add this new line
SelelctedUnitDict = SelelctedUnitDict
});
Finally, in GPADetails.cs, just like your code:
public string SelectedUnit
{
...
set
{
...
if (selectedUnit != null)
{
SelectedUnitDict[TemplateID] = selectedUnit;
}
}
}
That's worked for me. I can get the selectedItem of each picker in my SelectedUnitDict in GPACalculationViewmodel. The key is TemplateID and the value is SelectedItem.
Hope my answer could help you.
I think you can use the properties/events to interact with the ListView instead of relying in the observable property.
You can use ItemSelected
<ListView ItemsSource="{x:Static local:interactiveListViewXaml.items}" ItemSelected="OnSelection" ItemTapped="OnTap" IsPullToRefreshEnabled="true" Refreshing="OnRefresh">
https://github.com/xamarin/xamarin-forms-samples/blob/main/UserInterface/ListView/Interactivity/interactivityListView/interactivityListView/Views/interactiveListViewXaml.xaml
About the implementation I'm not sure what TemplateID is or all this sentence I want so store the SelectedItem of each Picker under the ListView in an array using a property I don't understand it

WPF combobox is empty when binding enum

I am trying to bind values of an enum to a combo box but the combo box remain empty with no options to choose.
This is the combo box xaml defintion:
<ComboBox Grid.Row="2" Grid.Column="1" ItemsSource="{Binding Path=SkillItemSource}" SelectedItem="{Binding Path=neededSkill, Mode=TwoWay}" SelectedIndex="0" Margin="5" MinWidth="100"></ComboBox>
And this is the items source and selected item which are defined in the window's cs:
public Skill neededSkill = Skill.FirstSkill;
public string[] SkillItemSource
{
get
{
return Enum.GetNames(typeof(Skill));
}
}
What is missing for the values to appear in the combobox?
What is missing for the values to appear in the combobox?
You need to set the DataContext of the ComboBox, or a parent element, to an instance of the class where the SkillItemSource property is defined. If the property is defined in the code-behind, you could just set the DataContext to the view itself: this.DataContext = this;
Also, you can't mix types. If the ItemsSource is bound to an IEnumerable<string>, the SelectedItem property should be bound to a string property.
Also note that neededSkill must be defined as a public property for you to be able to bind to it.
Try this:
public Skill neededSkill { get; set; } = Skill.FirstSkill;
public IEnumerable<Skill> SkillItemSource { get; } = Enum.GetValues(typeof(Skill)).Cast<Skill>();

Binding to ObservableCollection works, but not ListCollectionView

I'm trying to get my ListCollectionView to bind to a combo box. However, it seems to only work when I bind to my ObservableCollection.
Properties:
private ListCollectionView sitesView;
public ListCollectionView SitesView
{
get { return sitesView; }
set
{
sitesView = value;
RaisePropertyChanged(() => SitesView);
}
}
public ObservableCollection<ISite> SitesCollection { get; set; }
Constructor:
SitesCollection = new ObservableCollection<ISite>();
SitesView = (ListCollectionView)CollectionViewSource.GetDefaultView(SitesCollection);
When binding like so:
<ComboBox Grid.Row="2" ItemsSource="{Binding SitesView, Mode=TwoWay}"/>
any item I add to SitesCollection does not get shown when I click the drop down in my combo box. But if I do the binding like so:
<ComboBox Grid.Row="2" ItemsSource="{Binding SitesCollection, Mode=TwoWay}"/>
it works fine and I see the items when I click the drop down.
Attempts at fixing: After I add the an item to the SitesCollection, I tried to raise property change notifications on both the ListCollectionView and the ObservableCollection and it didn't make a difference. Any ideas?
I didn't have time to test my answer; I apologize and will follow up.
I think your issue is because CollectionViewSource.GetDefaultView(...)
returns an ICollectionView, which I believe is essentially just a wrapper that enables the underlying data to be sorted.
Try setting SitesView to the SourceCollection of the DefaultView.
SitesView = (ListCollectionView)CollectionViewSource.GetDefaultView(SitesCollection).SourceCollection

How to navigate through ICollectionView?

So I've got a basic view model that functions like so:
// The ICollectionVIew is what my ListBox binds to.
public ICollectionView UserView { get; set; }
// <signup> is a model that's populated from a database representing a signup table
private ObservableCollection<signup> _signup;
public ObservableCollection<signup> Signup
{
get
{
return _signup;
}
set
{
if (_signup != value)
{
value = _signup;
}
OnPropertyChanged("Signup");
}
}
// This is the constructor for the ViewModel
public registrationVM()
{
// entity context Fills up the Model
context.signups.Load();
// The below code fills up the ObservableCollection
var query = context.signups;
_signup = new ObservableCollection<signup>(query);
// And the below code fills up the ICollectionView using the ObservableCollection
UserView = CollectionViewSource.GetDefaultView(_signup);
}
So now instead of binding to the ObservableCollection, I can bind to the ICollection.
<ListBox ItemsSource="{Binding UserView}" DisplayMemberPath="firstName" SelectedItem="{Binding SelectedUser}"/>
This works perfectly in terms of loading my information. But then now comes the issue of navigating. I bound my button Commands to the ViewModel,
<Button x:Name="next" Command="{Binding Next}"/>
And in it's execution Method:
private object Next_CommandExecute(object param)
{
// 'UserView' Is the ICollectionView I declared earlier
return UserView.MoveCurrentToNext();
}
The problem is the button's function doesn't do anything. Same goes for the 'previous' Button. The on screen selected record doesn't change so I'm guessing there's something I'm doing wrong. What exactly is what I've been failing to figure out. Does anyone see where I'm going wrong?
As mentioned in my comment, you need to set IsSynchronizedWithCurrentItem = true on your ListBox
ListBox ItemsSource="{Binding UserView}"
DisplayMemberPath="firstName"
IsSynchronizedWithCurrentItem = true
SelectedItem="{Binding SelectedUser}"/>

MVVM Pattern in Master-Detail when editing details

i have a ObservableCollection with some data. They are displayed as Master (ListBox) and Detail (some Labels). I use binding and IsSynchronizedWithCurrentItem to show the correct details to the selected master item. This works all fine. Now i want to edit some details (load different image). I implemeted this a a Button Command in the ViewModel.
But how do i know which item is selected (UI) in the ViewModel-layer ?
Thanks for help
I don't really find the IsSynchronizedWithCurrentItem property that useful in MVVM scenarios.
Just expose another SelectedItem property in the ViewModel.
public ItemType SelectedItem
{
get { return _selectedItem; }
set
{
_selectedItem = value;
// your logic here
}
}
You need to bind a value or Enumerable of values of your ViewModel to the ListBox's SelectedItems property.
SelectedItems="{Binding VMProperty}"
http://msdn.microsoft.com/en-us/library/system.windows.controls.listbox.selecteditems(v=vs.110).aspx
If you only want one item selected you need to set:
SelectionMode="Single"
SelectedItem="{Binding VMProperty}"
If I understood you right, what you are looking for is the following
<ListBox SelectedItem="{Binding ObjectName, UpdateSourceTrigger=PropertyChanged}"/>
Plus under your ViewModel you have to declare the following
public YourObject ObjectName { get; set; }
Simple as that!
The normal way to detect which item in the collection is currently selected in the UI is to data bind a property of the same type as the items in the collection to the ListBox.SelectedItem property:
<ListBox ItemsSource="{Binding SomeCollection}"
SelectedItem="{Binding SomeProperty}" />
Now, whenever the user selects a new item, the SomeProperty setter will be called:
public YourDataType SomeProperty
{
get { return someProperty; }
set
{
someProperty = value;
// The value has just changed
}
}

Categories