When binding ComboBox to referenced source:
<ComboBox SelectedValue="{Binding Source.SelectedItem}"
ItemsSource="{Binding Source.Items}"
DisplayMemberPath="Name" />
where Source is
SourceType _source;
public SourceType Source
{
get { return _source; }
set { _source = value; OnPropertyChanged(); }
}
and SourceType is
public class SourceType: INotifyPropertyChanged
{
Item _selectedItem;
public Item SelectedItem
{
get { return _selectedItem; }
set { _selectedItem = value; OnPropertyChanged(); }
}
public IReadOnlyList<Item> Items { get; }
public SourceType(...)
{
Items = new List<Items>(...) // **new** list generated from passed arguments
SelectedItem = Items.First();
}
}
and Item is
public class Item: INotifyPropertyChanged
{
string _name;
public string Name
{
get { return _name; }
set { _name = value; OnPropertyChanged(); }
}
}
following happens:
for only one source (if Source never changes) it works: ComboBox display list of Items and correct item is selected (I can see its Name when switching view);
for more than one items ComboBox bugs: has no selection (but drop-down list is there and working fine), selection doesn't persist when switching view or Source is changed (e.g. between 2 sources).
It seems like ComboBox has some problems to identify SelectedValue or find it in the ItemsSource. And I can't figure out what is wrong.
Debugging does not uncover anything: Items is set correctly, SelectedItem is first item from Items collection, yet ComboBox is displayed without selection. Why?
I will use a ObservableCollection instead of a List for Items and use SelectedItem for the ComboBox, not SelectedValue.
Read this great answer for differences between SelectedItem and SelectedValue
Difference between SelectedItem, SelectedValue and SelectedValuePath
#Giangregorio answer is good and working in general, but now I remember why I used SelectedValue in first place.
SelectedValue is useful whenever ItemsSource does not contains SelectedValue. If SelectedItem is used (as per answer), then binding will call setter with null as if the user could select null from the list. I am exploiting this case (to avoid having data-templates and more complicated ViewModel), so I have to stick with SelectedValue and I think I found the reason of the problem in otherwise should-work case.
I have to declare ItemsSource binding first and SelectedValue second:
<ComboBox ItemsSource="{Binding Source.Items}"
SelectedValue="{Binding Source.SelectedItem}"
DisplayMemberPath="Name" />
It works!
Sounds to me like another xaml-specific issue, similar to declare CommandParameter before Command problem.
Related
I have a combobox which is bound to a property called "BlockDetails" in the viewmodel. When I expand the combobox I can see the items inside it. But the problem is it doesn't select/display the item. On top when I set SelectedValue="{Binding BlockName,Mode=TwoWay}", in the output window it gives a binding path error saying 'Error: BindingExpression path error: 'BlockName' property not found on 'Presentation.InstrumentUI.ViewsLoggedIn.ServiceUtilityMethodsView'. BindingExpression: Path='BlockName' DataItem='Presentation.InstrumentUI.ViewsLoggedIn.ServiceUtilityMethodsView'; target element is 'Windows.UI.Xaml.Controls.ComboBox' (Name='null'); target property is 'SelectedValue' (type 'Object')'. I don't understand why is it going and searcing in the View instead of the model. Please help.
Here is my combobox
<ComboBox uwpControls:DockPanel.Dock="Right"
Margin="16,0,0,0"
Style="{StaticResource ComboBoxStyleForm}"
ItemsSource="{x:Bind ViewModel.BlockDetails,Mode=TwoWay}"
DisplayMemberPath="BlockName"
SelectedValuePath="BlockName"
SelectedValue="{Binding BlockName,Mode=TwoWay}"></ComboBox>
In the code behind I have the ViewModel as follows, the item source for the Combobox is bound correctly
public IServiceUtilityMethodsViewModel ViewModel { get; }
public ServiceUtilityMethodsView()
{
InitializeComponent();
ViewModel = LifetimeScope.Resolve<IServiceUtilityMethodsViewModel>();
DataContext = this;
}
Here is the viewmodel property.
public List<VmServiceMethodBlockDefinition> BlockDetails
{
get => _blockDetails;
set => Set(ref _blockDetails, value);
}
In my model the class is declared as follows,
public class VmServiceMethodBlockDefinition : BindableBaseThreadSafe
{
private string _blockName;
public string BlockName
{
get => _blockName;
set => Set(ref _blockName, value);
}
private List<VmServiceMethodBlockParameters> _blockParameters;
public List<VmServiceMethodBlockParameters> BlockParameters
{
get => _blockParameters;
set => Set(ref _blockParameters, value);
}
}
I think you either confused the SelectedValue property or missed to post some details.
You have an object or data type that is an item of the ComboBox.
You have ComboBox.DisplayMemberPath to select the property of this data type, which should be displayed by the ComboBox in the drop down.
Finally you have ComboBox.SelectedValuePath to select a property of this data type to be the ComboBox.SelectedValue. You use ComboBox.SelectedValue instead of ComboBox.SelectedItem, if you are not interested of the actual data item, but a specific value of this item. Both properties serve the same purpose of exposing the currently selected item.
You typically bind this ComboBox.SelectedValue to the same view model (data context) that exposes the source collection, in your case the BlockDetails collection. Your view model should have a property SelectedBlockName. Then use x:Bind to bind the ComboBox.SelectedValue to this property:
IServiceUtilityMethodsViewModel
public List<VmServiceMethodBlockDefinition> BlockDetails
{
get => _blockDetails;
set => Set(ref _blockDetails, value);
}
private string _selectedBlockName
public string SelectedBlockName
{
get => _selectedBlockName;
set => Set(ref _selectedBlockName, value);
}
XAML
<ComboBox ItemsSource="{x:Bind ViewModel.BlockDetails, Mode=OneTime}"
DisplayMemberPath="BlockName"
SelectedValuePath="BlockName"
SelectedValue="{x:Bind ViewModel.SelectedBlockName, Mode=TwoWay}" />
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().
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
I am new to wpf and MVVM, and I've spent all day trying to get the value of a ComboBox to my ViewModel on SelectionChanged. I want to call a function in the selection changed process. In mvvm, what is the solution for it?
In MVVM, we generally don't handle events, as it is not so good using UI code in view models. Instead of using events such as SelectionChanged, we often use a property to bind to the ComboBox.SelectedItem:
View model:
public ObservableCollection<SomeType> Items { get; set; } // Implement
public SomeType Item { get; set; } // INotifyPropertyChanged here
View:
<ComboBox ItemsSource="{Binding Items}" SelectedItem="{Binding Item}" />
Now whenever the selected item in the ComboBox is changed, so is the Item property. Of course, you have to ensure that you have set the DataContext of the view to an instance of the view model to make this work. If you want to do something when the selected item is changed, you can do that in the property setter:
public SomeType Item
{
get { return item; }
set
{
if (item != value)
{
item = value;
NotifyPropertyChanged("Item");
// New item has been selected. Do something here
}
}
}
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
}
}