首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> 關於.NET

Windows 8 Store Apps 開發實戰 (17):控制項基礎核心 — Measure/Arrange 佈局系統、GeneralTransform 座標轉換、VisualTree 視覺樹操作

     編輯:關於.NET

前言:理解控制項運作的三大基石

自訂控制項、複雜佈局、座標轉換、視覺樹搜尋,都是進階開發的必修課。本篇深入解析 WinRT 佈局系統的 Measure/Arrange 雙階段遞迴機制、GeneralTransform 跨元素座標映射、VisualTreeHelper 視覺樹遍歷與搜尋技巧,並提供可直接參考的自訂 Panel 範例。

一、Measure/Arrange — 佈局系統的雙階段協定

核心流程

  1. Measure 階段:父元素告知可用空間 availableSize,子元素回報期望尺寸 desiredSize(更新 DesiredSize 屬性)。
  2. Arrange 階段:父元素決定最終分配空間 finalSize,子元素擺放位置(更新 RenderSize、ActualWidth/Height)。

自訂 Panel 範例:MyStackPanel

public class MyStackPanel : StackPanel
{
    // 1. 父傳遞 availableSize 給子元素測量
    protected override Size MeasureOverride(Size availableSize)
    {
        Size desiredSize = base.MeasureOverride(availableSize);
        Debug.WriteLine("availableSize: " + availableSize);
        Debug.WriteLine("desiredSize: " + desiredSize);
        return desiredSize;
        
        /* 自訂邏輯參考:
        Size childrenSize = new Size(0, 0);
        foreach (UIElement child in Children)
        {
            child.Measure(new Size(Double.PositiveInfinity, Double.PositiveInfinity));
            childrenSize.Width += child.DesiredSize.Width;
            childrenSize.Height += child.DesiredSize.Height;
        }
        return childrenSize; */
    }

    // 2. 父決定 finalSize,子元素安排位置
    protected override Size ArrangeOverride(Size finalSize)
    {
        Size renderSize = base.ArrangeOverride(finalSize);
        Debug.WriteLine("finalSize: " + finalSize);
        Debug.WriteLine("renderSize: " + renderSize);
        return renderSize;
        
        /* 自訂邏輯參考:
        Point childPos = new Point(0, 0);
        foreach (UIElement child in Children)
        {
            child.Arrange(new Rect(childPos, new Size(child.DesiredSize.Width, child.DesiredSize.Height)));
            childPos.Y += child.RenderSize.Height;
        }
        return finalSize; */
    }
}

輸出結果解讀

availableSize: 200,200   // 父給的空間
desiredSize: 150,250     // 子元素想要的空間(超過高度)
finalSize: 200,250       // 父妥協給的最終空間
renderSize: 200,250      // 實際呈現尺寸

關鍵屬性與方法

成員所屬類別說明
Measure(Size)UIElement觸發測量,更新 DesiredSize
Arrange(Rect)UIElement觸發排版,更新 RenderSize
MeasureOverrideFrameworkElement自訂測量邏輯
ArrangeOverrideFrameworkElement自訂排版邏輯
UpdateLayout()UIElement強制立即重跑佈局遞迴

二、GeneralTransform — 跨元素座標映射

UIElement.TransformToVisual(UIElement visual) 回傳 GeneralTransform,可將座標從來源元素空間轉換到目標元素空間(或螢幕空間)。

常用場景

// 1. 元素相對螢幕絕對位置
GeneralTransform gt = element.TransformToVisual(null); // null = 螢幕根座標
Point screenPos = gt.TransformPoint(new Point(0, 0));
Rect screenRect = new Rect(screenPos, new Size(element.ActualWidth, element.ActualHeight));

// 2. 元素 A 相對元素 B 的位置
GeneralTransform gt = elementA.TransformToVisual(elementB);
Point relativePos = gt.TransformPoint(new Point(0, 0));

三、VisualTreeHelper — 視覺樹操作利器

核心靜態方法

方法用途
GetChildrenCount(DependencyObject)取得直系子元素數量
GetChild(DependencyObject, int index)取得指定索引子元素
GetParent(DependencyObject)取得父元素
FindElementsInHostCoordinates(Point, UIElement, bool)點擊測試:取得指定點包含的所有元素(含 ControlTemplate 內部)
FindElementsInHostCoordinates(Rect, UIElement, bool)區域測試:取得矩形區域內所有元素

實戰:遞迴搜尋 ControlTemplate 內具名元素

private T GetVisualChild<T>(DependencyObject parent, string name) where T : FrameworkElement
{
    T child = default(T);
    int count = VisualTreeHelper.GetChildrenCount(parent);
    for (int i = 0; i < count; i++)
    {
        DependencyObject obj = VisualTreeHelper.GetChild(parent, i);
        child = obj as T;
        if (child == null || child.Name != name)
            child = GetVisualChild<T>(obj, name); // 遞迴深入
        if (child != null) break;
    }
    return child;
}

// 用法:在 ScrollViewer Template 中找 VerticalScrollBar
ScrollBar scrollBar = GetVisualChild<ScrollBar>(scrollViewer, "VerticalScrollBar");

點擊測試範例

void container_Tapped(object sender, TappedRoutedEventArgs e)
{
    // 點擊點包含的所有元素(含 Template 內部)
    var elements = VisualTreeHelper.FindElementsInHostCoordinates(e.GetPosition(null), container, true);
    foreach (var el in elements) Debug.WriteLine(el.GetType().Name);
    
    // 100x100 區域內的所有元素
    var elementsRect = VisualTreeHelper.FindElementsInHostCoordinates(
        new Rect(e.GetPosition(null), new Size(100, 100)), container, true);
}

完整程式碼下載

原始碼完整包(含所有範例專案)

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved