WPF ViewModel Commands CanExecute issue

后端 未结 5 755
情书的邮戳
情书的邮戳 2020-12-09 11:38

I\'m having some difficulty with Context Menu commands on my View Model.

I\'m implementing the ICommand interface for each command within the View Model, then creati

5条回答
  •  离开以前
    2020-12-09 12:33

    However, binding the same commands via a CommandReference in the ContextMenu do not act in the same way.

    That's a bug in CommandReference implementation. It follows from these two points:

    1. It is recommended that the implementers of ICommand.CanExecuteChanged hold only weak references to the handlers (see this answer).
    2. Consumers of ICommand.CanExecuteChanged should expect (1) and hence should hold strong references to the handlers they register with ICommand.CanExecuteChanged

    The common implementations of RelayCommand and DelegateCommand abide by (1). The CommandReference implementation doesn't abide by (2) when it subscribes to newCommand.CanExecuteChanged. So the handler object is collected and after that CommandReference no longer gets any notifications that it was counting on.

    The fix is to hold a strong ref to the handler in CommandReference:

        private EventHandler _commandCanExecuteChangedHandler;
        public event EventHandler CanExecuteChanged;
    
        ...
        if (oldCommand != null)
        {
            oldCommand.CanExecuteChanged -= commandReference._commandCanExecuteChangedHandler;
        }
        if (newCommand != null)
        {
            commandReference._commandCanExecuteChangedHandler = commandReference.Command_CanExecuteChanged;
            newCommand.CanExecuteChanged += commandReference._commandCanExecuteChangedHandler;
        }
        ...
    
        private void Command_CanExecuteChanged(object sender, EventArgs e)
        {
            if (CanExecuteChanged != null)
                CanExecuteChanged(this, e);
        }
    

    In order for the same behaviour, I must also include the EventHandler from Josh Smith's RelayCommand, within CommandReference, but in doing so I must comment out some code from within the OnCommandChanged Method. I'm not entirely sure why it is there, perhaps it is preventing event memory leaks (at a guess!)?

    Note that your approach of forwarding subscription to CommandManager.RequerySuggested also eliminates the bug (there's no more unreferenced handler to begin with), but it handicaps the CommandReference functionality. The command with which CommandReference is associated is free to raise CanExecuteChanged directly (instead of relying on CommandManager to issue a requery request), but this event would be swallowed and never reach the command source bound to the CommandReference. This should also answer your question as to why CommandReference is implemented by subscribing to newCommand.CanExecuteChanged.

    UPDATE: submitted an issue on CodePlex

提交回复
热议问题