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
<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
<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
<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
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)后的效果,分隔线正常。
NuGet package version
HandyControl 3.5.1
IDE
No response
Framework type
No response
Windows version
No response
Additional context
根因分析
Row.ArrangeOverride(src/Shared/HandyControl_Shared/Controls/Panel/Grid/Row.cs):
protected override Size ArrangeOverride(Size finalSize)
{
...
foreach (var child in cols)
{
if (!child.IsVisible) // ← 问题所在
{
continue;
}
...
child.Arrange(childBounds);
}
return finalSize;
}
当模板在宿主窗口 Hidden 期间实例化时,布局过程中 hc:Col 的 IsVisible 为 false(受祖先窗口隐藏状态影响),这些 Col 被 continue 跳过、从未被 Arrange。窗口恢复可见后,被跳过 Arrange 的 Col 布局状态无法再恢复(InvalidateMeasure/InvalidateArrange 均无效),ActualWidth/ActualHeight 永久保持 0。Measure 阶段是正常的(Col 的 DesiredSize 与可见时添加的完全一致),仅 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 属性(不受祖先窗口隐藏状态影响):
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——都会触发,且该元素此后永久不可见。
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
App.xaml
MainWindow.xaml
MainWindow.xaml.cs
运行后观察:窗口恢复显示时,两个 divider 都不渲染(分隔线与文字均不显示)。触发条件是 「模板实例化(首次布局展开)发生在窗口
Hidden期间」——第一个 divider 虽然是在窗口可见时添加的,但其模板展开前窗口已被隐藏,同样复现。Expected behavior
窗口恢复
Visible后,hc:Divider应正常渲染出分隔线与内容;即使模板在窗口隐藏期间实例化,布局也应在窗口恢复后自动恢复正常。Screenshots
见附件 screenshot.png(同一 DataTemplate、同一时序下的对照):
hc:Divider原始模板的行完全空白;带[fix]标记的行是仅把模板根hc:Row替换为修复版子类(见 Additional context)后的效果,分隔线正常。NuGet package version
HandyControl 3.5.1
IDE
No response
Framework type
No response
Windows version
No response
Additional context
根因分析
Row.ArrangeOverride(src/Shared/HandyControl_Shared/Controls/Panel/Grid/Row.cs):当模板在宿主窗口
Hidden期间实例化时,布局过程中hc:Col的IsVisible为false(受祖先窗口隐藏状态影响),这些Col被continue跳过、从未被Arrange。窗口恢复可见后,被跳过Arrange的Col布局状态无法再恢复(InvalidateMeasure/InvalidateArrange均无效),ActualWidth/ActualHeight永久保持 0。Measure阶段是正常的(Col的DesiredSize与可见时添加的完全一致),仅Arrange失败。诊断数据(窗口恢复显示后读取可视化树):
建议修复(已验证)
将
Row.ArrangeOverride中的检查改为读取元素自身的Visibility属性(不受祖先窗口隐藏状态影响):已通过子类化
Row覆写ArrangeOverride(仅替换这一处判断,其余逻辑与 3.6.0-rc3 完全一致)验证:同一 DataTemplate、同一时序下,原始版Line ActualW = 0,修复版Line ActualW ≈ 169,分隔线与内容均正常渲染。实际影响场景
任何 「最小化到托盘」 的应用(
WindowState = Minimized+Visibility = Hidden)在隐藏期间动态添加hc:Divider(或Row/Col栅格)内容——例如日志分区线、运行时生成的分区 UI——都会触发,且该元素此后永久不可见。