从ObservableCollection移除项时触发InvalidCastException问题排查
我有一个包含ListView、Button和ObservableCollection的Prism视图。当从集合中移除项时,在Remove()调用处触发了InvalidCastException。有趣的是,触发异常后检查ObservableCollection的内容,发现该项实际上已被移除。
View.xaml代码
<ListView Name="lvSomeEntities" SelectionMode="Single" ItemsSource="{Binding SomeEntityList}"> <i:Interaction.Triggers> <i:EventTrigger EventName="SelectionChanged"> <prism:InvokeCommandAction Command="{Binding LvSelectionChangedCommand}" CommandParameter="{Binding ElementName=lvSomeEntities, Path=SelectedItem}" /> </i:EventTrigger> </i:Interaction.Triggers> </ListView> <Button Name="button" Content="Delete SomeEntity" Command="{Binding DeleteSomeEntityCommand}" CommandParameter="{Binding ElementName=lvSomeEntities, Path=SelectedItem}"/>
ViewModel.cs代码
public DelegateCommand<SomeEntity> DeleteSomeEntityCommand { get; private set; } private ObservableCollection<SomeEntity> _someEntityList = new ObservableCollection<SomeEntity>(); public ObservableCollection<SomeEntity> SomeEntityList { get { return _someEntityList; } set { SetProperty(ref _someEntityList, value); } } public ViewModel() { DeleteSomeEntityCommand = new DelegateCommand<SomeEntity>(DeleteSomeEntity, DeleteSomeEntityEnabled); } private void DeleteSomeEntity(SomeEntity someEntity) { SomeEntityList.Remove(someEntity);//此处触发InvalidCastException } private bool DeleteSomeEntityEnabled(SomeEntity someEntity) { return someEntity != null; }
SomeEntity.cs代码
public class SomeEntity { public SomeEntity(int id, string name) { Id = id; Name = name; } public int Id { get; set; } public string Name { get; set; } public override string ToString() { return Id + " " + Name; } }
异常信息
System.InvalidCastException: 'Unable to cast object of type 'System.Windows.Controls.SelectionChangedEventArgs' to type 'SomeEntity'.'
问题原因
异常的根源不在DeleteSomeEntity方法里的Remove调用,而是你的LvSelectionChangedCommand没有正确处理参数类型。
你在XAML里给SelectionChanged事件的InvokeCommandAction绑定了LvSelectionChangedCommand,但默认情况下,InvokeCommandAction会把事件参数(也就是SelectionChangedEventArgs)传递给命令。如果你的LvSelectionChangedCommand是声明为DelegateCommand<SomeEntity>类型,就会在尝试把SelectionChangedEventArgs强转为SomeEntity时抛出这个异常。
之所以看起来像是Remove调用触发的异常,是因为调试器的调用栈可能把异常指向了最近的用户代码,但实际异常是在选择变更事件触发时抛出的,只是延迟到UI更新阶段才被捕获。
解决方法
有两种修复方式:
- 修正
LvSelectionChangedCommand的参数传递逻辑:如果只需要传递选中项而非事件参数,在InvokeCommandAction里明确添加PassEventArgsToCommand="False",这样就会使用你绑定的SelectedItem作为命令参数:<i:EventTrigger EventName="SelectionChanged"> <prism:InvokeCommandAction Command="{Binding LvSelectionChangedCommand}" CommandParameter="{Binding ElementName=lvSomeEntities, Path=SelectedItem}" PassEventArgsToCommand="False"/> </i:EventTrigger> - 调整命令参数类型:如果需要处理事件参数,将
LvSelectionChangedCommand声明为DelegateCommand<SelectionChangedEventArgs>类型。 - 移除不必要的事件触发器:如果不需要通过命令处理选择变更事件,直接删除对应的
i:Interaction.Triggers节点即可。
内容的提问来源于stack exchange,提问作者thburri

