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; }
}
}
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.
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.
in my WPF UI, I use RoutedCommands that I refer to in my xaml via the following code:
Command="viewModel:MessageListViewModel.DeleteMessagesCommand"
I don't like this static link to my ViewModel class,I think this is not as nice as creating a custom ICommand implementation and use a syntax like the following
Command="{Binding DeleteMessagesCommand}"
Having created one, I notice one major drawback of what I've done: RoutedCommands utilize the CommandManager and (in some way that is completely opaque to me) fire the CommandManager.RequerySuggested event, so that their CanExecute Method is requeried automatically. As for my custom implementation, CanExecute is only fired once at startup and never again after that.
Does anybody have an elegant solution for this?
Just implement the CanExecuteChanged event as follows:
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
When you assign the command to a control, it subscribes to the CanExecuteChanged event. If you "redirect" it to the CommandManager.RequerySuggested event, the control will be notified whenever CommandManager.RequerySuggested is triggered.
I very much prefer the DelegateCommand implementation of Prism for viewmodel binding (http://msdn.microsoft.com/en-us/library/ff654132.aspx). You can invoke CanExecute() on every command invoker by calling RaiseCanExecuteChanged on it.
Simple usage example:
public class ViewModel
{
public ViewModel()
{
Command = new DelegateCommand<object>(x => CommandAction(), x => CanCommandAction());
}
bool state;
public void ChangeState(bool value)
{
state = value;
Command.RaiseCanExecuteChanged();
}
public DelegateCommand<object> Command {get; private set;}
private void CommandAction()
{
//do smthn
}
private bool CanCommandAction() { return true == state; }
}
//and binding as usual
Command="{Binding Command}"
What would be the cleanest way to have a Save state for an application so that whenever a property or content was updated the "Save" options would become enabled.
For example, there is a menu and toolbar "Save" buttons. When the WPF app first opens both buttons are disabled. When the user updates the properties or document, the buttons become enabled until a "Save" is done, at which point they go back to disabled.
Bind IsEnabled to a ViewModel that exposes an "IsDirty" or "HasBeenModified" boolean property, or something of similar ilk. The ViewModel would watch for changes to the Model and set IsDirty to true if the Model is modified for any reason. When saved, the ViewModel can be told to set IsDirty to false, disabling the button.
You are using the Model-View-ViewModel pattern, right? Here are a few links on the pattern to help you on your way, just in case:
http://en.wikipedia.org/wiki/Model_View_ViewModel
http://msdn.microsoft.com/en-us/magazine/dd419663.aspx
http://www.wintellect.com/CS/blogs/jlikness/archive/2010/04/14/model-view-viewmodel-mvvm-explained.aspx
Are all the properties you wish to watch for in the same class? If so, something like this will work:
Have the class derive from INotifyPropertyChanged;
Watch for property changes from the class and set an IsDirty flag when there are changes
Set IsEnabled for the Save button based on the IsDirty flag
When the Save command is executed, set IsDirty=false
Notifying Class Example
public class NotifyingClass : INotifyPropertyChanged
{
private string Property1Field;
public string Property1
{
get { return this.Property1Field; }
set { this.Property1Field = value; OnPropertyChanged("Property1"); }
}
private string Property2Field;
public string Property2
{
get { return this.Property2Field; }
set { this.Property2Field = value; OnPropertyChanged("Property2"); }
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
}
Watching for Property Changes
public partial class MainWindow : Window
{
private bool isDirty;
NotifyingClass MyProperties = new NotifyingClass();
public MainWindow()
{
InitializeComponent();
this.MyProperties.PropertyChanged += (s, e) =>
{
this.isDirty = true;
};
}
}
How you set the disabled/enabled state depends on what type of button/command implementation you are doing. If you would like further help just let me know how you are doing it (event handler, RoutedCommand, RelayCommand, other) and I'll check it out.
I have a Silverlight application in which I implemented MVVM pattern.
In my application there is a child window on which I have ComboBox. I bound ItemsSource and SelectedItem of my combobox to a property (typeof ObservableCollection) and property of MyType appropriately. MyType is a "MODEL" derived from INotifyPropertyChanged. When my window is loaded I set values to this properties. But my combobox doesn't display selected item.
I found that when I set property which is bound to selected item (in ViewModel), the PropertyChanged event is null.
Can anyone help me. Thanks.
From the way you've described it the only thing being bound to is the ViewModel yet the only thing that implements INotifyPropertyChanged is MyType. Nothing is actually binding to the instance of my type to listen to its PropertyChanged event which is why its null.
It sounds like you haven't implemented INotifyPropertyChanged on your ViewModel.
PropertyChanged works fine, so it must be in your implementation of it. Simply implementing INotifyProperty changed isn't good enough, you have to explicity call the event.
For example, this will not work:
public class Model : INotifyPropertyChanged
{
public string Title { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
}
In order for it to work, you must raise the property changed. Easiest way is to encapsulate the logic in a method, like this:
public class Model : INotifyPropertyChanged
{
private string _title;
public string Title
{
get { return _title; }
set
{
_title = value;
RaisePropertyChanged("Title");
}
}
protected void RaisePropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
Of course you can put the event and the method in a base class to inherit from so multiple models can take advantage of it.