views:

66

answers:

2

I think I already know the answer to my question, but I want to put it out there anyway. I have an app with a ton of command handlers, each with special logic in their CanExecute methods to enable the bound Buttons appropriately.

Now I'm in a situation where I don't want any of the logic to execute, because execution results in calls to a library that I don't want to occur just for GUI updates. I can't stub out the library calls because they are important for the functionality of the rest of the app.

I looked into the Conditional attributes in .NET, and sadly, this won't work because they only work on methods that return void. I could use #if and #define to either use my logic or just return true. I could also query a property in the viewmodel and allow this to determine whether or not to just return true.

The problem is, I'm not lazy but I also don't want to do a bunch of grunt work to make the modifications that I am guessing are unavoidable. However, if anyone knows of a way to use something in .NET to automatically have my buttons enabled without needing to call CanExecute or at least avoid using the underlying logic, please post the answer! :)

A: 

I don't know if there is a way with an attribute, but try this. Somewhere in the window startup code loop over all the command bindings and handle the PreviewCanExecute event for each one.

public MainWindow()
{
    foreach (CommandBinding cb in CommandBindings)
    {
        cb.PreviewCanExecute += new CanExecuteRoutedEventHandler(cb_PreviewCanExecute);
    }
}

void cb_PreviewCanExecute(object sender, CanExecuteRoutedEventArgs e)
{
    e.CanExecute = false; // disable / enable all commands
    e.Handled = true; // Set this to skip calling your existing logic
}
Brian Ensink
oh that's brilliant! I will give it a shot tonight. thanks!
Dave
Brian, how does one typically get at the CommandBindings from the ViewModel?
Dave
The `CommandBindings` property is on the `Window` so you will need loop over them in the window or pass the property to the view model I guess.
Brian Ensink
Sorry for the late response - I did look at the CommandBindings in the window, but the collection is empty. So there must be some object somewhere that contains all of them. I'll do some searches.
Dave
+1  A: 

I question why it is not good to call into this library for GUI logic. You don't want to enable buttons that won't do anything, so commands that are disabled when they can't actually work can be a good thing.

When using the MVVM pattern, my ViewModels normally expose the ICommands directly. If you use Prism, there is a DelegateCommand implementation that makes it easy to implement these commands in your ViewModels. If not, or if the CanExecute is likely to change, I would implement the ICommand in a separate class.

If you have a long running / expensive operation, you can always cache the result of the library call. I'm assuming you will get an event or callback if the state changes which would enable/disable the command. In that case, in your ICommand implementation, you would raise the CanExecuteChanged event in order for the GUI to reflect the changes to the command. Here is an example of a command that uses a backing service that is aware of when an operation can be completed:

public class ExpensiveCommand : ICommand
{
    private readonly IExpensiveService service;
    private bool canExecute;

    public ExpensiveCommand (IExpensiveService service)
    {
        this.service = service;
        canExecute = service.CanExecute();
        service.CanExecuteChanged += OnCanExecuteChanged;
    }

    public void Execute(object parameter)
    {
        service.Execute();
    }

    public bool CanExecute(object parameter)
    {
        return canExecute;
    }

    public event EventHandler CanExecuteChanged;
    private void OnCanExecuteChanged(object sender, EventArgs e)
    {
        canExecute = service.CanExecute();

        if (CanExecuteChanged != null)
            CanExecuteChanged(this, EventArgs.Empty);
    }
}

Alternately, if you always want the command to be executable, you can just return true from the CanExecute method.

Abe Heidebrecht
the reason for it is simply that any time the window repaints, all of the Buttons' CanExecute methods get called, which calls into a library that talks to a physical device. This then results in extra traffic on the communication bus that we don't want to have to sift through if it's not necessary.
Dave
what you're saying about ICommands is interesting. I've done button handlers two ways so far: implement the button Click event handler, and also to bind the button to an ICommand. From what you're saying, it almost sounds like you're using ICommands, but don't use CanExecute / Execute. Can you please point me in the direction of this approach, as I haven't seen it before?
Dave
I think this is what you think I'm doing: http://codingcontext.wordpress.com/2008/12/10/commandbindings-in-mvvm/, but I am using ICommands.
Dave
Yeah, when you said "command binding", I was thinking you were doing CommandBindings to RoutedCommands. I'll update the answer to reflect CanExecute/Execute for your situation.
Abe Heidebrecht
Thanks for the update, Abe. Let me confirm that I'm understanding what you're talking about here. The ExpensiveCommand that the View binds to is just delegating all of its operations to something that inherits from IExpensiveService. This "expensive service" lives in the ViewModel, and then presumably by some parameter in the VM, will dictate whether or not the Command can execute; thus, the ExpensiveCommand will also know but the logic won't need to be there. Overall, it sounds like the act of "blanket" disabling CanExecute for all commands is the same amount of work. (continued)
Dave
... so perhaps your response is actually answering a different question. I'm not concerned that the operation is long-running, I just want the command to always be possible to execute.
Dave
Oh, if all you want is for the command to execute, just return true in CanExecute. If you take a look at CompositeWPF(Prism)'s DelegateCommand, if you don't supply a CanExecute handler, it does this.
Abe Heidebrecht
@Abe: yes, but my question is asking whether or not it's possible to write the code such that, based on a flag, I can either return true *or* evaluate the methods that result in low-level function calls.
Dave