WinUI 3中删除选中TabView标签时程序崩溃问题求助
在WinUI 3.0(C++/WinRT)中删除选中TabViewItem时程序崩溃的问题
我在Windows 11上用C++/WinRT开发WinUI 3.0应用,用TabView控件管理文档标签。未选中的标签能正常删除,但删除处于选中状态的标签时程序会崩溃。尝试过删除前取消选中该标签,但没有效果。
我试过直接取消TabViewItem的选中状态但失败,当前的删除代码如下:
void MainWindow::TabView_TabCloseRequested(::winrt::Microsoft::UI::Xaml::Controls::TabView const& sender, ::winrt::Microsoft::UI::Xaml::Controls::TabViewTabCloseRequestedEventArgs const& args) { uint32_t indexOf; if (sender.TabItems().IndexOf(args.Tab(), indexOf)) { sender.TabItems().RemoveAt(indexOf); } }
XAML页面代码:
<!-- Copyright (c) Microsoft Corporation and Contributors. --> <!-- Licensed under the MIT License. --> <Window x:Class="Vectraset.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:Vectraset" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:canvas="using:Microsoft.Graphics.Canvas.UI.Xaml" mc:Ignorable="d"> <RelativePanel> <StackPanel x:Name="menu" Orientation="Horizontal" RelativePanel.AlignLeftWithPanel="True" RelativePanel.AlignTopWithPanel="True"> <MenuBar> <MenuBarItem Title="File"> <MenuFlyoutItem Text="New"/> <MenuFlyoutItem Text="Open..."/> <MenuFlyoutItem Text="Save"/> <MenuFlyoutItem Text="Exit"/> </MenuBarItem> <MenuBarItem Title="Edit"> <MenuFlyoutItem Text="Undo"/> <MenuFlyoutItem Text="Cut"/> <MenuFlyoutItem Text="Copy"/> <MenuFlyoutItem Text="Paste"/> </MenuBarItem> <MenuBarItem Title="Help"> <MenuFlyoutItem Text="About"/> </MenuBarItem> </MenuBar> </StackPanel> <StackPanel x:Name="stack2" Orientation="Vertical" RelativePanel.AlignLeftWithPanel="True" RelativePanel.Below="menu"> <TabView x:Name="myTab" SelectionChanged="TabView_SelectionChanged" AddTabButtonClick="TabView_AddButtonClick" TabCloseRequested="TabView_TabCloseRequested" Loaded="TabView_Loaded"/> <Canvas x:Name="myStack" Width="2500" Height="1250" HorizontalAlignment="Left" VerticalAlignment="Top"> <canvas:CanvasControl x:Name="D2D_CanvasControl" Canvas.Left="0" Canvas.Top="0" Width="2500" Height="1250" HorizontalAlignment="Right" VerticalAlignment="Bottom" Draw="CanvasControl_Draw" ClearColor="CornflowerBlue"> </canvas:CanvasControl> <local:PanelPart x:Name="myPanelPart" Canvas.Left="100" Canvas.Top="100" PointerPressed="PanelPart_PointerPressed" PointerReleased="PanelPart_PointerReleased" PointerMoved="PanelPart_PointerMoved" Width="75" Height="200" Background="Blue" Label="PanelPart"/> </Canvas> </StackPanel> <AppBar x:Name="bottomAppBar" Padding="0,0,0,0" MinWidth="100" RelativePanel.AlignLeftWithPanel="True" RelativePanel.AlignRightWithPanel="True" RelativePanel.AlignBottomWithPanel="True" AppBar.IsSticky="True"> <StackPanel Orientation="Horizontal" HorizontalAlignment="Left"> <AppBarButton Icon="Add" Label="Add Part" Click="AddPartButton_Click"/> <AppBarButton AutomationProperties.Name="Sample Button" AutomationProperties.AutomationId="SampleAppBarButton" Click="AppBarButton_Click"/> </StackPanel> </AppBar> </RelativePanel> </Window>
添加TabViewItem的代码:
// CPP void MainWindow::TabView_AddButtonClick(::winrt::Microsoft::UI::Xaml::Controls::TabView const& sender, ::winrt::Windows::Foundation::IInspectable const& e) { myTab().TabItems().Append(CreateNewTab(L"Default Title")); } Microsoft::UI::Xaml::Controls::TabViewItem MainWindow::CreateNewTab(hstring title) { TabViewItem newItem; newItem.Header(box_value(title)); return newItem; }
解决方案
这个问题的核心是:删除选中的TabViewItem时,TabView的选中状态还引用着已被移除的元素,后续UI更新时会因空引用崩溃。可以用以下两种方法解决:
方法一:删除前重置选中项
在删除选中标签前,先将TabView的选中项切换到其他标签(如果存在),或者设为nullptr:
void MainWindow::TabView_TabCloseRequested(::winrt::Microsoft::UI::Xaml::Controls::TabView const& sender, ::winrt::Microsoft::UI::Xaml::Controls::TabViewTabCloseRequestedEventArgs const& args) { uint32_t indexOf; if (sender.TabItems().IndexOf(args.Tab(), indexOf)) { // 如果当前要删除的是选中项,先重置选中状态 if (sender.SelectedItem() == args.Tab()) { if (sender.TabItems().Size() > 1) { // 选中前一个标签,要是当前是第一个就选中下一个 uint32_t newSelectedIndex = indexOf > 0 ? indexOf - 1 : indexOf + 1; sender.SelectedIndex(newSelectedIndex); } else { // 只剩最后一个标签的话,把选中项设为空 sender.SelectedItem(nullptr); } } sender.TabItems().RemoveAt(indexOf); } }
方法二:用Dispatcher延迟删除操作
通过UI线程的Dispatcher把删除操作延迟到当前事件处理完成后执行,避免在选中状态更新期间修改TabItems集合:
void MainWindow::TabView_TabCloseRequested(::winrt::Microsoft::UI::Xaml::Controls::TabView const& sender, ::winrt::Microsoft::UI::Xaml::Controls::TabViewTabCloseRequestedEventArgs const& args) { auto tabToRemove = args.Tab(); // 异步执行删除操作 sender.Dispatcher().RunAsync(Microsoft::UI::Dispatching::DispatcherPriority::Normal, [sender, tabToRemove]() { uint32_t indexOf; if (sender.TabItems().IndexOf(tabToRemove, indexOf)) { sender.TabItems().RemoveAt(indexOf); } }); }
额外注意事项
- 如果你用到了
SelectionChanged事件,要在事件处理里判断选中项是否为null,避免访问已被删除的元素。 - 优先选方法一,它直接处理了选中状态的同步问题,比异步操作更稳妥。
内容的提问来源于stack exchange,提问作者Andrew
相关产品推荐
相关产品推荐

