IsEnabled vs Command.CanExecute

Feb 15, 2012 at 6:11 PM

I need a button with bindings on both Command and IsEnabled properties of a button. But IsEnabled is overwritten by the result of Command.CanExecute (as documented on the BindableApplicationBar home page). I could move the logic from the IsEnabled binding to the Command.CanExecute method, but I would still need to somehow raise ICommand.CanExecuteChanged at the appropriate times. That would be less than ideal for my app.

How about making the current behavior (Command.CanExecute sets IsEnabled) conditional, based on a property of BindableApplicationBar? I've already prototyped this for myself. It involved minor changes to the bar, button, and menu item classes. (I'm using changeset 5461 built from source.)

Coordinator
Mar 6, 2012 at 4:46 AM

I am glad you managed to solve the problem for yourself, but I don't think another property is an elegant solution overall if I understand your problem correctly.

Having the library available in source form allows anyone to modify it to suit their needs, but adding another property here to configure the behavior seems to be complicating things unnecessarily. The goal of this library is to support the use of MVVM pattern in Windows Phone applications and I think if you go so far as to use it to get rid of code-behind - your command is the best source of information for IsEnabled property of the underlying ApplicationBarIconButton.

If you already have a view-model property that BindableApplicationBarButton.IsEnabled is binding to - just remove that binding and make the command use it in its CanExecute implementation. If your IsEnabled property is set explicitly or bound to something else than a property of your view-model - perhaps you should rather modify it so that it IS bound to the view-model.

That said - there might be some way to make the current implementation work a little bit better in such occasions without changing the API.

Coordinator
Mar 6, 2012 at 4:51 AM
This discussion has been copied to a work item. Click here to go to the work item and continue the discussion.
Mar 6, 2012 at 6:18 PM

Thanks for the reply. And thank you for writing this great library.

I would certainly prefer to use the library without modifications. But as it stands right now, the IsEnabled and Command properties are mutually exclusive with respect to data binding. The changes I made were a quick-and-dirty attempt to decouple IsEnabled and Command. I'm certainly open to a more elegant solution.

 

Dec 4, 2012 at 4:48 AM

Can you describe your solution?  I'm finding that I'm having to call RaiseCanExecuteChanged() on my relay commands when the condition changes.  If as the original poster mentioned, IsEnabled were mutually exclusive I could simply call RaisePropertyChanged("") as I was doing before.  I have it working but it seems a bit odd.  Thanks.

Dec 4, 2012 at 7:56 PM
Edited Dec 4, 2012 at 7:58 PM

The solution I mentioned above was temporary and inelegant. The solution I eventually settled on involves patching BindableApplicationBar's button and menu item classes wherever this.IsEnabled is set to the result of someCommand.CanExecute. For example, in BindableApplicationBarButton.CommandCanExecuteChanged, I modified the code as follows:

 

        private void CommandCanExecuteChanged(object sender, EventArgs e)
        {
            if (this.Command != null)
            {
                var policy = this.Command as IBindableApplicationBarCommand;

                if (policy == null || policy.ShallSetIsEnabled)
                {
                    this.IsEnabled = this.Command.CanExecute(this.CommandParameter);
                }
            }
        }

 

where IBindableApplicationBarCommand is an interface that provides "policy" for the button. Note that this modification is backward compatible; in the absence of policy, the old behavior remains in place.

I did submit a patch for the work item, but it has disappeared from CodePlex. And I no longer have access to the source code for the patch. Nevertheless, the necessary patches are straightforward. Here is the declaration for IBindableApplicationBarCommand:

 

namespace BindableApplicationBar
{
    /// <summary>
    /// Defines a command with policy relevant to the items of a BindableApplicationBar.
    /// </summary>
    public interface IBindableApplicationBarCommand : ICommand
    {
        /// <summary>
        /// Gets a value indicating whether the IsEnabled property of a button/menu item
        /// shall be overwritten by the result of the CanExecute method.
        /// </summary>
        bool ShallSetIsEnabled { get; }
    }
}

 

Patch the BAB code as outlined above, then assign the Command property of your buttons and menu items to instances of a class that implements this interface, and you should be good to go.