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;     } } 
Add Comment
1 Answer(s)

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}" 
Answered on July 16, 2020.
Add Comment

Your Answer

By posting your answer, you agree to the privacy policy and terms of service.