Combobox ItemSource not binding with data from ViewModel - c#

I have a Combobox:
<ComboBox Height="23"
Name="DriveSelection" Width="120"
ItemsSource="{Binding Path=FixedDrives}"
DisplayMemberPath="fixedDrives"
SelectedItem="{Binding Path=DriveSelection_SelectionChanged}"
IsSynchronizedWithCurrentItem="True"/>
Here code for ItemsSource:
private ObservableCollection<DriveInfo> fixedDrives;
public ObservableCollection<DriveInfo> FixedDrives
{
get
{
if(fixedDrives==null)
{
var query =
from driveInfo in DriveInfo.GetDrives()
//where driveInfo.DriveType == DriveType.Fixed
select driveInfo;
fixedDrives= new ObservableCollection<DriveInfo>(query);
return fixedDrives;
}
return fixedDrives;
}
}
and here event handler:
private void DriveSelection_SelectionChanged()
{
if (page.DriveSelection.IsEnabled)
{
this.UpdatePathManager();
}
}
I checked similiar questions like this one or this one and didnt find there any answers.
I know that ViewModel is bounded to View. Other binds to buttons etc are working.
After Updates:
private DriveInfo driveSelection;
public DriveInfo DriveSelection_SelectionChanged
{
get
{
return driveSelection;
}
set
{
if (value == driveSelection) return;
driveSelection = value;
NotifyOfPropertyChange(() => UpdatePathManager()); //UpdatePatchmanager is my function and it exists.
//Notify... throws does not exists in currenct context
}
}
XAML:
<ComboBox Height="23"
Name="DriveSelection"
Width="120"
ItemsSource="{Binding Path=FixedDrives}"
DisplayMemberPath="Name"
SelectedItem="{Binding Path=DriveSelection_SelectionChanged}"
IsSynchronizedWithCurrentItem="True" />
and binding the ViewModel:
public PathSelectionPage()
{
InitializeComponent();
this.DataContext = new PathSelectionPageViewModel(this);
}
After all thouse updates Combobox is still without any options and its greyed out.
And NotifyOfPropertyChange is throwing does not exists in current context
and:
class PathSelectionPageViewModel : ObservableObject, INavigable, INotifyPropertyChanged

Your DisplayMemberPath should be a property name within your DriveInfo class and not DisplayMemberPath="fixedDrives" and SelectedItem should be a property on VM of type DriveInfo not a function

Your DisplayMemberPath Should be a property of your Collection not the Collection Itself.
From this to:
DisplayMemberPath="fixedDrives"
Something Like this:
<ComboBox Height="23"
Name="DriveSelection" Width="120"
ItemsSource="{Binding Path=FixedDrives}"
DisplayMemberPath="Property1"
SelectedItem="Property"
IsSynchronizedWithCurrentItem="True"/>

You don't need write event handler because it is nor MVVM way. You must write something like this
<ComboBox Height="23"
Name="DriveSelection" Width="120"
ItemsSource="{Binding Path=FixedDrives}"
DisplayMemberPath="PropertyOfDriveInfo"
SelectedItem="{Binding Path=SelectedInfo}" />
class ViewModel: INotifyPropertyChanged
{
private ObservableCollection<DriveInfo> fixedDrives;
public ObservableCollection<DriveInfo> FixedDrives
{
get
{
if(fixedDrives==null)
{
var query =
from driveInfo in DriveInfo.GetDrives()
//where driveInfo.DriveType == DriveType.Fixed
select driveInfo;
fixedDrives= new ObservableCollection<DriveInfo>(query);
return fixedDrives;
}
return fixedDrives;
}
}
private DriveInfo _selectedInfo
public DriveInfo SelectedInfo
{
get { return _electedInfo; }
set
{
if (value == _electedInfo) return;
_selectedInfo= value;
NotifyOfPropertyChange(() => SelectedInfo);//must be implemented
}
}
}

Related

MVVM Prism 5 WPF and combo boxes

I'm not understanding why my combo box doesn't update when I make a selection in another combobox. I'm still new to MVVM but in theory my code should work. I can populate the combos when the form loads but i need to refresh the combo with new values, and that's not working. I can see it retrieving new values but it never displays them on the form.
My XAML looks like this:
<ComboBox Grid.Column="1" ItemsSource="{Binding Path=Vendors}" SelectedItem="{Binding SelectedVendor, Mode=TwoWay}" HorizontalAlignment="Left" Margin="24,12,0,11" Grid.Row="3" VerticalAlignment="Center" Width="293" />
<ComboBox x:Name="VendorProductServiceCB" HorizontalAlignment="Left" Margin="20.6,16.2,0,55.4" VerticalAlignment="Center" Width="293" Grid.Row="7" Grid.Column="1" ItemsSource="{Binding Path=VendorProductServices, UpdateSourceTrigger=PropertyChanged}" DisplayMemberPath="Name" SelectedItem="{Binding Path=SelectedVendorProductService, Mode=TwoWay}" Height="22"/>
My ViewModel code is this:
public ObservableCollection<string> Vendors { get; set; }
public ObservableCollection<VendorProductService> VendorProductServices { get; private set; }
public VendorProductService SelectedVendorProductService
{
get { return _selectedVendorProductService; }
set { SetProperty(ref _selectedVendorProductService, value); }
}
public string SelectedVendor
{
get { return _selectedVendor; }
set
{
SetProperty(ref _selectedVendor, value);
SelectionChangedCommand.Execute(this);
}
}
public FormSelectionViewModel()
{
Vendors = new ObservableCollection<string>(FetchVendors());
VendorProductServices = new ObservableCollection<VendorProductService>(FetchVendorProductServices(_selectedVendor));
SelectionChangedCommand = new DelegateCommand(SelectionChanged);
}
public void SelectionChanged()
{
VendorProductServices = new ObservableCollection<VendorProductService>(FetchVendorProductServices(_selectedVendor));
}
You're not raising the PropertyChanged event for the VendorProductServices, because it's an auto property.
Either change it to:
private ObservableCollection<VendorProductService> _vendorProductServices;
public ObservableCollection<VendorProductService> VendorProductServices
{
get { return _vendorProductServices; }
private set { SetProperty(ref _vendorProductServices, value); }
}
Or change your collection properties to read only and use .Clear() and .Add() instead of creating new collections.

C# WPF Combobox select first item

Goodday,
I want my combobox to select the first item in it. I am using C# and WPF. I read the data from a DataSet. To fill the combobox:
DataTable sitesTable = clGast.SelectAll().Tables[0];
cbGastid.ItemsSource = sitesTable.DefaultView;
Combo box XAML code:
<ComboBox
Name="cbGastid"
ItemsSource="{Binding}"
DisplayMemberPath="Description"
SelectedItem="{Binding Path=id}"
IsSynchronizedWithCurrentItem="True" />
If I try:
cbGastid.SelectedIndex = 0;
It doesn't work.
Update your XAML with this:
<ComboBox
Name="cbGastid"
ItemsSource="{Binding}"
DisplayMemberPath="Description"
SelectedItem="{Binding Path=id}"
IsSynchronizedWithCurrentItem="True"
SelectedIndex="0" /> // Add me!
Try this, instead of SelectedIndex
cbGastid.SelectedItem = sitesTable.DefaultView.[0][0]; // Assuming you have items here.
or set it in Xaml
<ComboBox
Name="cbGastid"
ItemsSource="{Binding}"
DisplayMemberPath="Description"
SelectedItem="{Binding Path=id}"
IsSynchronizedWithCurrentItem="True"
SelectedIndex="0" />
It works for me if I add a SelectedIndex Property in my VM with the proper binding in the xaml. This is in addition to the ItemSource and the SelectedItem. This way SelectedIndex defaults to 0 and I got what I wanted.
public List<string> ItemSource { get; } = new List<string> { "Item1", "Item2", "Item3" };
public int TheSelectedIndex { get; set; }
string _theSelectedItem = null;
public string TheSelectedItem
{
get { return this._theSelectedItem; }
set
{
this._theSelectedItem = value;
this.RaisePropertyChangedEvent("TheSelectedItem");
}
}
And the proper binding in the xaml;
<ComboBox MaxHeight="25" Margin="5,5,5,0"
ItemsSource="{Binding ItemSource}"
SelectedItem="{Binding TheSelectedItem, Mode=TwoWay}"
SelectedIndex="{Binding TheSelectedIndex}" />
Update your XAML with this code :
<ComboBox
Name="cbGastid"
ItemsSource="{Binding}"
DisplayMemberPath="Description"
SelectedItem="{Binding Path=id, UpdateSourceTrigger=PropertyChanged, Mode=OneWayToSource}"
IsSynchronizedWithCurrentItem="True" />
Hope it works :)
Try this,
remove from de C# code the following line:
cbGastid.ItemsSource = sitesTable.DefaultView;
and add this:
cbGastid.DataContext = sitesTable.DefaultView
Try this..
int selectedIndex = 0;
cbGastid.SelectedItem = cbGastid.Items.GetItemAt(selectedIndex);
XAML Code:
<ComboBox
Name="cbGastid"
ItemsSource="{Binding}"
DisplayMemberPath="Description"
SelectedItem="{Binding Path=id}"
IsSynchronizedWithCurrentItem="True" />
This works for me... Given an Authors and Books table with a one-to-many relationship. The XAML Looks like this:
<ComboBox DisplayMemberPath="AuthorName" ItemsSource="{Binding Authors}" Name="ComboBoxAuthors"
SelectedItem="{Binding SelectedAuthor}"
IsSynchronizedWithCurrentItem="True" Grid.Row="0" Grid.Column="0"/>
<ComboBox DisplayMemberPath="BookTitle" ItemsSource="{Binding Books}" Name="ComboBoxBooks"
SelectedItem="{Binding SelectedBook}"
IsSynchronizedWithCurrentItem="True" Grid.Row="0" Grid.Column="1" />
Then my ViewModel looks like this:
enter public class MainViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
BooksEntities ctx = new BooksEntities();
List<Author> _authors;
List<Book> _books;
Author _selectedAuthor;
Book _selectedBook;
public MainViewModel()
{
FillAuthors();
}
public List<Author> Authors
{
get { return _authors; }
set
{
_authors = value;
NotifyPropertyChanged();
if (_authors.Count > 0) SelectedAuthor = _authors[0]; // <--- DO THIS
}
}
public Author SelectedAuthor
{
get { return _selectedAuthor; }
set
{
_selectedAuthor = value;
FillBooks();
NotifyPropertyChanged();
}
}
public List<Book> Books
{
get { return _books; }
set
{
_books = value;
NotifyPropertyChanged();
if (_books.Count > 0) SelectedBook = _books[0]; // <--- DO THIS
}
}
public Book SelectedBook
{
get { return _selectedBook; }
set
{
_selectedBook = value;
NotifyPropertyChanged();
}
}
#region Private Functions
private void FillAuthors()
{
var q = (from a in ctx.Authors select a).ToList();
this.Authors = q;
}
private void FillBooks()
{
Author author = this.SelectedAuthor;
var q = (from b in ctx.Books
orderby b.BookTitle
where b.AuthorId == author.Id
select b).ToList();
this.Books = q;
}
#endregion
}
Take a look at the Authors and Books properties of the ViewModel class. Once they are set, the usual PropertyChanged event is raised and the SelectedAuthor / SelectedBook is set to the first item.
Hope this helps.
Let me share my solution, that worked for me after several trials.
Here is my combo box:
<ComboBox
Name="fruitComboBox"
ItemsSource="{Binding Fruits}"
SelectedIndex="0"
SelectedValue="{Binding ComboSelectedValue}"
IsSynchronizedWithCurrentItem="True">
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<i:InvokeCommandAction Command="{Binding displayFruitName}"
CommandParameter="{Binding SelectedValue, ElementName=fruitComboBox}"/>
</i:EventTrigger>
<i:EventTrigger EventName="Loaded">
<i:InvokeCommandAction Command="{Binding displayFruitName}"
CommandParameter="{Binding SelectedValue, ElementName=fruitComboBox}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</ComboBox>
In my case, I had to invoke a command every time a new item was selected in the comboBox or when the itemsource was being updated. But, the element at zero index was not getting selected when the item source was updated. So, what did I do? I added:
IsSynchronizedWithCurrentItem="True"
in the comboBox properties. It did the trick for me.
A little code from my ViewModel is below:
/// item source for comboBox
private List<string> fruits = new List<string>();
public List<string> Fruits
{
get { return fruits; }
set
{
fruits = value;
OnPropertyChanged();
ComboSelectedValue = value[0];
}
}
// property to which SelectedValue property of comboxBox is bound.
private string comboselectedValue;
public string ComboSelectedValue
{
get { return comboselectedValue; }
set
{
comboselectedValue = value;
OnPropertyChanged();
}
}
You can refer to this stackoverflow link and msdn link for further clarification regarding IsSynchronizedWithCurrentItem="True"
Hope it Helps! :)

Automatically add a combobox when you fill another one

Hi i need to make a program where you have to add an undefined number of elements to a list by choosing them from a combobox. I planned to use 4 basic comboboxes and when the user choose an element from the last one, the program should automatically add another one under the last one (i want to use a stackpanel).
How could i do?
Thanks.
My XAML:
<StackPanel Name="listPanel" Grid.Column="0" Margin="10">
<Label Content="Example" FontWeight="Bold" HorizontalAlignment="Center"/>
<ComboBox Name="ex1Combobox" Margin="0,10,0,0"
ItemsSource="{Binding ExList, Mode=TwoWay}"
SelectedValue="{Binding SelectedEx}"
DisplayMemberPath="Name"
SelectedValuePath="ID"/>
<ComboBox Name="ex2Combobox" Margin="0,10,0,0"
ItemsSource="{Binding ExList, Mode=TwoWay}"
SelectedValue="{Binding SelectedEx}"
DisplayMemberPath="Name"
SelectedValuePath="ID"/>
<ComboBox Name="ex3Combobox" Margin="0,10,0,0"
ItemsSource="{Binding ExList, Mode=TwoWay}"
SelectedValue="{Binding SelectedEx}"
DisplayMemberPath="Name"
SelectedValuePath="ID"/>
</StackPanel>
This is a pretty good example of why you should use MVVM.
Model
Has a collection of selected values only something like
public class MyChoices
{
public IEnumerable<string> Selections {get; set;}
}
ViewModel
Has a collection that extends as soon as you modify the last item
public class MyChoicesViewModel
{
public MyChoicesViewModel()
{
Selections = new ObservableCollection<ChoiceViewModel>();
//Add first empty value
AddNewItem();
Selections.CollectionChanged += (sender, e) =>
{
// If you change the last add another
if (e.NewItems.Contains(Selections.Last()))
AddNewItem();
};
}
public ObservableCollection<ChoiceViewModel> Selections {get; private set;}
public void AddNewItem()
{
var newItem = new ChoiceViewModel();
Selections.Add(newItem);
newItem.PropertyChanged += () =>
{
//This is where we update the model from the ViewModel
Model.Selections = from x in Selections
select x.Value;
}
}
}
public class ChoiceViewModel : INotifyPropertyChanged
{
private string _chosen;
public string Chosen
{
get { return _chosen; }
set {
if (_chosen != value)
{
_chose = value;
OnPropertyChanged();
}
}
}
public void OnPropertyChanged([CallerMemberName] string property)
{
var temp = PropertyChanged;
if (temp != null)
{
temp(this, new PropertyChangedEventArgs(property));
}
}
}
}
View
<!-- Then show many of them-->
<ListBox ItemsSource="{Binding Selections}"/>

Errors when binding selectedItem on a combobox

I want to databind the selected item of a combobox to a c# property. When i do the following, the property get the value "Suite.Module.RateExperiment.ViewModels.ChamberViewModel"(which is not the value of the selected item in the combobox):
<ComboBox DisplayMemberPath="ChamberName" Grid.Column="0" Grid.Row="1" Height="20" VerticalAlignment="Top" ItemsSource="{Binding ChamberCollection}" SelectedValue="{Binding SelectedChamber}">
</ComboBox>
And c#:
public string SelectedChamber
{
get { return _selectedChamber; }
set
{
_selectedChamber = value;
UpdateChart();
}
}
Am i binding wrong since this property gets this value?
SelectedChamber property should be of type ChamberViewModel, try changing it as below:
public ChamberViewModel SelectedChamber
{
get { return _selectedChamber; }
set
{
_selectedChamber = value;
UpdateChart();
}
}

Notify viewmodel that model has changed (from combobox)

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.

Categories

Resources