Skip to content

fix: Row fails to arrange Cols when the template is instantiated while the hosting window is hidden - #1819

Open
ABA2396 wants to merge 1 commit into
HandyOrg:masterfrom
ABA2396:fix/row-arrange-while-window-hidden
Open

fix: Row fails to arrange Cols when the template is instantiated while the hosting window is hidden#1819
ABA2396 wants to merge 1 commit into
HandyOrg:masterfrom
ABA2396:fix/row-arrange-while-window-hidden

Conversation

@ABA2396

@ABA2396 ABA2396 commented Aug 17, 2026

Copy link
Copy Markdown

问题

hc:Divider(或任何以 hc:Row/hc:Col 为模板根的控件)的模板在 「存在 Hidden 祖先」 的状态下被实例化时(最典型的是 「最小化到托盘」 场景:宿主窗口 WindowState = Minimized + Visibility = HiddenDivider 自身或任何祖先为 Hidden 时同理),该元素会永久渲染失败:

  • 控件本身能拿到正常的布局尺寸(例如 420×15);
  • 但模板内的 hc:Col 子元素 ActualWidth/ActualHeight 永久保持 0,分隔线与内容完全不显示;
  • 祖先恢复 Visible 后不会自愈,手动调用 InvalidateMeasure / InvalidateArrange / UpdateLayout 均无效。

而使用标准 Grid/Border 模板的元素在相同时序下完全正常,因此该问题特定于 Row/Col 栅格。

根因

Row.ArrangeOverride 使用 child.IsVisible 决定是否跳过 Col

foreach (var child in cols)
{
    if (!child.IsVisible)
    {
        continue;
    }
    ...
    child.Arrange(childBounds);
}

IsVisible 是综合考虑整条祖先链(包括宿主窗口)的运行时可见性——窗口 Hidden 期间实例化的模板子树在布局过程中 IsVisible == false,导致这些 Col 被跳过、从未被 Arrange。窗口恢复可见后,被跳过 ArrangeCol 布局状态无法再恢复,ActualWidth/ActualHeight 永久为 0(Measure 阶段是正常的,DesiredSize 与可见时添加的完全一致,仅 Arrange 失败)。

该状态没有任何自愈路径——实测以下手段全部无效:

  • 窗口恢复后 Col.IsVisible 实际已变回 true,但 Actual 仍为 0×0;
  • Divider 祖先链调用 InvalidateMeasure / InvalidateArrange / UpdateLayout
  • Col 自身调用 InvalidateMeasure / InvalidateArrange / UpdateLayout
  • Divider 自身 VisibilityHidden 改回 Visible,或先改为 Collapsed 再改回 Visible

(对比:Collapsed 场景天然不触发本 bug——Collapsed 子树不参与布局、模板不会实例化,恢复 Visible 时才首次展开,首次布局即正常,见下方差异分析。)

修复

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

         foreach (var child in cols)
         {
-            if (!child.IsVisible)
+            // Check the local Visibility, not IsVisible: Cols skipped while the hosting
+            // window is hidden can never recover their layout once it is shown again.
+            if (child.Visibility != Visibility.Visible)
             {
                 continue;
             }

行为差异分析(相对原实现):

场景 原实现(IsVisible 新实现(Visibility
Col 自身 CollapsedHidden 跳过 跳过(一致)
自身或任何祖先 Collapsed 期间子树不布局、模板不实例化;恢复 Visible 时才首次展开,首次布局即正常 同左(一致)
任何 Hidden 祖先(Divider 自身 Hidden、宿主窗口 Hidden 等,Col 自身 Visible 模板在 Hidden 期间实例化,Col 被跳过且事后无法自愈(本 bug) 正常 Arrange,恢复可见后正常显示

关键在于 WPF 两种 「不可见」 的语义差异:Collapsed 的子树不参与布局、模板不会实例化,恢复 Visible 时是 「首次布局 + 模板首次展开」,天然不触发本 bug;而 Hidden 参与布局,模板照常实例化,Col 会在 IsVisible == false 的布局轮次中被跳过 Arrange。实测(NuGet 3.6.0-rc3,窗口始终可见):Divider 自身或祖先为 Collapsed 时恢复后 Line ActualW = 138.7 / 124.5(正常);Divider 自身 Hidden 期间实例化、改回 VisibleLine ActualW 仍为 0.0——与宿主窗口 Hidden 场景症状完全一致。

验证

复现工程(net10.0-windows + HandyControl):窗口隐藏期间向 ItemsControl 添加使用 hc:Divider 的 item,2 秒后恢复窗口,读取可视化树:

修复前(NuGet 3.5.1 / 3.6.0-rc3,两者均复现):

[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

修复后(本分支源码构建的 HandyControl.dll 直接替换验证):

[hc:Divider] Divider Actual=420.0x15.2, Line ActualW=181.8

完整复现工程与运行截图见 issue。

@ABA2396

ABA2396 commented Aug 17, 2026

Copy link
Copy Markdown
Author

fix #1818

ABA2396 added a commit to MaaAssistantArknights/MaaAssistantArknights that referenced this pull request Aug 17, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant