Measure和Arrange

WPF 布局学习笔记 —— Measure 与 Arrange

一、基础概念

1️⃣ 布局生命周期

Measure → Arrange → Render
  • Measure:问“我需要多大?”
  • Arrange:定“你放哪、放多大?”
  • Render:画出来

2️⃣ 一句话总结

阶段 角色 本质
Measure 协商 子控件报“我最低需要多少空间”
Arrange 执行 父控件决定子控件位置和最终尺寸
Render 呈现 根据 Arrange 的结果绘制 UI

二、Measure(测量阶段)

1️⃣ 核心概念

  • MeasureOverride(Size availableSize)
    availableSize:父控件允许的最大空间

  • Measure 的作用:

    • 子控件告诉父控件 自己最低需要多大
    • 不一定得到父控件给的空间
  • Measure 的结果:

    • child.DesiredSize → 子控件报的“需求大小”

2️⃣ 中文翻译

“父控件给你一个预算(availableSize),你告诉我最少要多大才能正常工作。”

3️⃣ 常见用法

① 容器型控件(Panel / Adorner / Overlay)

foreach (UIElement child in Children)
    child.Measure(availableSize);
  • 子控件自由测量
  • 父控件收集最大需求或叠加结果

② 内容型控件(Button / TextBlock / 自绘控件)

  • 返回自己内容的大小
  • 例:return new Size(100,50);

③ 覆盖型控件(Adorner)

  • 不参与布局
  • 例:return constraint;Size.Empty

4️⃣ 注意点

  • Measure 不是决定大小,只是报需求
  • 可接受 availableSizeInfinity → 意味着“无限预算”
  • Measure 顺序通常按子控件顺序执行

5️⃣ 实战例子

protected override Size MeasureOverride(Size availableSize)
{
    Size result = new Size();
    foreach (UIElement child in Children)
    {
        child.Measure(availableSize);  // 让子控件报需求
        result.Width += child.DesiredSize.Width;   // 横向累加
        result.Height = Math.Max(result.Height, child.DesiredSize.Height);
    }
    return result;
}
  • child.Measure(availableSize) → 让子控件“报账”
  • child.DesiredSize → 子控件告诉你它需要多大
  • 累加/取最大 → 父控件自己的需求

6️⃣ 进阶技巧

可选缩减预算(用于横/纵向布局)

double remainingWidth = availableSize.Width;
foreach (UIElement child in Children)
{
    child.Measure(new Size(remainingWidth, availableSize.Height));
    remainingWidth -= child.DesiredSize.Width;
}

避免所有子控件都以为自己可以用满父控件宽度


三、Arrange(摆放阶段)

1️⃣ 核心概念

  • ArrangeOverride(Size finalSize)
    finalSize:父控件决定给你的最终空间

  • Arrange 的作用:

    • 决定子控件的位置 (x, y)
    • 决定子控件最终大小 (width, height)

2️⃣ 中文翻译

“你最终就放在这里,不管你 Measure 报了多少,听我的!”

3️⃣ 特性

  • 命令式:子控件不能拒绝
  • 可以使用负坐标(如 Adorner)
  • 可以放大或缩小子控件(不必遵循 DesiredSize)
  • 返回值一般就是 finalSize

4️⃣ 实战例子

protected override Size ArrangeOverride(Size finalSize)
{
    Children[0].Arrange(new Rect(0, 0, 50, 50));       // 左上
    Children[1].Arrange(new Rect(100, 0, 50, 50));     // 右上
    Children[2].Arrange(new Rect(0, 80, 200, 50));     // 下方
    return finalSize;
}
  • 把 Measure 得到的尺寸转换成 屏幕坐标和大小
  • 所有空间逻辑必须自己计算

5️⃣ Arrange 与 Measure 的关系

Measure Arrange
提需求 执行摆放
协商 命令
DesiredSize → 参考 可忽略 DesiredSize

四、Measure 与 Arrange 直观理解

1️⃣ 比喻法

阶段 对应生活场景
Measure 各部门报预算(多少空间够用)
Arrange 老板拍板分配(预算分配到具体位置)
Render 各部门真正花钱(画到屏幕)

2️⃣ “脑内画面法”

  1. Measure → 每个子控件告诉你自己大小
  2. Arrange → 父控件给每个子控件一个矩形(Rect)
  3. Render → 屏幕显示矩形里的内容

五、常见坑与实践技巧

场景 问题 建议
横向布局 所有子控件都传 availableSize → DesiredSize 全报满 → 父控件撑大 逐步减剩余空间传入 Measure
Adorner / Overlay 放在 OnRender 里调用 Arrange → 打乱生命周期 ArrangeOverride 或自定义 Panel 中处理
负坐标 不理解 → 控件消失 允许负值,理解为相对于父控件左上角偏移
返回值 Measure / Arrange 返回值随意 → 布局异常 Measure 返回父控件所需最小空间;Arrange 返回 finalSize

六、总结

  1. Measure = 提需求,协商预算 → DesiredSize
  2. Arrange = 下命令,确定位置和最终尺寸
  3. Render = 用资源,把子控件画出来
  4. 横向/纵向布局 → 注意 Measure 传递的 Size
  5. Adorner / Overlay → Measure 可随便放权,Arrange 控制位置

posted @ 2025-12-23 13:06  Ytytyty  阅读(2)  评论(0)    收藏  举报