开发者

Is there a clean way to make CanExecute always return true with an attribute?

开发者 https://www.devze.com 2022-12-21 14:54 出处:网络
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

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 imp开发者_如何学Cortant 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! :)


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
}


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.

0

精彩评论

暂无评论...
验证码 换一张
取 消