Raise "Property Of A Property" Changed

Feb 17, 2014 at 10:57 AM
I have defined a class called Contact, which contains a string property called Name. My ViewModel has a property of type Contact, called NewContact. In my View, I have a TextBox bound to NewContact.Name with a two-way binding. So far so good.
However, when I bind something else to NewContact.Name, it is not refreshed when the text in the TextBox is changed. How can I cause the binding to be refreshed?

RaisePropertyChanged(() => NewContact) does not do it. Nor does RaisePropertyChanged(() => NewContact.Name);

Any thoughts?

Thanks,
John
Feb 17, 2014 at 11:39 AM
OK, I've been doing a bit more digging. Binding a TextBlock, say, to NewContact.Name does, in fact, update as expected. But what I'm trying to do is this: also on my View, I have a Button, whose CommandParameter is bound to NewContact, which I use to evaluate CanExecute of the Button's Command (bound to an IActionCommand in my ViewModel).
In simple terms, I'm setting CanExecute to false if Name is empty, otherwise true. CanExecute is evaluated when the view is activated, but thereafter the the Button remains inactive whatever I do.
Coordinator
Feb 17, 2014 at 12:13 PM
John,

Data-binding doesn't listen for property change notification on methods on commands. Instead, commands expose an event called CanExecuteChanged. If the status of CanExecute changes for any reason, you need to have the command raise the event and the status in the UI will be re-evaluated.
Feb 17, 2014 at 12:26 PM
Hi Jeremy,

Thanks for replying so quickly! I have tried raising CanExecuteChanged on the command, but there are no listeners, so the button stays inactive. I thought the button would be listening for CanExecuteChanged automatically - do I need to wire that up myself?

Thanks,
John
Feb 17, 2014 at 1:17 PM
Edited Feb 17, 2014 at 1:18 PM
I've been doing the same thing elsewhere with Infragistics ButtonTools. I assume the ButtonTool receives notification through the binding that its CommandParameter has changed, and therefore calls CanExecute on the bound Command with the new parameter. Is that not the case with standard Microsoft buttons?
Coordinator
Feb 17, 2014 at 1:30 PM
No. The only thing that causes the button to call "CanExecute" again is when it receives a "CanExecuteChanged" event. So when you raise property changed for the property that can execute evaluates, you should then call RaiseCanExecuteChanged on the command. That button should then fire CanExecute again and change its status.

In my article here: http://csharperimage.jeremylikness.com/2010/04/model-view-viewmodel-mvvm-explained.html scroll down to the ConactsViewModel and you'll see the example for the Delete command.
Feb 17, 2014 at 2:06 PM
I'm there now. I had tried calling RaiseCanExecuteChanged() on the command in my viewmodel, but what I needed to do was call it on the button (why? Aren't they the same?). So in my viewmodel, I now have this:
NewContact.PropertyChanged += (s, e) =>
                {
                    NewContactView ncv = (NewContactView)Router.ViewQuery("NewContact");
                    (ncv.okButton.Command as IActionCommand).RaiseCanExecuteChanged();
                };
Which works, but seems inelegant. Is there a better way of doing it?

Thank you for your help with this.
Coordinator
Feb 17, 2014 at 2:07 PM
You can create an IActionCommandWithRaise and extend IActionCommand to expose RaiseCanExecuteChanged then call it directly.


Feb 17, 2014 at 2:34 PM
I was instantiating a new command in the command property's getter. Now I've taken that out and put it in the constructor instead, I can call RaiseCanExecuteChanged() in the viewmodel. Presumably, before, I was calling it on a new instance so that's why the button wasn't getting updated.

Thanks again.
Feb 17, 2014 at 7:40 PM
Of topic but there is a great ViewModel base at http://houseofbilz.com/archives/2010/05/08/adventures-in-mvvm-my-viewmodel-base/#VMB.8

It lets you connect a CanExecute method to a change in a property using an Attribute on the CanExecute method. You don't have to raise the CanExecuteChanged event yourself, just adding an Attribute looks after it.

There is a lot of other good stuff in the ViewModelBase as well that can take some of the ceremony out of common tasks.