WPF Combobox is not updating when collection changed in Model.
I am using ICollectionView for both DataGrid and ComboBox. DataGrid is updating when collection is changed in model, but ComboBox is not updating. Please let me know if there are any alternate ways to do this.
Here is the code
Model->
In Model I have
public ObservableCollection<Product> MyModelProducts
ViewModel->
DataGrid Collection
public ICollectionView MyViewModelProducts
{
get
{
return CollectionViewSource.GetDefaultView(MyModel.Instance.MyModelProducts);
}
}
ViewModel-ComboBox Collection
public ICollectionView MyViewModelListOfProducts
{
get
{
return CollectionViewSource.GetDefaultView(MyModel.Instance.MyModelProducts.Select(p => p.Category).Distinct().ToList<string>());
}
}
Code in View-->
<ComboBox ItemsSource="{Binding MyViewModelListOfProducts, Mode=OneWay}" />
Binding MyViewModelProducts to DataGrid
Binding MyViewModelListOfProducts to a ComboBox.
You have to refer to the selected product in offer to filter
Set IsSynchronizedWithCurrentItem to true in the DataGrid.
Raise a property change of MyViewModelListOfProducts when you want it to update. You subscribe the the MyViewModelProducts.CurrentChanged event and update it from there.
You can have a property DistinctCategories or so in the Product class that does that linq query (and is notified upon change, of course), and then bind the ComboBox to the MyViewModelProducts and point to this property - this I think is the preferred way, unless you think the distinct categories is has really no use in the model layer.
Related
I got stuck on a problem. In the project I am working on I have to populate one column of ListView with cheboxes and another one with comboboxes. The following is the data model that I am using as an ObservableCollection to bind it to a listview. Works really well!
public class PointDataMainListView
{
public string CheckBoxName { get; set; }
public ObservableCollection<string> ComboBoxItems{ get; set; }
public Visibility visibility { get; set; }
}
Except I have a hard time changing the properties of the combobox itself. In particular the visibility property.
The following is the data template i am using for the combo box
<DataTemplate x:Key="ComboBoxCell">
<ComboBox x:Name="ComboBox"
ItemsSource="{Binding ComboBoxItems}"
Width="100"
Visibility="{Binding visibility}"/>
</DataTemplate>
When populating the listview for the first time or adding a new item to a listview visibility could be set no problem. When visibility inside my ObservableCollection < PointDataMainListView > is changed for the item already displayed nothing is happening.
One of the solution I was looking into is trying to itterate through a list view items to try and get a reference to the actual combobox to change it's property. That said, I believe there must be a more elegant solution to achieve the desired results. Thank you for any help.
Your class needs to implement INotifyPropertyChanged and your properties setters need to invoke the PropertyChanged method.
I'm very new to WPF. I'm trying to bind to a property a row in a DataGrid so that when the row's clicked the property is set. The ItemsSource that's bound to the DataGrid is an ObservableCollection of objects of type Field.
I've tried to bind to the SelectedItem attribute on the DataGrid, but the property is not being called. I'm using almost identical code to bind to the SelectedItem of a ComboBox and this is working fine. Is there a difference that I don't know about?
<ComboBox ItemsSource="{Binding RecordTypes}" SelectedItem="{Binding SelectedRecordType}" ...
<DataGrid ItemsSource="{Binding Fields}" SelectedItem="{Binding SelectedField}" ...
In my ViewModel:
private Field SelectedField
{
get
{
return _selectedField;
}
set
{
_selectedField = value;
}
}
(I will use auto properties later, it's just currently set up like this so that I could break when the property was set).
I'm not sure if it makes a difference, but the DataGrid is composed of 2 DataGridTextColumns and a DataGridTemplateColumn, which contains a checkbox.
Does anyone have any ideas? I'd really appreciate any suggestions.
To confirm, the reason that I want to listen to the click of a row is so that I can have the checkbox be checked whenever a row is clicked. If there's a better solution for this then please let me know.
You need to make it a two-way binding:
SelectedItem="{Binding SelectedField,Mode=TwoWay}"
That propagates changes in the view (user selects an item, SelectedItem changes) back to the viewmodel ("SelectedField" property).
Also, as #KevinDiTraglia pointed out, you need to make sure that the viewmodel property SelectedField is public, not private, otherwise the binding will not be able to access the getter/setter.
I have a ComboBox in WPF binding its ItemsSource Property to a Property returning an IEnumerable of String. The binding is just one-way. The class that contains the data for the ComboBox implements INotifyPropertyChanged Interface and calls the OnPropertyChanged(..) as soon as the Property gets updated. When I leave the ComboBox untouched the changes are correctly propagated. But as soon as the ComboBox is expanded once or a value is selected the changes in the ItemsSource Collection are no longer updated. What may be the reason for this behaviour?
Heres the XAML
<ComboBox Name="cmbSourceNames"
Grid.Row="0"
SelectedItem="{Binding Path=CurrentSource, UpdateSourceTrigger=PropertyChanged}"
ItemsSource="{Binding Path=SourceAddresses, NotifyOnSourceUpdated=True}"/>
The DataContext is set in the code behind:
this.cmbSourceNames.DataContext = this._dpa;
And this one is the Method that triggers the change of the Property. The Method for adding the Packet is delegated to the Current Dispatcher with BeginInvoke.
private void DispatcherAddDataPacket(DataPacket dp)
{
ObservableCollection<DataPacket> dpList;
this._dpkts.TryGetValue(dp.SourceAddress, out dpList);
if (dpList == null)
{
dpList = new ObservableCollection<DataPacket>();
dpList.Add(dp);
this._dpkts.Add(dp.SourceAddress, dpList);
OnPropertyChanged("SourceAddresses");
}
else
{
dpList.Add(dp);
}
}
The Property is giving back the Keys of the Dictionary as IEnumerable.
Finally I implemented the Binding Property using an ObservableCollection tracking the keys when a new Packet gets added (so every key to the Dictionary has an equivalent in this ObservableCollection). I think it's not really a satisfying solution (because you have to keep track of both Collections independently) but it works like this.
I have a View that is linked to my ViewModel using a DataTemplate, like this
<DataTemplate DataType="{x:Type ViewModels:ViewModel}">
<Views:View />
</DataTemplate>
The ViewModel holds a property ProcessOption that is of type MyEnum?, where MyEnum is a custom enumeration that has let's say 3 values: Single, Multiple and All. I am trying to bind a combobox to this property, so the approach I am following is:
ViewModel has a property of List<string> that is
public List<string> Options
{
get
{
return Enum.GetNames(typeof(MyEnum)).ToList();
}
}
to which I bind the ItemsSource property of the Combobox. Then, in addition to the ProcessOption property, the ViewModel also has an OptionName property (of string), which is intended to hold the selected option's name. The ViewModel implements INotifyPropertyChanged and both properties raise the PropertyChanged event in their setters. The binding I am using then is:
<ComboBox ItemsSource="{Binding Options}"
SelectedItem="{Binding OptionName}"
SelectedValue="{Binding ProcessOption}"/>
This works fine up to this point. Initially the combobox is empty and both properties are null, and when the user selects an option this is propagated to the ViewModel as it should.
The problem appears when I load the data from a Database, and I want to load the controls with initial values. In this case, in the ViewModel's constructor I have this:
this.ProcessOption = objectFromDB.ProcessOption // this is the value restored from DB, let's say it is MyEnum.Multiple
this.OptionName = Options.First(x => x.Equals(Enum.GetName(typeof(MyEnum), objectFromDB.ProcessOption)));
The problem is, although the above sets the two properties to their correct values, they are later set to null from the Combobox binding, so the initial values are not kept. I have also tried to do something like if (value == null) { return; } in the properties' setters, in which case they have the correct values after the View loads, however the Combobox still does not display the correct option, it is empty.
I should also note that I've also tried setting IsSynchronisedWithCurrentItem and it doesn't make any difference, apart from the fact that the first element is displayed instead of the combobox being empty.
Can anyone help with this binding? Any help will be very much appreciated, this is driving me crazy!
<ComboBox ItemsSource="{Binding Options}"
SelectedItem="{Binding OptionName}"
SelectedValue="{Binding ProcessOption}"/>
Your binding doesn't look like it should work at all -- you don't have TwoWay binding set up, and I think SelectedItem and SelectedValue is an either/or proposition.
I suggest that you get rid of OptionName and just bind SelectedItem to ProcessOption (TwoWay) with an IValueConverter that will convert to/from string.
I'm working on my first true WPF MVVM pattern application.
Currently I have a number of ComboBoxes on various screens that are bound to Collection classes and properties of the relevant ViewModel class.
They always have an entry with the text <Add>, which is really an empty object class and I currently use it to trigger an AddNewObject event if the Property bound to the SelectedItem has <Add> in its ToString() output. This strikes me as cumbersome and it ties the View too closely to the View model for my liking.
e.g.
<ComboBox ItemsSource="{Binding AllObjects}" SelectedItem="{Binding SelectedObject}" />
then in ViewModel code:
public SomeObjectType SelectedObject
{
get{return this.fieldSomeObjectType;}
set
{
if(null==value)
return;
if(value.ToString().Contains(#"<Add>"))
{
if(null!=this.AddNewObject)
{
this.AddNewObject;
}
}
}
}
Is there a way in XAML of adding this extra line into the ComboBox drop down list and binding it to an AddNewObject Command?
The code you've written in your view has nothing to do with your business logic. Its fine.
MVVM doesn't say that you shouldn't have anything in the codebehind of the view. Showing 'Add' is a requirement on the view and can be handled by the code behind of view.
In ASP.NET I've been doing this that I databinded the list control to some data but also specified some items in the html and it would merge them. Have you tried that?
use CompositeCollection for merging a default item with a itemsource. http://msdn.microsoft.com/en-us/library/ms742405.aspx