Layout

本主題描述Windows Presentation Foundation (WPF) 版面配置系統。 瞭解版面配置計算在 WPF 中建立使用者介面的必要方式和時機。

本主題包含下列幾節:

項目週框方塊

在 WPF 中考慮版面配置時,請務必瞭解圍繞所有元素的周框方塊。 配置系統所取用的每個 FrameworkElement 都可以視為配置中位置的矩形。 類別 LayoutInformation 會傳回專案配置或位置的界限。 矩形的大小取決於計算可用的螢幕空間、任何條件約束的大小、版面配置特定屬性 (例如邊界和邊框間距) ,以及父 Panel 元素的個別行為。 處理此資料時,配置系統能夠計算特定 Panel 之所有子系的位置。 請務必記住,在父元素上定義的調整大小特性,例如 Border 會影響其子系。

下圖顯示簡單的版面配置。

Screenshot that shows a typical grid, no bounding box superimposed.

您可以使用下列 XAML 來達成此版面配置。

<Grid Name="myGrid" Background="LightSteelBlue" Height="150">
  <Grid.ColumnDefinitions>
    <ColumnDefinition Width="250"/>
  </Grid.ColumnDefinitions>
  <Grid.RowDefinitions>
    <RowDefinition />
    <RowDefinition />
    <RowDefinition />
  </Grid.RowDefinitions>
  <TextBlock Name="txt1" Margin="5" FontSize="16" FontFamily="Verdana" Grid.Column="0" Grid.Row="0">Hello World!</TextBlock>
  <Button Click="getLayoutSlot1" Width="125" Height="25" Grid.Column="0" Grid.Row="1">Show Bounding Box</Button>
  <TextBlock Name="txt2" Grid.Column="1" Grid.Row="2"/>
</Grid>

TextBlock 一專案裝載于 Grid 內。 雖然文字只會填滿第一個資料行的左上角,但 的已配置空間 TextBlock 實際上會更大。 您可以使用 方法來擷取 GetLayoutSlot 任何 的 FrameworkElement 周框方塊。 下圖顯示 專案的周框方塊 TextBlock

Screenshot that shows that the TextBlock bounding box is now visible.

如黃色矩形所示,元素的已配置空間 TextBlock 實際上遠大於其顯示空間。 當其他元素加入 至 Grid 時,此配置可能會根據新增的元素類型和大小而縮小或展開。

的配置位置 TextBlock 會使用 GetLayoutSlot 方法轉譯成 Path 。 這種方式可用於顯示項目的週框方塊。

private void getLayoutSlot1(object sender, System.Windows.RoutedEventArgs e)
{
    RectangleGeometry myRectangleGeometry = new RectangleGeometry();
    myRectangleGeometry.Rect = LayoutInformation.GetLayoutSlot(txt1);
    Path myPath = new Path();
    myPath.Data = myRectangleGeometry;
    myPath.Stroke = Brushes.LightGoldenrodYellow;
    myPath.StrokeThickness = 5;
    Grid.SetColumn(myPath, 0);
    Grid.SetRow(myPath, 0);
    myGrid.Children.Add(myPath);
    txt2.Text = "LayoutSlot is equal to " + LayoutInformation.GetLayoutSlot(txt1).ToString();
}
Private Sub getLayoutSlot1(ByVal sender As Object, ByVal e As RoutedEventArgs)
    Dim myRectangleGeometry As New RectangleGeometry
    myRectangleGeometry.Rect = LayoutInformation.GetLayoutSlot(txt1)
    Dim myPath As New Path
    myPath.Data = myRectangleGeometry
    myPath.Stroke = Brushes.LightGoldenrodYellow
    myPath.StrokeThickness = 5
    Grid.SetColumn(myPath, 0)
    Grid.SetRow(myPath, 0)
    myGrid.Children.Add(myPath)
    txt2.Text = "LayoutSlot is equal to " + LayoutInformation.GetLayoutSlot(txt1).ToString()
End Sub

配置系統

簡單來說,版面配置是可調整項目大小、定位項目並在螢幕上繪製項目的遞迴系統。 更具體來說,版面配置描述測量及排列專案 Children 集合成員 Panel 的程式。 版面配置是需要大量處理的程序。 集合愈大 Children ,必須建立的計算數目愈大。 也可以根據擁有集合的專案 Panel 所定義的版面配置行為來導入複雜度。 相對簡單的 Panel ,例如 Canvas ,相較于更複雜的 Panel ,例如 Grid ,可以大幅提升效能。

每次子 UIElement 系變更其位置時,可能會觸發配置系統的新傳遞。 因此,請務必了解可叫用配置系統的事件,因為不必要的叫用可能會導致不良的應用程式效能。 下列描述在叫用配置系統時所發生的程序。

  1. 子系 UIElement 會先測量其核心屬性,以開始配置程式。

  2. 評估上 FrameworkElement 定義的調整大小屬性,例如 WidthHeightMargin

  3. Panel套用特定邏輯,例如 Dock 方向或堆疊 Orientation

  4. 測量所有子系之後,會排列內容。

  5. 集合 Children 會在畫面上繪製。

  6. 如果新增至 Children 集合、套用 、 LayoutTransformUpdateLayout 呼叫 方法,則會再次叫用進程。

下列各節會詳述定義此程序和其叫用方式。

測量和排列子系

版面配置系統會針對集合的每個成員 Children 完成兩次傳遞,一個量值階段和一個排列階段。 每個子系 Panel 都會提供自己的 MeasureOverrideArrangeOverride 方法來達成自己的特定版面配置行為。

在量值階段期間,會評估集合的每個成員 Children 。 此程式會從呼叫 Measure 方法開始。 這個方法是在父 Panel 元素的實作內呼叫,不需要明確呼叫配置才能發生。

首先,會評估 的 UIElement 原生大小屬性,例如 ClipVisibility 。 這會產生名為 constraintSize 的值,這個值會傳遞至 MeasureCore

其次,會處理上 FrameworkElement 定義的架構屬性,這會影響 的值 constraintSize 。 這些屬性通常會描述基礎 UIElement 的大小特性,例如其 HeightWidthMarginStyle 。 所有這些屬性都可以變更顯示項目所需的空間。 MeasureOverride 接著會以 constraintSize 作為參數呼叫 。

注意

WidthActualHeightActualWidth 和 的屬性 Height 之間有差異。 例如, ActualHeight 屬性是以其他高度輸入和版面配置系統為基礎的計算值。 此值是由配置系統本身根據實際轉譯階段所設定,因此可能會稍微落後屬性的設定值,例如 Height ,這是輸入變更的基礎。

因為 ActualHeight 是計算值,所以您應該注意,因為配置系統的各種作業,可能會有多個或累加回報的變更。 配置系統可能會計算子項目所需的測量空間、父項目的條件約束,依此類推。

量值階段的最終目標是讓子系判斷其 DesiredSize ,這會在 MeasureCore 呼叫期間發生。 值 DesiredSize 會儲存在 Measure 內容排列階段期間使用。

排列傳遞的開頭為方法的 Arrange 呼叫。 在排列階段期間,父 Panel 元素會產生代表子系界限的矩形。 這個值會傳遞至 ArrangeCore 方法進行處理。

方法 ArrangeCoreDesiredSize 評估子系的 ,並評估可能會影響專案呈現大小的任何其他邊界。 ArrangeCore 會產生 arrangeSize ,它會當做參數傳遞至 ArrangeOverridePanel 方法。 ArrangeOverride 會產生 finalSize 子系的 。 最後,方法 ArrangeCore 會執行位移屬性的最終評估,例如邊界和對齊方式,並將子系放在其版面配置位置內。 子系不需要 (通常也不會) 填入整個已配置的空間。 控制項接著會傳回父 Panel 代,且版面配置程式已完成。

面板項目和自訂版面配置行為

WPF 包含衍生自 Panel 的元素群組。 這些 Panel 元素可啟用許多複雜的版面配置。 例如,使用 元素可以輕鬆地達成 StackPanel 堆疊元素,而使用 更複雜且自由流動的版面配置則可使用 Canvas

下表摘要說明可用的版面配置 Panel 元素。

面板名稱 描述
Canvas 定義區域,您可以在其中依相對於區域的座標 Canvas 明確定位子項目。
DockPanel 定義一個區域,可供您在其中以子元素彼此間相對的水平或垂直方式排列子元素。
Grid 定義彈性的格線區域,由欄與列組成。
StackPanel 將子元素排成單一行,以水平或垂直方式排列。
VirtualizingPanel 提供 Panel 項目的架構,這些項目會虛擬化其子資料集合。 這是 abstract 類別。
WrapPanel 將子項目置放於由左至右的連續位置,其中會在包含方塊的邊緣將內容換至下一行。 後續順序會根據 屬性的值 Orientation ,循序從上到下或由右至左進行。

對於需要使用任何預先 Panel 定義元素而不需要配置的應用程式,自訂版面配置行為可以透過繼承和 PanelMeasureOverride 寫 和 ArrangeOverride 方法來達成。

版面配置效能考量

版面配置是遞迴程序。 集合中的每個 Children 子專案都會在配置系統的每個調用期間進行處理。 因此,不需要時,應該避免觸發配置系統。 下列考量可協助您達到更佳的效能。

  • 請注意哪些屬性值變更將會由配置系統強制遞迴更新。

    使用公用旗標,可標記其值可以初始化配置系統的相依性屬性。 AffectsMeasure 並提供 AffectsArrange 有用的線索,讓屬性值變更會強制配置系統進行遞迴更新。 一般而言,可能會影響元素周框方塊大小的任何屬性都應該將 AffectsMeasure 旗標設定為 true。 如需詳細資訊,請參閱相依性屬性概觀

  • 可能的話,請使用 RenderTransform ,而不是 LayoutTransform

    LayoutTransform是影響使用者介面內容 (UI) 非常有用的方式。 不過,如果轉換的效果不一定會影響其他元素的位置,最好改用 , RenderTransform 因為 RenderTransform 不會叫用配置系統。 LayoutTransform 會套用其轉換,並強制遞迴配置更新,以考慮受影響元素的新位置。

  • 避免對 UpdateLayout 進行不必要的呼叫。

    方法 UpdateLayout 會強制遞迴版面配置更新,而且通常不需要。 除非您確定需要完整更新,否則請依賴配置系統來呼叫此方法。

  • 使用大型 Children 集合時,請考慮使用 而不是 VirtualizingStackPanel 一般 StackPanel

    藉由虛擬化子集合, VirtualizingStackPanel 唯一會將目前在父系 ViewPort 內的物件保留在記憶體中。 因此,在大部分情況下,都可以大幅改善效能。

子像素轉譯和版面配置進位

WPF 圖形系統使用與裝置無關的單位來啟用解析度和裝置獨立性。 每個裝置獨立圖元都會使用系統的每英吋點自動縮放, (DPI) 設定。 這會為不同的 DPI 設定提供 WPF 應用程式適當的縮放比例,並讓應用程式自動感知 DPI。

不過,此 DPI 獨立性可能會因為消除鋸齒而建立不規則的邊緣轉譯。 如果邊緣位置落在裝置像素中間,而不是裝置像素之間,則這些成品一般會看起來模糊或具有半透明邊緣。 配置系統提供一種方式,利用版面配置來進行這項的調整。 版面配置進位是配置系統會在版面配置階段期間將任何非整數像素值四捨五入。

預設會停用版面配置進位。 若要啟用版面配置舍入,請將 UseLayoutRounding 任何 FrameworkElement 上的 屬性設定為 true 。 因為這是相依性屬性,所以值將會傳播到視覺樹狀結構中的所有子系。 若要啟用整個 UI 的配置四捨五入,請將 設定 UseLayoutRoundingtrue 為根容器上的 。 如需範例,請參閱 UseLayoutRounding

後續步驟

了解如何測量和排列項目是了解版面配置的第一個步驟。 如需可用 Panel 元素的詳細資訊,請參閱 面板概觀。 若要進一步了解會影響版面配置的各種定位屬性,請參閱對齊、邊界和填補概觀。 當您準備好將它全部放在輕量型應用程式中時,請參閱逐步解說 :我的第一個 WPF 傳統型應用程式

另請參閱