Description:
First I created an ObservableCollection which is accessed via the public property MyCollection. Now if I bind my DataGrid UI to MyCollection it will recognize collection changes, but not if MyCollection itself changes (ie. UpdateCollection method). To solve this issue I applied the familiar 'PropertyChanged("MyCollection")' to the MyCollection Property.
Now I found the need to group my DataGrid Content which requires a Collection View layer. When I added and binded to the CollectionView the UI no longer updates when MyCollection gets re-assigned. I read that only CollectionChanged propagate from the Source to the View. I guess in my case it is the PropertyChange on MyCollection that needs to somehow trigger a CollectionChanged event on the Source or View.
Question:
How can I get a re-assigmend on MyCollection to trigger a UI update, which is bound to a View of MyCollection?
Note: The reason for re-assigning MyCollection is due to a Modular MEF/MVVM architecture.
public class MyViewModel
{
public MyViewModel()
{
MyCollectionViewSource = new CollectionViewSource() { Source = MyCollection};
// The DataGrid is bound to this ICollectionView
MyCollectionView = MyCollectionViewSource.View;
}
// Collection Property
// NotifyPropertyChanged added specifically to notify of MyCollection re-assignment
ObservableCollection<MyObject> _MyCollection;
public ObservableCollection<MyObject> MyCollection
{
get {return _MyCollection;}
set {if (value != _MyCollection)
{_MyCollection = value;
NotifyPropertyChanged("MyCollection");}}
}
public MyCollectionViewSource PropertiesCollectionViewSource { get; private set; }
public ICollectionView = MyCollectionView { get; private set; }
// Method updates MyCollection itself (Called via ICommand from another ViewModel)
public void UpdateCollection(ObservableCollection<MyObject> NewCollection)
{
MyCollection = NewCollection;
}
}
Thanks,
Have a look at the Active Grouping Collection, its aimed at a different problem but might solve yours.
Building a smarter WPF CollectionView
Active Collection View on CodePlex
Related
I have a user control whose DataContext is bound to its view model which should display Status Messages from any other UserControls which are bound to their respective view models.
Status Messages is an ObservableCollection<StatusGridModel>. Now I am trying to use EventAggregation in order to pass this collection from my different view models to StatusViewModel.
In the constructor of every view model I have IEventAggregator of Prism resolved by Unity. Now on ButtonClick in first ViewModel I am doing the following:
DashBoardStatusCol.Add(statusGridModel);
eventAggregator.GetEvent<StatusEvent>().Publish(DashBoardStatusCol);
where StatusEvent class derives from PubSubEvent
public class StatusEvent : PubSubEvent<ObservableCollection<StatusGridModel>>
{
}
Below is my second view model where the Status Messages should be displayed. I have subscribed to my StatusEvent class in the constructor.
public class StatusGridViewModel : ViewModelBase<PresentationModel>
{
IEventAggregator eventAggregator;
public StatusGridViewModel(IEventAggregator eventAggregator)
{
this.eventAggregator = eventAggregator;
this.eventAggregator.GetEvent<StatusEvent>().Subscribe(SetStatus, true);
}
private void SetStatus(ObservableCollection<StatusGridModel> collection)
{
StatusCollection = collection;
}
private ObservableCollection<StatusGridModel> statusCollection;
public ObservableCollection<StatusGridModel> StatusCollection
{
get { return statusCollection; }
set { statusCollection = value; }
}
}
Now my problem is the subscribed event SetStatus is never called when the button is clicked in the first view model.
What am i missing? Should my ObservableCollection implement INotifyPropertyChanged and on the setter of property I should publish my event?
Should my ObservableCollection implement INotifyPropertyChanged and on the setter of property I should publish my event?
StatusCollection should raise the PropertyChanged event, so the view knows it should update its bindings. ObservableCollection only works if the content is updated (Add, Remove...), not if you replace the whole collection.
I'm following MVVM pattern. I have a listview control which has multiple checkboxes. my viewmodel has collection of Student which is bounded to listview control.
public ObservableCollection<Student> students{ get; private set; }
private ObservableCollection<Student> _displays { get; set; }
viewmodel doesn't know anything about the view so it doesn't access to the listview control
I tried by defining the Student class by below
public class Student
{
public string Name{ get; set; }
public string class { get; set; }
}
In viewmodel, i want to perform some action when user select/unselect the checkbox.
how can I get which items are checked or not, how can i get selected item state in viewmodel?
I'm following mvvm pattern.
In WPF, we generally use data binding. This means that ideally, you would have data bound a property of your Student class to a Checkbox in the UI:
public class Student : INotifyPropertyChanged
{
public bool IsSomething { get; set; } // Implement INotifyPropertyChanged here
...
}
...
<Checkbox IsChecked="{Binding IsSomething}" />
If you do this, then you can find out which Checkboxes were checked simply by looking at the relevant Student object from your view model:
bool isSomething = CurrentStudent.IsSomething;
If you want to react to the changing value, then you just have to monitor the property for changes:
public bool IsSomething
{
get { return isSomething; }
set
{
if (value != isSomething)
{
isSomething= value;
NotifyPropertyChanged("IsSomething");
if (isSomething) CheckedBoxWasChecked();
else CheckedBoxWasUnChecked();
}
}
}
Can you check multiple items at a time?
If not, you can simple add a SelectedItem property to your view model and bind the SelectedItem property of the listView to the SelectedItem property of the view model.
If you need to be able to check more than one item at a time, you can add a boolean IsSelected property to the Student class. Then in your data template for the list view, bind the IsChecked property of the checkbox to the IsSelected property of the Student.
Please make sure your view model and student class implement INotifyPropertyChanged, etc.
I have a UserControl with a ComboBox in it and I'm binding an ObservableCollection to it such as follow. Right now the collection is populated in the UserControl. However, I would like to create the ObservableCollection in the MainWindow and have another constructor for my UserControl. here's what I got now and it's working:
public ObservableCollection<ComboBoxInfo> Items { get; private set; }
public CustomComboBox()
{
InitializeComponent();
Items = new ObservableCollection<ComboBoxInfo>();
cmb.ItemsSource = Items;
if (!System.ComponentModel.DesignerProperties.GetIsInDesignMode(this))
{
this.createNameComboBox(); // ObservatoryCollection populating
}
}
I tried implementing a second constructor and moving the collection populating function in the Main Window but I get an error saying my comboBox in the UserControl is not set to an instance of an object. Ideally I would like something like this:
public CustomComboBox(ObservableCollection<ComboBoxInfo> Items)
{
this.Items = Items
// Not sure if the binding should be done here or in default constructor
}
Any idea how to properly do this ? Thanks
Your solution should include a ViewModel which would be set as a DataContext of your User Control.
This ViewModel should contain and expose the ObservableCollection as a public property, ideally it should use some injected service provider to obtain the data from some data store and populate the ObservableCollection with that data, Finally, the ComboBox from your User Control should bind to that ObservableCollection in the ViewModel.
Your User Control code-behind should have no code other than some event handlers to manipulate the UI in response to UI events if necessary...
That is how things are done properly in WPF utilizing the MVVM pattern.
Here is an example of how a service is injected into the VM constructor and used to populate a collection with some data:
public class MainWindowViewModel
{
private ICustomerService _customerService;
public MainWindowViewModel(ICustomerService customerService)
{
_customerService = customerService;
Customers = new ListCollectionView(customerService.Customers);
}
public ICollectionView Customers { get; private set; }
}
I set DataContext:
this.DataContext = new MainWindowViewModel();
And I am binding the ItemsSource of a TabControl, when I add a new TabItem in the contructor of MainWindowViewModel it is working! But when I add a new TabItem in an event (Click) there is no effect.
I have this property:
List<Item> _listOfItem;
public List<Item> ListOfItem
{
get
{
return _listOfItem;
}
set
{
_listOfItem = value;
PropertyChanged(this, new PropertyChangedEventArgs("ListOfItem"));
}
}
Please help.
You should use an ObservableCollection, rather than a List if you wish the UI to be notified of collection changes.
ObservableCollection<Item> _listOfItem;
public ObservableCollection<Item> ListOfItem
{
get
{
return _listOfItem;
}
set
{
_listOfItem = value;
PropertyChanged(this, new PropertyChangedEventArgs("ListOfItem"));
}
}
Note that you only need to invoke the PropertyChanged event for your ListOfItem if the reference changes after construction of your view model type. If it doesn't change, then a simple auto property will suffice for ListOfItem.
For collection changes you need the source collection to implement INotifyCollectionChanged, you could use an ObservableCollection<T> (which implements it) instead of a List<T>.
You need to use and ObservableCollection for the UI to see additions and deletions to the collection. It worked in the constructor as the List is was built for the the UI.
List will not work.
You should use ObservableCollection for the ListOfItem.
Short Version
If I update the Model object that my ViewModel wraps, what's a good way to fire property-change notifications for all the model's properties that my ViewModel exposes?
Detailed Version
I'm developing a WPF client following the MVVM pattern, and am attempting to handle incoming updates, from a service, to data being displayed in my Views. When the client receives an update, the update appears in the form of a DTO which I use as a Model.
If this model is an update to an existing model being shown in the View, I want the associated ViewModel to update its databound properties so that the View reflects the changes.
Let me illustrate with an example. Consider my Model:
class FooModel
{
public int FooModelProperty { get; set; }
}
Wrapped in a ViewModel:
class FooViewModel
{
private FooModel _model;
public FooModel Model
{
get { return _model; }
set
{
_model = value;
OnPropertyChanged("Model");
}
}
public int FooViewModelProperty
{
get { return Model.FooModelProperty; }
set
{
Model.FooModelProperty = value;
OnPropertyChanged("FooViewModelProperty");
}
}
The Problem:
When an updated model arrives, I set the ViewModel's Model property, like so:
instanceOfFooVM.Model = newModel;
This causes OnPropertyChanged("Model") to fire, but not OnPropertyChanged("FooViewModelProperty"), unless I call the latter explicitly from Model's setter. So a View bound to FooViewModelProperty won't update to display that property's new value when I change the Model.
Explicitly calling OnPropertyChanged for every exposed Model property is obviously not a desirable solution, and neither is taking the newModel and iterating through its properties to update the ViewModel's properties one-by-one.
What's a better approach to this problem of updating a whole model and needing to fire change notifications for all its exposed properties?
According to the docs:
The PropertyChanged event can indicate all properties on the object have changed by using either null or String.Empty as the property name in the PropertyChangedEventArgs.
One option is to listen to your own events, and make a helper routine to raise the other notifications as required.
This can be as simple as adding, in your constructor:
public FooViewModel()
{
this.PropertyChanged += (o,e) =>
{
if (e.PropertyName == "Model")
{
OnPropertyChanged("FooViewModelProperty");
// Add other properties "dependent" on Model here...
}
};
}
Whenever your Model property is set, subscribe to its own PropertyChanged event. When your handler gets called, fire off your own PropertyChanged event. When the Model is set to something else, remove your handler from the old Model.
Example:
class FooViewModel
{
private FooModel _model;
public FooModel Model
{
get { return _model; }
set
{
if (_model != null)
{
_model.PropertyChanged -= ModelPropertyChanged;
}
if (value != null)
{
value.PropertyChanged += ModelPropertyChanged;
}
_model = value;
OnPropertyChanged("Model");
}
}
public int FooViewModelProperty
{
get { return Model.FooModelProperty; }
set
{
Model.FooModelProperty = value;
OnPropertyChanged("FooViewModelProperty");
}
}
private void ModelPropertyChanged(object sender, PropertyChangedEventArgs e)
{
// Here you will need to translate the property names from those
// present on your Model to those present on your ViewModel.
// For example:
OnPropertyChanged(e.PropertyName.Replace("FooModel", "FooViewModel"));
}
}
Implements INotifyPropertyChanged
Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(String.Empty))
For VB.net if anybody else needs it. If you have already implemented "INotifyPropertyChanged" then the last line is all you need.