窗口隐藏(`Visibility.Hidden`)期间实例化的 `hc:Divider` 分隔线渲染宽度为 0

Author: ABA2396Created Aug 17, 2026Updated Aug 17, 2026

Describe the bug

当宿主窗口处于 Visibility.Hidden 状态(典型的 「最小化到托盘」 场景)时,如果有 hc:Divider(或任何以 hc:Row/hc:Col 为模板根的控件)的模板在此期间被实例化并加入可视化树,这些元素会永久渲染失败:

  • Divider 控件本身能拿到正常的布局尺寸(例如 420×15);
  • 但其模板内的三个 hc:Col(左线 / 内容 / 右线)的 ActualWidth/ActualHeight 均保持 0,视觉上完全不显示;
  • 窗口恢复 Visible 后不会自愈,手动调用 InvalidateMeasure / InvalidateArrange / UpdateLayout 也无法恢复;
  • 使用标准 Grid/Border 模板的元素在同样时序下显示正常,因此该问题特定于 Row/Col 栅格。

Steps to reproduce the bug

最小复现工程

DividerTest.csproj

xml
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>WinExe</OutputType>
    <TargetFramework>net10.0-windows10.0.17763.0</TargetFramework>
    <Nullable>enable</Nullable>
    <UseWPF>true</UseWPF>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="HandyControl" Version="3.6.0-rc3" />
  </ItemGroup>
</Project>

App.xaml

xml
<Application x:Class="DividerTest.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             StartupUri="MainWindow.xaml">
    <Application.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="pack://application:,,,/HandyControl;component/Themes/SkinDefault.xaml" />
                <ResourceDictionary Source="pack://application:,,,/HandyControl;component/Themes/Theme.xaml" />
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Application.Resources>
</Application>

MainWindow.xaml

xml
<Window x:Class="DividerTest.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:hc="https://handyorg.github.io/handycontrol"
        Title="Divider repro" Width="900" Height="600">
    <Window.Resources>
        <DataTemplate x:Key="LogTemplate">
            <hc:Divider MaxWidth="420" HorizontalContentAlignment="Center"
                        Content="{Binding}" />
        </DataTemplate>
    </Window.Resources>
    <ScrollViewer HorizontalScrollBarVisibility="Disabled">
        <ItemsControl x:Name="LogList" ItemTemplate="{StaticResource LogTemplate}" />
    </ScrollViewer>
</Window>

MainWindow.xaml.cs

csharp
using System;
using System.Windows;
using System.Windows.Threading;

namespace DividerTest;

public partial class MainWindow
{
    public MainWindow()
    {
        InitializeComponent();

        // 1s 后添加第一个 divider,随即最小化并隐藏窗口(模拟最小化到托盘)
        var t1 = new DispatcherTimer { Interval = TimeSpan.FromSeconds(1) };
        t1.Tick += (s, e) =>
        {
            t1.Stop();
            LogList.Items.Add("A: 添加后立即隐藏");
            WindowState = WindowState.Minimized;
            ShowInTaskbar = false;
            Visibility = Visibility.Hidden;

            // 2s 后(窗口仍隐藏)再添加一个
            var t2 = new DispatcherTimer { Interval = TimeSpan.FromSeconds(2) };
            t2.Tick += (s2, e2) =>
            {
                t2.Stop();
                LogList.Items.Add("B: 隐藏期间添加");

                // 2s 后恢复窗口
                var t3 = new DispatcherTimer { Interval = TimeSpan.FromSeconds(2) };
                t3.Tick += (s3, e3) =>
                {
                    t3.Stop();
                    Show();
                    WindowState = WindowState.Normal;
                    Activate();
                };
                t3.Start();
            };
            t2.Start();
        };
        t1.Start();
    }
}

运行后观察:窗口恢复显示时,两个 divider 都不渲染(分隔线与文字均不显示)。触发条件是 「模板实例化(首次布局展开)发生在窗口 Hidden 期间」——第一个 divider 虽然是在窗口可见时添加的,但其模板展开前窗口已被隐藏,同样复现。

Expected behavior

窗口恢复 Visible 后,hc:Divider 应正常渲染出分隔线与内容;即使模板在窗口隐藏期间实例化,布局也应在窗口恢复后自动恢复正常。

Screenshots

见附件 screenshot.png(同一 DataTemplate、同一时序下的对照):hc:Divider 原始模板的行完全空白;带 [fix] 标记的行是仅把模板根 hc:Row 替换为修复版子类(见 Additional context)后的效果,分隔线正常。

Image

NuGet package version

HandyControl 3.5.1

IDE

No response

Framework type

No response

Windows version

No response

Additional context

根因分析

Row.ArrangeOverridesrc/Shared/HandyControl_Shared/Controls/Panel/Grid/Row.cs):

csharp
protected override Size ArrangeOverride(Size finalSize)
{
    ...
    foreach (var child in cols)
    {
        if (!child.IsVisible)      // ← 问题所在
        {
            continue;
        }
        ...
        child.Arrange(childBounds);
    }
    return finalSize;
}

当模板在宿主窗口 Hidden 期间实例化时,布局过程中 hc:ColIsVisiblefalse(受祖先窗口隐藏状态影响),这些 Colcontinue 跳过、从未被 Arrange。窗口恢复可见后,被跳过 ArrangeCol 布局状态无法再恢复(InvalidateMeasure/InvalidateArrange 均无效),ActualWidth/ActualHeight 永久保持 0。Measure 阶段是正常的(ColDesiredSize 与可见时添加的完全一致),仅 Arrange 失败。

诊断数据(窗口恢复显示后读取可视化树):

[hc:Divider 原始]  Divider Actual=420.0x15.2, Line ActualW=0.0
    Row: _fixedWidth=81.0, RenderSize=420.0x15.2
      col[0] Col: Desired=169.3x1.0, Actual=0.0x0.0
      col[1] Col: Desired=81.0x15.2,  Actual=0.0x0.0
      col[2] Col: Desired=169.3x1.0, Actual=0.0x0.0
[MyRow 修复版]     Divider Actual=420.0x15.2, Line ActualW=169.7

建议修复(已验证)

Row.ArrangeOverride 中的检查改为读取元素自身的 Visibility 属性(不受祖先窗口隐藏状态影响):

csharp
if (child.Visibility != Visibility.Visible)
{
    continue;
}

已通过子类化 Row 覆写 ArrangeOverride(仅替换这一处判断,其余逻辑与 3.6.0-rc3 完全一致)验证:同一 DataTemplate、同一时序下,原始版 Line ActualW = 0,修复版 Line ActualW ≈ 169,分隔线与内容均正常渲染。

实际影响场景

任何 「最小化到托盘」 的应用(WindowState = Minimized + Visibility = Hidden)在隐藏期间动态添加 hc:Divider(或 Row/Col 栅格)内容——例如日志分区线、运行时生成的分区 UI——都会触发,且该元素此后永久不可见。