You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.02 11:55:40