WPF - CanExecute dosn't fire when raising Comm

2019-07-11 15:30发布

问题:

I've got a button strip usercontrol that I want to have on most of my forms.

I've added the commands as follows ...

    public ICommand Create
    {
        get
        {
            return buttonCreate.Command;
        }
        set
        {
            buttonCreate.Command = value;
        }
    }

I've set these as dependency properties so I can bind to them ...

        public static readonly DependencyProperty CreateCommandProperty =
        DependencyProperty.Register(
        "Create",
        typeof(ICommand),
        typeof(StandardButtonStrip),
        new PropertyMetadata((ICommand)null));

I'm then binding my usercontrol to a command ...

<commoncontrols:StandardButtonStrip HorizontalAlignment="Stretch" Create="{Binding CreateCommand}" />

I'm setting up the command as follows ...

_viewModel.CreateCommand = new DelegateCommand<object>(OnCreateCommand, CanCreate);

but despite the fact that i'm always returning true on my CanCreate method the button is disabled ... if I put a break point on return true it never fires!

    public bool CanCreate(object parm)
    {
        return true;
    }

I've tried this to see if it will refresh the binding, but no joy!

_viewModel.CreateCommand.RaiseCanExecuteChanged();

I think the problem is down to the user control and how I'm passing the Command up as a property, but not sure ...

Can anyone help?

Cheers,

Andy

回答1:

The way this looks, you have a dependency property on a view model. If you are really using MVVM, that is definetly not the way to go about it (not because of religious adherence to a pattern, but because it's not the optimal way).

First of all, is your view model a DependencyObject?

If it is, you should downgrade it to a class which implements INotifyPropertyChanged. Why? Because the Button's Command property is a DependencyProperty itself (inherited from ButtonBase) and supports databinding already.

If it isn't, then a dependency property on it will not work, which is fine, because you shouldn't have dependency properties on your view model in the first place.

What you should do, is have the view model as the DataContext for your control (I'm guessing you already have that set up). Then change your view model's CreateCommand to a plain ICommand, and bind the createButton's Command property like this (in your default StandardButtonStrip style)

<Button Name="createButton" HorizontalAlignment="Stretch" Command="{Binding CreateCommand}" />

This way, it will still be reusable, you just need to ensure that any view model you associate with your user control has a property CreateCommand of type ICommand (and that view model will be inherited down to the button control by default - one of the nicest things the wpf guys thought up).

So to recap, you should do it the other way around, more or less.

Hope this was helpful, cheers.



回答2:

One caveat to the accepted answer -

Using a delegate command I could only get this to work if I created a new

Command<T> : DelegateCommand<T> and hooked up the Command Manager.

event EventHandler ICommand.CanExecuteChanged 
{ 
 add { CommandManager.RequerySuggested += value; }
 remove { CommandManager.RequerySuggested -= value; }
} 


回答3:

Have you overridden any of the user control's functionality?

One common problem is overriding a method without calling the base's implementation. For example, if you've overridden OnContentChanged() without calling base.OnContentChanged(), you may have accidentally supressed the firing of the ContentChanged event.