I'm trying to capture the value of the selected item from a ComboBox in my ViewModel. I do get the value but for some reason, when I do a string comparison in an IF statement to determine which item has been selected the comparison doesn't work.
What am I missing?
Is this the right way to capture the value in ViewModel?
XAML
<ComboBox x:Name="comboBox"
SelectedItem="{Binding SelectedItemInFilter, UpdateSourceTrigger=PropertyChanged}>
<ComboBoxItem IsSelected="True">No Selection</ComboBoxItem>
<ComboBoxItem>Car</ComboBoxItem>
<ComboBoxItem>Truck</ComboBoxItem>
</ComboBox>
ViewModel
public class MyViewModel : ViewModelBase{
public string _selectedItemInFilter;
public string SelectedItemInFilter
{
get { return _selectedItemInFilter; }
set {
if (_selectedItemInFilter != value) {
_selectedItemInFilter = value;
ComboBoxChanged();
Console.WriteLine("SelectedItem: {0}", SelectedItemInFilter); // outputs the right item name
RaisePropertyChanged();
}
}
}
private void ComboBoxChanged()
{
if (SelectedItemInFilter.ToString() == "Car") {
Console.WriteLine("Do something with car...");
}
else {
Console.WriteLine("Is not Car...");
}
}
}
Since you explicitly add ComboBoxItems (instead of setting or binding the ItemsSource to a collection of strings), the SelectedItem also is a ComboBoxItem, and not a string. You may however bind to the Content string of the selected item by using SelectedValue and SelectedValuePath:
<ComboBox SelectedValue="{Binding SelectedItemInFilter}"
SelectedValuePath="Content">
<ComboBoxItem>No Selection</ComboBoxItem>
<ComboBoxItem>Car</ComboBoxItem>
<ComboBoxItem>Truck</ComboBoxItem>
</ComboBox>
Even simpler would be not to use ComboBoxItems at all:
xmlns:sys="clr-namespace:System;assembly=mscorlib"
...
<ComboBox SelectedItem="{Binding SelectedItemInFilter}">
<sys:String>No Selection</sys:String>
<sys:String>Car</sys:String>
<sys:String>Truck</sys:String>
</ComboBox>
You are getting it wrong because the items aren't really strings, if you need to do this way you could do the following:
public object _selectedItemInFilter;
public object SelectedItemInFilter
{
get
{
return _selectedItemInFilter;
}
set
{
if (_selectedItemInFilter != value)
{
_selectedItemInFilter = value;
ComboBoxChanged();
Console.WriteLine("SelectedItem: {0}", SelectedItemInFilter); // outputs the right item name
NotifyPropertyChanged("SelectedItemInFilter");
}
}
}
private void ComboBoxChanged()
{
if (((ComboBoxItem)SelectedItemInFilter).Content.ToString() == "Car")
{
Console.WriteLine("Do something with car...");
}
else
{
Console.WriteLine("Is not Car...");
}
}
It's not the same to bind an ObservableCollection than declaring the items into the ComboBox control.
Related
I have List<string> MyList with 4 values. These are shown in a ComboBox control. The binding works perfectly in my MVVM WPF project.
I also have a string SelectedMyList which binds to my XAML and is supposed to show the selected item. The problem I have is, regardless of using SelectedItem or SelectedValue, it always passes the first item in MyList
private MyClass()//constructor
{
MyList = new List<string>() {"Hi", "Bye", "Hello", "See ya"};
}
private string _selectedMyList;
public string SelectedMyList
{
get
{
return this._selectedMyList;
}
set
{
//value is always Hi
if (this._selectedMyList== value)
return;
this._selectedMyList= value;
OnPropertyChanged("SelectedMyList");
}
}
private List<string> _myList;
public List<string> MyList
{
get
{
return this._myList;
}
set
{
if (this._myList== value)
return;
this._myList= value;
OnPropertyChanged("MyList");
}
}
And my XAML
<ComboBox ItemsSource="{Binding MyList}" SelectedValue="{Binding SelectedMyList, UpdateSourceTrigger=PropertyChanged}" />
There are no errors/binding errors etc in the output window.
Why does the SelectedItem/SelectedValue not pass what I consider to be the selected item from the ComboBox?
This works for me.
private string _selectedMyList;
public string SelectedMyList
{
get
{
return this._selectedMyList;
}
set
{
//value is always Hi
if (this._selectedMyList != value)
{
this._selectedMyList= value;
OnPropertyChanged("SelectedMyList");
}
}
}
private List<ObservableCollection> _myList;
public ObservableCollection<string> MyList
{
get
{
return this._myList;
}
set
{
if (this._myList== value)
{
this._myList= value;
OnPropertyChanged("MyList");
}
}
}
Xaml:
<ComboBox ItemsSource="{Binding MyList}"
SelectedItem="{Binding SelectedMyList}"
IsSynchronizedWithCurrentItem="True"/>
If you want to use the SelectedValue attribute then you need to use it with the SelectedValuePath attribute. See this link to a similar question.
What can I do if I want to bind to some property which is already bound to something else?
In my case, I got a window which has a TextBox. The Text property of this TextBox is data bound to a combo box to it's selectedItem. My Window class got a public string property which I want to update with whatever value is in the TextBox so I wanted to data bind with the Text property but as I said, it's already bound.
How can I update my property with the text in TextBox? Must it use a routed event of TextChanged or can I do it via Xaml?
Also specifically with properties you define them yourself in your window.cs ... how can you bind them to the TextBox.Text? I tried doing it with the <Window> declaration, meaning <Window MyProperty={Binding ...} /> but the property is not recognized there. Why and how do I do it?
You could solve this easily using the MVVM pattern.
ViewModel:
public class ChooseCategoryViewModel : ViewModelBase
{
private string[] _categories =
{ "Fruit", "Meat", "Vegetable", "Cereal" };
public string[] Categories
{
get { return _categories; }
}
private string _selectedCategory;
public string SelectedCategory
{
get { return _selectedCategory; }
set
{
_selectedCategory = value;
OnPropertyChanged("SelectedCategory");
if (value != null && CategoryName != value)
CategoryName = value;
}
}
private string _categoryName;
public string CategoryName
{
get { return _categoryName; }
set
{
_categoryName = value;
OnPropertyChanged("CategoryName");
if (Categories.Contains(value))
{
SelectedCategory = value;
}
else
{
SelectedCategory = null;
}
}
}
}
XAML:
<ComboBox ItemsSource="{Binding Categories}"
SelectedItem="{Binding SelectedCategory}" />
<TextBox Text="{Binding CategoryName}" />
I am not able to find out Why is combo box binding not working?
I have a view model which looks like (2 properties)
public ProcessMaintenanceDataObject CurrentProcess
{
get
{
return _CurrentProcess;
}
set
{
_CurrentProcess = value;
OnPropertyChanged("CurrentProcess");
}
}
public ObservableCollection<ProcessMaintenanceDataObject > Processes
{
get
{
return _Processes;
}
set
{
_Processes = value;
OnPropertyChanged("Processes");
}
}
public ObservableCollection<FolderInfo> Folders
{
get
{
return _folders;
}
set
{
_folders = value;
OnPropertyChanged("Folders");
}
}
The following is the ProcessMaintenanceDataObject definition
[DataMember]
public string ProcessName
{
get
{
return _ProcessName;
}
set
{
_ProcessName = value;
OnPropertyChanged("ProcessName");
}
}
[DataMember]
public string Id
{
get
{
return _Id;
}
set
{
_Id = value;
OnPropertyChanged("Id");
}
}
[DataMember]
public string FolderId
{
get
{
return _FolderId;
}
set
{
_FolderId = value;
OnPropertyChanged("FolderId");
}
}
[DataMember]
public FolderInfo Folder
{
get
{
return _Folder;
}
set
{
_Folder = value;
if (_Folder != null)
FolderId = _Folder.FolderId;
OnPropertyChanged("Folder");
}
}
The FolderInfo class has FolderName and FolderId Property.
I have a method in viewmodel which fills the Processes.
In my view I have structure something like, I have a treeview which will be bound to Processes and while selecting any of the item from the treeview, i need to allow user to edit that entity.
In the view the combo box binding is as:
<ComboBox ItemsSource="{Binding Path=Folders, Mode=OneWay}"
DisplayMemberPath="FolderName"
SelectedItem="{Binding Source={StaticResource viewModel}, Path=CurrentProcess.Folder, Mode=TwoWay}">
...
This binding doesn't work I mean when I select any object from the tree it fills other information like ProcesName in the textBox but it doesn't make the Folder object as the selected item in combobox, however the combo box will be filled.
Any suggestion.
Do refer this:
If you want to bind a ComboBox to your folders property in two way mode with edit support,
Then you should define data template for your combo box and then bind properties of FolderInfo class to those text boxes
Binding display member path will not solve your problem
I'd suggest you to change DisplayMemberPath with appropriate DataTemplate:
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding FolderName}">
</StackPanel>
<DataTemplate>
This will fix SelectedItem context.
Maybe just maybe, your Folderinfo class is not a notificationObject. If it is the case make sure it implements INotifyPropertyChange.
you have to use SelectedValuePath and SelectedValue instead of SelectedItem like below,
<ComboBox ItemsSource="{Binding Path=Folders, Mode=OneWay}"
DisplayMemberPath="FolderName"
SelectedValuePath="FolderId"
SelectedValue="{Binding Path=FolderId, Mode=TwoWay}">
SelectedItem binds the whole object whereas SelectedValue binds only particular properties of the object.
I have one model that implements INotifyPropertyChanged through BaseModel class.
It has other model as element inside of it.
class SIDPoslJavnaUstanova : BaseModel
{
private int? _sid_posl_javna_ustanova_id;
...
private decimal? _udaljenost;
private SIDJavnaUstanova _sid_javna_ustanova;
public SIDJavnaUstanova SidJavnaUstanova
{
get { return _sid_javna_ustanova; }
set {
if (_sid_javna_ustanova != value)
{
_sid_javna_ustanova = value;
if (_sid_javna_ustanova != null)
{
_sid_javna_ustanova_id = _sid_javna_ustanova.SidJavnaUstanovaId;
}
else
{
_sid_javna_ustanova_id = null;
}
RaisePropertyChanged("SidJavnaUstanova");
}
}
}
I have viewmodel that has observable collection of this model objects.
class BaseViewModel<T> : ObservableObject
{
private ObservableCollection<T> _elements = new ObservableCollection<T>();
public ObservableCollection<T> Elements
...
class SIDPoslJavnaUstanovaViewModel : BaseViewModel<SIDPoslJavnaUstanova>
{
}
}
And finally, mainviewmodel that is bound to view:
class MainViewModel : BaseViewModel<Store>
{
private SIDJavnaUstanovaViewModel _sidJavnaUstanovaViewModel;
private SIDJavnaUstanova _sidJavnaUstanova;
public SIDPoslJavnaUstanovaViewModel SidPoslJavnaUstanovaViewModel
{
get { return _sidPoslJavnaUstanovaViewModel; }
set
{
if (_sidPoslJavnaUstanovaViewModel != value)
{
_sidPoslJavnaUstanovaViewModel = value;
RaisePropertyChanged("SidPoslJavnaUstanovaViewModel");
}
}
}
public SIDJavnaUstanovaViewModel SidJavnaUstanovaViewModel
{
get { return _sidJavnaUstanovaViewModel; }
set
{
if (_sidJavnaUstanovaViewModel != value)
{
_sidJavnaUstanovaViewModel = value;
RaisePropertyChanged("SidJavnaUstanovaViewModel");
}
}
}
SidJavnaUstanova is only used to populate combobox, and to bind to object when choosen.
I have combobox in datagrid, that has mulitple lines. Element is SIDJAVNAUSTANOVA , and dropdown is SIDJAVNAUSTANOVAVIEWMODEL.
Dropdown is SIDJAVNAUSTANOVAVIEWMODEL.ELEMENTS
(cannot show you picture not enough reputation)
<src:BaseWindow.Resources>
<viewmod:MainViewModel x:Key="StoreViewM"/>
</src:BaseWindow.Resources>
<DataGrid.Columns>
<DataGridTemplateColumn Width="140" Header="{StaticResource name}">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox ItemsSource="{Binding Path=SidJavnaUstanovaViewModel.Elements,
Source={StaticResource StoreViewM}}"
SelectedItem="{Binding Path=SidJavnaUstanova,UpdateSourceTrigger=PropertyChanged,
Mode=TwoWay}"
DisplayMemberPath="Naziv"
SelectedValue="{Binding Path=SidJavnaUstanova, Mode=TwoWay}">
</ComboBox>
...
Everything is working fine except when combobox is changed, element SIDJavnaUstanova of object SIDPoslJavnaUstanova is changed, and I can catch this in its model property. But what I must have, is to catch change of this SidJavnaUstanova in viewmodel, so I can implement check-out if there are duplicates of sidjavnaustanova in sidposljavnaustanovaviewmodel.elements. I cannot realize how to do that.
Something like
SIDPoslJavnaUstanova.Elements.??? SIDJavnaUstanova
I cannot do this because elements is observable collection.
Maybe it is a bad model, please suggest something or help with current code.
You need to a) specify source for SelectedItem b) bind SelectedItem to the property of the same type, as elements in your collection (i.e. SIDPoslJavnaUstanova in your case).
This should work, i guess:
<ComboBox ItemsSource="{Binding Path=SidJavnaUstanovaViewModel.Elements,
Source={StaticResource StoreViewM}}"
SelectedItem="{Binding Path=SelectedModel,UpdateSourceTrigger=PropertyChanged,
Mode=TwoWay, Source={StaticResource StoreViewM}}"
DisplayMemberPath="Naziv">
</ComboBox>
.........................................
//MainViewModel
public SIDPoslJavnaUstanova SelectedModel
{
get { return _selectedModel; }
set
{
if (_selectedModel != value)
{
_selectedModel = value;
RaisePropertyChanged("SelectedModel");
}
}
}
And yes, this is some awful design.
I want to execute some function when user selects a different value in the combo box.
I am using MVVM pattern .
Any Idea how I Data Bind in this case ?
bind to a property on SelectedItem.
in the property's setter, execute your function.
SelectedItem="{Binding Path=SomeProperty}"
You'd want to either bind to the SelectedValue, SelectedItem, or SelectedIndex depending on what you want to do:
public class MyViewModel : INotifyPropertyChanged
{
public ObservableCollection<MyObj> MyCollection { get; private set; }
private MyObj _theItem;
public MyObj TheItem
{
get { return _theItem; }
set
{
if (Equals(value, _theItem)) return;
_theItem= value;
//Or however else you implement this...
OnPropertyChanged("TheItem");
//Do something here.... OR
//This will trigger a PropertyChangedEvent if you're subscribed internally.
}
}
private string _theValue;
public string TheValue
{
get { return _theValue; }
set
{
if (Equals(value, _theValue)) return;
_theValue= value;
OnPropertyChanged("TheValue");
}
}
private int _theIndex;
public int TheIndex
{
get { return _theIndex; }
set
{
if (Equals(value, _theIndex)) return;
_theIndex = value;
OnPropertyChanged("TheIndex");
}
}
}
public class MyObj
{
public string PropA { get; set; }
}
<!-- Realistically, you'd only want to bind to one of those -->
<!-- Most likely the SelectedItem for the best usage -->
<ItemsControl ItemsSource="{Binding Path=MyCollection}"
SelectedItem="{Binding Path=TheItem}"
SelectedValue="{Binding Path=TheValue}"
SelectedIndex="{Binding Path=TheIndex}"
/>