Prism DelegateCommand's CanExecute getting null parameter after RaiseCanExecuteChanged
I’m trying to write a ListView
, where for each item there is a button bound to a DelegateCommand
. I want this command’s CanExecute
to be based on a boolean property of an item. ItemSource
is an ObservableCollection
and I’m populating it using an async
method. The problem is that when RaiseCanExecuteChanged()
is fired, CanExecute
receives null
as parameter.After that, when I use another UI bound command to RaiseCanExecuteChanged()
, the method behaves properly.
Here’s part of the view:
<ListView x:Name="root" ItemsSource="{Binding olVersions}" IsSynchronizedWithCurrentItem="true" HorizontalContentAlignment="Stretch"> <ListView.ItemTemplate> <DataTemplate> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="*" /> <ColumnDefinition Width="Auto" /> <ColumnDefinition Width="Auto" /> </Grid.ColumnDefinitions> <TextBlock Text="{Binding Path=sFullName}" Grid.Column="0" VerticalAlignment="Center"></TextBlock> <Button Grid.Column="1" Content="Run" CommandParameter="{Binding}" Command="{Binding ElementName=root, Path=DataContext.cRunVersionCommand}"></Button> <Button Grid.Column="2" Content="Download" CommandParameter="{Binding}" Command="{Binding ElementName=root, Path=DataContext.cDownloadVersionCommand}"></Button> </Grid> </DataTemplate> </ListView.ItemTemplate> </ListView>
Here’s the ViewModel:
public class VersionsTabViewModel : BindableBase { public ObservableCollection<CVersion> olVersions { get; private set; } public DelegateCommand<CVersion> cRunVersionCommand { get; private set; } public DelegateCommand<CVersion> cDownloadVersionCommand { get; private set; } private IVersionManager c_version_manager; public VersionsTabViewModel(IVersionManager cVersionManager) { olVersions = new ObservableCollection<CVersion>(); cRunVersionCommand = new DelegateCommand<CVersion>(v_try_run_version, b_can_run); cDownloadVersionCommand = new DelegateCommand<CVersion>(v_try_download_version); c_version_manager = cVersionManager; v_read_available_versions(); } private async void v_read_available_versions() { List<CVersion> l_versions = await c_version_manager.lGetVersions(); List<CVersion> l_versions_sorted = l_versions.OrderByDescending(cVersion => cVersion.cSemanticVersion).ToList(); olVersions.Clear(); olVersions.AddRange(l_versions_sorted); cRunVersionCommand.RaiseCanExecuteChanged(); } private void v_try_run_version(CVersion cVersionToRun) { MessageBox.Show($"Run {cVersionToRun.sFullName}", "Run"); //TODO } private void v_try_download_version(CVersion cVersionToDownload) { MessageBox.Show($"Download {cVersionToDownload.sFullName}", "Download"); //TODO cRunVersionCommand.RaiseCanExecuteChanged(); } private bool b_can_run(CVersion cVersion) { return cVersion?.bIsInstalled ?? false; } }
It seems that this is a general issue with command bindings, there is a related question, here. In order to make it work in your case, you have to change your ElementName
binding to root
for the Command
property from
Command="{Binding ElementName=root, Path=DataContext.cRunVersionCommand}"
to a relative source binding to the parent ListView
control like this.
Command="{Binding RelativeSource={RelativeSource AncestorType={x:Type ListView}}, Path=DataContext.cRunVersionCommand}"