Prism DelegateCommand CanExecute passes a null for CommandParameter

Topics: Prism v4 - WPF 4
Jan 15, 2014 at 4:06 PM
Edited Jan 15, 2014 at 4:07 PM
Gostrowsky, asked me to repost this here from the Prism v1.0 thread:

The CommandParameter is passed as a null to the CanExecute method when using an MVVM architecture to generate the ContextMenus. The regular commands seem to function properly if the CanExecute function is disabled (which isn't really an option).

I've got a View Model used to generate the context menu (as per the MVVM goals). The code that creates the context menu looks like this:
                    <Grid>
                        <Grid.ContextMenu>
                            <ContextMenu ItemsSource="{Binding ListViewMenuItems}"/>
                        </Grid.ContextMenu>
                    </Grid>
The DataTemplate used to create the menu items looks like this:
        <Style.Resources>
            <DataTemplate DataType="{x:Type local:MenuItemViewModel}">
                <MenuItem Header="{Binding Header}"
                          Icon="{Binding Icon}"
                          CommandParameter="{Binding CommandParameter}"
                          Command="{Binding Command}"/>
            </DataTemplate>
        </Style.Resources>
and, finally, the View Model for the menu items looks like this:
    public class MenuItemViewModel : NotificationObject
    {
        private Object headerField;
        private Object iconField;
        private ICommand commandField;
        private Object commandParameterField;

        public Object Header
        {
            get
            {
                return this.headerField;
            }
            set
            {
                if (this.headerField != value)
                {
                    this.headerField = value;
                    this.RaisePropertyChanged("Header");
                }
            }
        }

        public Object Icon
        {
            get
            {
                return this.iconField;
            }
            set
            {
                if (this.iconField != value)
                {
                    this.iconField = value;
                    this.RaisePropertyChanged("Icon");
                }
            }
        }

        public ICommand Command
        {
            get
            {
                return this.commandField;
            }
            set
            {
                if (this.commandField != value)
                {
                    this.commandField = value;
                    this.RaisePropertyChanged("Command");
                }
            }
        }

        public Object CommandParameter
        {
            get
            {
                return this.commandParameterField;
            }
            set
            {
                if (this.commandParameterField != value)
                {
                    this.commandParameterField = value;
                    this.RaisePropertyChanged("CommandParameter");
                }
            }
        }
    }
And the implementation of the MenuItemViewModel property looks like like this:
        public override ObservableCollection<Object> ListViewMenuItems
        {
            get
            {
                var list = new ObservableCollection<Object>();
                list.Add(new MenuItemViewModel
                {
                    Header = "Delete",
                    Command = GlobalCommands.Delete,
                    CommandParameter = this
                });
                return list;
            }
        }
Note that the delete command is a CompositeCommand and the CommandParameter is set to the ViewModel that I want to be passed to the command handler to determine if it can be deleted. The CommandParameter will always be set and will always point to a valid view model.

The first time, and then randomly after the first time, the DelegateCommand CanExecute handler gets a null when it is called:
            this.deleteCommandField = new DelegateCommand<IExplorerItem>(this.DeleteItem, this.CanDeleteItem);
            GlobalCommands.Delete.RegisterCommand(this.deleteCommandField);
        private Boolean CanDeleteItem(IExplorerItem explorerItem)
        {
            CustomerNode customerNode = explorerItem as CustomerNode;
            if (customerNode != null)
            {
                return customerNode.Children.Count == 0;
            }
            return false;
        }
Jan 17, 2014 at 4:13 PM
Hi DRAirey1,

An answer has been posted on the thread you mentioned above regarding the CommandParamater's behaviour:

However, I noticed that the problem you describe slightly differs from the other discussion. It seems that you are able to get the CanExecute command called. Instead, you are getting null parameters sometimes, but sometimes the parameter is properly set, is this correct?

The reason of getting a null reference the first time would be related to the initialization order. I would suggest you to verify if you set the DataContext ViewModel after initializing the View component, and the CommandParameter's ViewModel was already instantiated.

Following Damian's solution in the disscussion linked above, in order to call the canExecute method when the CommandParameter changes, you could trigger the RaiseCanExecuteChanged event by listening to the List SelectionChanged or CollectionChanged event.

I hope this helps you,
Regards.

Gabriel Ostrowsky
https://blogs.southworks.net/gostrowsky
Coordinator
Jan 21, 2014 at 12:11 AM
Hello DRAirey1,

I am able to reproduce the passing of null in the CommandParameter the first time CanExecute is called on the DelegateCommand. However, I haven't been able to reproduce the random passing of null. I always see a non-null parameter being passed to CanExecute after the initial call.

My bigger concern is how you are using CompositeCommand in the first place.
CompositeCommand is meant to aggregate the functionality of child ICommand instances. For example, if you had multiple controls on your app surface at the same time, all with the ability to delete, you could register all these delete commands with a composite delete command. The composite delete command would not pass any context (command parameter) to its registered child commands. The composite command would simply listen to CanExecuteChanged events of all of its children and respond by calling CanExecute on all of its children and if all return true, then the composite command's CanExecute would return true, raising the CanExecuteChanged if necessary). You can see here that the composite command would not have context of all the various things that could be deleted. It merely aggregates the enablement of all its registered child commands.

-Francis
Coordinator
Jan 21, 2014 at 7:32 PM
Also, there seems to be a problem with CommandParameter binding order. Sounds like once Command is bound, CanExecute is called. If CommandParameter isn't bound yet, null gets passed.

http://stackoverflow.com/questions/335849/wpf-commandparameter-is-null-first-time-canexecute-is-called