UWP ListView通过x:Bind切换SelectionMode触发StackOverflowException
解决UWP ListView切换SelectionMode时的StackOverflowException问题
问题原因分析
你遇到的StackOverflowException核心是属性变更通知的循环触发,结合你的代码细节,主要有两个关键诱因:
- 未正确指定
RaisePropertyChanged的属性范围:如果你的RaisePropertyChanged实现没有使用[CallerMemberName]特性,且调用时未传入具体属性名(比如直接调用空参的RaisePropertyChanged()),会导致UWP认为所有属性都发生了变更,包括ListSelectionMode。这会形成死循环:- 设置
ListSelectionMode→ 更新ListView的SelectionMode→ 触发SelectionChanged→ 执行SelectionChangedCommand→ 更新SelectedThreads→ 触发全属性变更通知 → 再次设置ListSelectionMode→ 无限递归。
- 设置
SelectedItem双向绑定与模式切换的联动冲突:从Single切换到Multiple模式时,ListView会自动将原SelectedItem同步到SelectedItems,触发SelectionChanged事件。如果SelectedItem的双向绑定在这个过程中触发额外的属性变更,也会间接加剧循环。
解决方案
1. 修复RaisePropertyChanged的调用(最关键)
确保所有属性的Set方法中,RaisePropertyChanged能精准通知当前属性的变更,推荐使用[CallerMemberName]自动获取属性名:
// 首先确保ViewModel基类的RaisePropertyChanged正确实现 protected void RaisePropertyChanged([CallerMemberName] string propertyName = null) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } // 然后修改所有属性的Set方法 private ListViewSelectionMode _listSelectionMode = ListViewSelectionMode.Single; public ListViewSelectionMode ListSelectionMode { get { return _listSelectionMode; } set { if (_listSelectionMode != value) { _listSelectionMode = value; RaisePropertyChanged(); // 自动获取当前属性名,无需手动传参 } } } private List<object> _selectedThreads = new List<object>(); public List<object> SelectedThreads { get { return _selectedThreads; } set { if (value != _selectedThreads) { _selectedThreads = value; RaisePropertyChanged(); // 自动通知"SelectedThreads"变更 } } } private string _selectedThreadCounter; public string SelectedThreadCounter { get { return _selectedThreadCounter; } set { if (value != _selectedThreadCounter) { _selectedThreadCounter = value; RaisePropertyChanged(); // 自动通知"SelectedThreadCounter"变更 } } }
如果你的基类不支持[CallerMemberName],则手动传入属性名:RaisePropertyChanged(nameof(SelectedThreads))。
2. 避免模式切换时的不必要事件触发
在切换SelectionMode时,临时忽略SelectionChangedCommand的执行,打断循环链条:
private bool _isChangingSelectionMode; // 切换SelectionMode的命令方法(绑定到按钮) public void ToggleSelectionMode() { _isChangingSelectionMode = true; try { ListSelectionMode = ListSelectionMode == ListViewSelectionMode.Single ? ListViewSelectionMode.Multiple : ListViewSelectionMode.Single; } finally { _isChangingSelectionMode = false; } } // 修改SelectionChangedCommand的逻辑 private RelayCommand<IList<object>> _selectionChangedCommand; public RelayCommand<IList<object>> SelectionChangedCommand { get { if (_selectionChangedCommand == null) { _selectionChangedCommand = new RelayCommand<IList<object>>(items => { // 模式切换过程中忽略事件 if (_isChangingSelectionMode) return; // 仅在Multiple模式下处理选中项 if (ListSelectionMode == ListViewSelectionMode.Multiple) { SelectedThreads = items.ToList(); SelectedThreadCounter = $"({SelectedThreads.Count})"; } }); } return _selectionChangedCommand; } }
3. 优化SelectedItem绑定(可选)
在Multiple模式下,SelectedItem会自动同步为SelectedItems的第一个项,双向绑定可能导致不必要的联动。可以通过样式触发器动态调整绑定模式:
<ListView x:Name="MyList" Grid.Row="2" ItemsSource="{x:Bind ViewModel.Threads, Mode=OneWay}" ShowsScrollingPlaceholders="True" SelectionMode="{x:Bind ViewModel.ListSelectionMode, Mode=OneWay}"> <ListView.Style> <Style TargetType="ListView"> <!-- Single模式下使用双向绑定 --> <Setter Property="SelectedItem" Value="{x:Bind ViewModel.Selected, Mode=TwoWay}" /> <Style.Triggers> <!-- Multiple模式下改为单向绑定,避免联动冲突 --> <DataTrigger Binding="{x:Bind ViewModel.ListSelectionMode}" Value="Multiple"> <Setter Property="SelectedItem" Value="{x:Bind ViewModel.Selected, Mode=OneWay}" /> </DataTrigger> </Style.Triggers> </Style> </ListView.Style> <interactivity:Interaction.Behaviors> <core:EventTriggerBehavior EventName="SelectionChanged"> <core:InvokeCommandAction Command="{x:Bind ViewModel.SelectionChangedCommand}" InputConverter="{StaticResource SelectionChangedConverter}" InputConverterParameter="{Binding ElementName=MyList}" /> </core:EventTriggerBehavior> </interactivity:Interaction.Behaviors> </ListView>
验证步骤
- 优先修复
RaisePropertyChanged的调用,这是解决循环问题的核心; - 测试模式切换,如果仍有问题,添加
_isChangingSelectionMode的逻辑; - 最后根据需求调整
SelectedItem的绑定模式。
内容的提问来源于stack exchange,提问作者newone
相关产品推荐
相关产品推荐

