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.
Related
I use MVVM Light.
My Model inherits from MVVM Light's ObservableObject.
The Model has a property X
public float X
{
get
{
return x_;
}
set
{
Set<float>(() => this.X, ref x_, value);
}
}
In my ViewModel I have a property X
public float X
{
get
{
myModel.X;
}
}
My View has a label where the content is binding to the ViewModel property X.
(DataContext is set to ViewModel)
When the value of property X in Model is updated from code, the label in the view never gets updated.
Question what is the recommended way to make sure that the View correctly reflects the updated value?
(If I make a property in my ViewModel that returns the Model, I could bind direclty to Model.X in my View. But I want my View to bind to the ViewModel not directly down to the Model)
When myModel.X fires the PropertyChanged event, that does not automatically fire the ViewModel's PropertyChanged.
Moreover, if your Model class already implements INotifyPropertyChanged, there is no need to duplicate the X property in the ViewModel class.
Just turn the myModel member into a public property
public class ViewModel
{
public Model MyModel { get; set; }
}
and bind to it by {Binding MyModel.X}.
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'm using MVVM light toolkit to handle a button click. If I do:
CustomerSaveCommand = new RelayCommand(
() => CustomerSave(),
()=> true);
private void CustomerSave() {
customer.Address="My Street";
}
The function is invoked but the Address field bound in the UI is not updated.
If I put customer.Address="1234" in the ViewModel constructor, the UI IS updated. What am I doing wrong?
EDITED:
The problem is really strange: if I do viewModel.customer.City = "CITY1" in the window load it runs, if I add a button and, in the code-behind click, I add viewModel.customer.City = "CITY2" it does not work.
The customer object in your viewmodel needs to implement the INotifyPropertyChanged interface.
Then in the Address Property setter, you would invoke the PropertyChanged event.
Alternatively, your viewModel can implement the INotifyPropertyChanged interface, and could wrap the Address property and call the PropertyChanged event. You would have to update your bindings, but your model objects wouldn't have to implement any interfaces.
The reason you're seeing that the address is showing up when you modify the object in the constructor is because binding has not taken place yet. In order for the UI to be updated you need to instruct the binding engine that a property binding has changed. To do that you use the INotifyPropertyChanged interface.
try something like this:
public class AutoDelegateCommand : RelayCommand, ICommand
{
public AutoDelegateCommand(Action<object> execute)
: base(execute)
{
}
public AutoDelegateCommand(Action<object> execute, Predicate<object> canExecute)
: base(execute, canExecute)
{
}
event EventHandler ICommand.CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
}
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
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.