如何安全地替换用作 TreeView 的 ItemsSource 的 ObservableCollection 的内容?

问题描述

我正在编写 WinUI 3 桌面应用程序。主窗口由一个 TreeView 组成,其中汽车品牌作为父项,汽车模型作为子项。相关代码如下:

MainWindow.xaml

<Window
    x:Class="Cars.View.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:model="using:Cars.Model"
    xmlns:view="using:Cars.View"
    xmlns:utility="using:Cars.View.Utility">
    
    <Grid>
        <Grid.Resources>
                        
            <DataTemplate x:Key="CarMakeTemplate" x:DataType="model:CarMake">
                <TreeViewItem ItemsSource="{x:Bind Path=CarModels,Mode=OneWay}">
                    <StackPanel Orientation="Horizontal">
                        <view:CarMakeView CarMake="{x:Bind Mode=OneWay}"/>
                        <Button Content="Delete" Click="DeleteCarMake"/>
                    </StackPanel>
                </TreeViewItem>
            </DataTemplate>
            
            <DataTemplate x:Key="CarModelTemplate" x:DataType="model:CarModel">
                <TreeViewItem>
                    <view:CarModelView CarModel="{x:Bind Mode=OneWay}"/>
                </TreeViewItem>
            </DataTemplate>
            
            <utility:CarItemSelector
                x:Key="CarItemSelector"
                CarMakeTemplate="{StaticResource CarMakeTemplate}"
                CarModelTemplate="{StaticResource CarModelTemplate}" />
        </Grid.Resources>
        
        <TreeView ItemsSource="{x:Bind Cars,Mode=OneWay}"  
                  ItemTemplateSelector="{StaticResource CarItemSelector}"
                  SelectionChanged="HandleSelectedCarMakeChanged">
        </TreeView>
    </Grid>
    
</Window>

MainWindow.cs

public sealed partial class MainWindow : Window,INotifyPropertyChanged
    {
        public ObservableCollection<CarMake> Cars { get; set; } =
            new ()
            {
                new CarMake { Name = "Chevrolet",CarModels = { new CarModel { Name = "Camaro" },new CarModel { Name = "Blazer" },new CarModel { Name = "Beretta" } } },new CarMake { Name = "Land Rover",CarModels = { new CarModel { Name = "discovery" },new CarModel { Name = "LR3" },new CarModel { Name = "Range Rover" } } },new CarMake { Name = "Quadra",CarModels = { new CarModel { Name = "Turbo-R 740" },new CarModel { Name = "Type-66 Avenger" }} },new CarMake { Name = "Powell Motors",CarModels = { new CarModel { Name = "The Homer" }}}
            };

        public MainWindow()
        {
            this.InitializeComponent();
        }

        private void DeleteCarMake(object sender,RoutedEventArgs eventInfo)
        {
            var carMaketoDelete = ((Button) eventInfo.OriginalSource).DataContext as CarMake;
            var updatedCarMakes = new List<CarMake>(Cars);
            updatedCarMakes.Remove(carMaketoDelete!);
            
            Cars.Clear();
            foreach (CarMake carMake in updatedCarMakes)
            {
                Cars.Add(carMake);
            }
            OnPropertyChanged(nameof(Cars));
        }

        private void HandleSelectedCarMakeChanged(TreeView sender,TreeViewSelectionChangedEventArgs info)
        {
            //do stuff
        }
        
        public event PropertyChangedEventHandler? PropertyChanged;

        [NotifyPropertyChangedInvocator]
        private void OnPropertyChanged([CallerMemberName] string? propertyName = null)
        {
            PropertyChanged?.Invoke(this,new PropertyChangedEventArgs(propertyName));
        }
    }

问题:以下是导致神秘异常的事件顺序:

  1. 用户点击“删除”按钮。
  2. DeleteCarMake() 通过回调调用
  3. DeleteCarMake() 删除选定的 CarMake 并完全重置 Cars ObservableCollection内容
  4. 下次用户点击 TreeView 时会引发异常。

问题似乎与完全删除 Cars内容以及随后将数据复制回 Cars(注意 Cars 作为 ItemsSource TreeView)。如果我只是从 carMaketoDelete删除 Cars,则下次用户单击 TreeView 时不会抛出异常。但是,由于我的简化代码无法表达的原因,我需要能够在每次删除时完全Clear() Cars内容,而不仅仅是删除单个项目。

完整的调用堆栈是 here,但我可以说相关部分似乎在中间:

Microsoft.UI.Xaml.Controls.dll!viewmodel::UpdateNodeselection(struct winrt::Microsoft::UI::Xaml::Controls::TreeViewNode const &,enum TreeViewNode::TreeNodeselectionState const &)  UnkNown
Microsoft.UI.Xaml.Controls.dll!viewmodel::UpdateSelection(struct winrt::Microsoft::UI::Xaml::Controls::TreeViewNode const &,enum TreeViewNode::TreeNodeselectionState const &)  UnkNown
Microsoft.UI.Xaml.Controls.dll!SelectedTreeNodeVector::UpdateSelection(struct winrt::Microsoft::UI::Xaml::Controls::TreeViewNode const &,enum TreeViewNode::TreeNodeselectionState) UnkNown
Microsoft.UI.Xaml.Controls.dll!SelectedTreeNodeVector::RemoveAt(unsigned int)   UnkNown
Microsoft.UI.Xaml.Controls.dll!SelectedTreeNodeVector::Clear(void)  UnkNown
Microsoft.UI.Xaml.Controls.dll!winrt::impl::produce<class SelectedTreeNodeVector,struct winrt::Windows::Foundation::Collections::IVector<struct winrt::Microsoft::UI::Xaml::Controls::TreeViewNode> >::Clear(void)  UnkNown
Microsoft.UI.Xaml.Controls.dll!winrt::impl::consume_Windows_Foundation_Collections_IVector<struct winrt::Windows::Foundation::Collections::IVector<struct winrt::Microsoft::UI::Xaml::DependencyObject>,struct winrt::Microsoft::UI::Xaml::DependencyObject>::Clear(void)   UnkNown
Microsoft.UI.Xaml.Controls.dll!viewmodel::SelectNode(struct winrt::Microsoft::UI::Xaml::Controls::TreeViewNode const &,bool)    UnkNown
Microsoft.UI.Xaml.Controls.dll!TreeView::UpdateSelection(struct winrt::Microsoft::UI::Xaml::Controls::TreeViewNode const &,bool)    UnkNown
Microsoft.UI.Xaml.Controls.dll!TreeViewItem::UpdateSelection(bool)  UnkNown
Microsoft.UI.Xaml.Controls.dll!TreeViewItem::OnIsSelectedChanged(struct winrt::Microsoft::UI::Xaml::DependencyObject const &,struct winrt::Microsoft::UI::Xaml::DependencyProperty const &) UnkNown

我想知道的:我怎样才能安全地Clear() Cars ObservableCollection内容而不导致在下一次抛出异常用户点击 TreeView 的时间?

一个可能的线索:如果我没有在我的 TreeView 中为 SelectionChangedMainWindow.xaml 事件定义事件处理程序,那么异常永远不会发生.我不太确定这意味着什么,但希望其他人可以将拼图的各个部分拼凑起来。

解决方法

这似乎是 WinUI 3 下一个预览版中将修复的错误。来源 here

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...