Does guard method refresh IsEnabled?

Mar 25, 2011 at 3:46 PM

I have a viewmodel with

public bool CanDoSomething(object arg) {

    if(arg.AllowSomething) {
       return true;
    }

    return false;
}

public void DoSomething(object arg) {
    
}

It seems like ActionContext.CanExecute() is evaluated once and then never reevaluated (even if the viewmodel calls refresh)

It looks like ActionMessage.PrepareContext will subscribe to INotifyPropertyChanged if binding to a property, but not a method?
Is there a way to accomplish this? I want to set a property on my view model and then cause the guard method (and subsequent availability) to update.

I should mention the control in use is a ContextMenu since they can be rather funky.
Silverlight 4

Thanks

Mar 25, 2011 at 4:16 PM

If possible, you should implement guards as properties, since in such a case re-evaluation is performed automatically (as you could verify).

If this is not an option, you can force the re-evaluation of the guard calling the ActionMessage.UpdateAvailability function.

Mar 25, 2011 at 5:07 PM

Is there a good way to get a hold of the ActionMessage instance? The only thing I could figure out was to override PrepareContext and grab it there.

Coordinator
Mar 25, 2011 at 5:22 PM

If you want it in your guard, you can have it injected as a special parameter. It's called $executionContext. You can get just about anything from there, including the ActionMessage. If you want to streamline it so that you just get the message, you should investigate adding a custom special parameter to the MessageBinder.

Mar 25, 2011 at 5:27 PM
Edited Mar 25, 2011 at 5:29 PM

Honestly, I tend to use properties as guards, since in most cases it is possible to expose guard arguments as properties of the VM itself, so I have never encountered this scenario.

I suppose that, if you cannot follow the above suggestion and the VM is an IViewAware, you could retrieve the ActionMessage instance during OnAttached... but honestly, I don't like it very much...

Edit: Following Rob's suggestion you should be able to retrieve the context the first time the guard is executed. I suppose it's the simplest way.

Coordinator
Mar 25, 2011 at 5:32 PM

I agree with BladeWise...properties as guards should be your default approach. What we are talking about here is a "if there's no other way" type of scenario in my opinion. Just keep that in mind.

Mar 25, 2011 at 5:42 PM

I have a list of items in 1 viewmodel, and a contextmenu with actions bound to a different view model.

There are a lot of rules about which options are available (and when they are available) and the state is always changing based on what other users are doing.
They started out as all one viewmodel, but keeping the state got so unwieldly that i broke them apart and separated responsibilities.

Thanks for the advice tho, really appreciate it.

Feb 21, 2013 at 11:46 PM
A related issue, is getting the compilation error "Cannot convert lambda expression to type 'string' because it is not a delegate type" when using "NotifyOfPropertyChange(() => CanExecute);" where CanExecute is a method and not a Property. This was a really obscure error (for me anyway), having modeled my code after the SayHello example (http://devlicio.us/blogs/rob_eisenberg/archive/2010/07/06/caliburn-micro-soup-to-nuts-pt-1-configuration-actions-and-conventions.aspx) which shows a method, not a Property. After pulling all my hair out, I found this article and redefined my CanExecute as a Property and the error went away and the guard worked. I'm derived from a Screen though and not PropertyChangedBase, so that was probably the reason though I don't understand the details behind it. Just wanted to post to save hair elsewhere... Dave