共用方式為


版面配置

此主題描述 Windows Presentation Foundation (WPF) 版面配置系統。 了解如何及何時進行版面配置計算,對於在 WPF 中建立使用者介面十分重要。

本主題包含下列幾節:

項目週框方塊

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

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

此螢幕擷取畫面顯示典型的方格,沒有周框方塊疊加於其上。

使用下列 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 (部分機器翻譯) 元素的周框方塊。

螢幕擷取畫面顯示現在可以看見 TextBlock 周框方塊。

如黃色矩形所示,已針對 TextBlock (部分機器翻譯) 元素配置的空間實際上遠大於其顯示的大小。 將其他元素新增至 Grid (部分機器翻譯) 時,可根據所新增元素的類型和大小來縮減或擴充此配置。

您可以使用 GetLayoutSlot (部分機器翻譯) 方法,將 TextBlock (部分機器翻譯) 的版面配置位置轉譯為 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

配置系統

簡單來說,版面配置是可調整項目大小、定位項目並在螢幕上繪製項目的遞迴系統。 更具體來說,版面配置會描述測量和排列 Panel (部分機器翻譯) 元素之 Children (英文) 集合成員的流程。 版面配置是需要大量處理的程序。 Children (英文) 集合越大,必須進行的計算次數就越多。 您也可以根據擁有集合之 Panel (部分機器翻譯) 元素所定義的版面配置行為,引進複雜度。 相較於更複雜的 Panel (部分機器翻譯) (例如 Grid (部分機器翻譯)),相對簡單的 Panel (部分機器翻譯) (例如 Canvas (部分機器翻譯)) 的效能明顯更好。

每次子 UIElement (部分機器翻譯) 變更其位置時,就有可能觸發版面配置系統的新階段。 因此,請務必了解可叫用配置系統的事件,因為不必要的叫用可能會導致不良的應用程式效能。 下列描述在叫用配置系統時所發生的程序。

  1. UIElement (部分機器翻譯) 的版面配置流程一開始會先測量其核心屬性。

  2. 評估 FrameworkElement (部分機器翻譯) 上定義的調整大小屬性,例如 Width (部分機器翻譯)、Height (部分機器翻譯) 和 Margin (部分機器翻譯)。

  3. 套用 Panel 專有的邏輯,例如 Dock (部分機器翻譯) 方向或堆疊 Orientation (部分機器翻譯)。

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

  5. Children (英文) 集合會繪製於螢幕上。

  6. 如果將其他 Children (英文) 新增至集合、套用 LayoutTransform (部分機器翻譯) 或呼叫 UpdateLayout (部分機器翻譯) 方法,則會再次叫用該流程。

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

測量和排列子系

版面配置系統會為 Children (英文) 集合的每個成員完成兩個階段︰測量階段和排列階段。 每個子 Panel (部分機器翻譯) 都提供自己的 MeasureOverride 和 (英文) 和 ArrangeOverride (英文) 方法來實現自己特定的版面配置行為。

在測量階段,會評估 Children (英文) 集合的每個成員。 此流程會從呼叫 Measure (英文) 方法開始。 此方法是在父 Panel (部分機器翻譯) 元素的實作中呼叫,而且不需明確呼叫即可進行版面配置。

首先,評估 UIElement (部分機器翻譯) 的原生大小屬性,例如 Clip (部分機器翻譯) 和 Visibility (部分機器翻譯)。 這會產生名為 constraintSize 的值,此值會傳遞至 MeasureCore (英文)。

其次,會處理 FrameworkElement (部分機器翻譯) 上定義的架構屬性,而這會影響 constraintSize 的值。 這些屬性通常會描述底層 UIElement (部分機器翻譯) 的調整大小特性,例如,其 Height (部分機器翻譯)、Width (部分機器翻譯)、Margin (部分機器翻譯) 和 Style (英文)。 所有這些屬性都可以變更顯示項目所需的空間。 接著會使用 constraintSize 作為參數來呼叫 MeasureOverride (英文)。

注意

Height (部分機器翻譯)、Width (部分機器翻譯)、ActualHeight (部分機器翻譯) 和 ActualWidth (部分機器翻譯) 的屬性之間存有差異。 例如,ActualHeight (部分機器翻譯) 屬性是根據其他高度輸入和版面配置系統所計算出來的值。 此值由版面配置系統本身根據實際轉譯階段所設定,因此,可能會稍微落後作為輸入變更基礎之屬性 (例如 Height (部分機器翻譯)) 的設定值。

由於 ActualHeight (部分機器翻譯) 是計算出來的值,所以,您應該注意,因版面配置系統所進行的各種作業,可能會造成多個或累加式報告變更。 配置系統可能會計算子項目所需的測量空間、父項目的條件約束,依此類推。

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

此排列階段會從呼叫 Arrange (英文) 方法開始。 在排列階段期間,父 Panel (部分機器翻譯) 元素會產生代表子元素界限的矩形。 此值會傳遞至 ArrangeCore (英文) 方法進行處理。

ArrangeCore (英文) 方法會評估子元素的 DesiredSize (英文),並評估可能影響元素轉譯大小的任何其他邊界。 ArrangeCore (英文) 會產生 arrangeSize,以參數形式傳遞至 Panel (部分機器翻譯) 的 ArrangeOverride (英文) 方法。 ArrangeOverride (英文) 會產生子元素的 finalSize。 最後,ArrangeCore (英文) 方法會執行最終位移屬性評估 (例如邊界和對齊方式),並將子元素放在其版面配置位置內。 子系不需要 (通常也不會) 填入整個已配置的空間。 控制權接著會傳回到父 Panel (部分機器翻譯),版面配置流程隨即完成。

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

WPF 包含一組衍生自 Panel (部分機器翻譯) 的元素。 這些 Panel (部分機器翻譯) 元素可啟用許多複雜的版面配置。 例如,使用 StackPanel (部分機器翻譯) 元素可以輕鬆地堆疊元素,而使用 Canvas (部分機器翻譯) 可以有更複雜且自由流動的版面配置。

下表摘要說明可用的版面配置 Panel (部分機器翻譯) 元素。

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

對於需要使用任何預先定義的 Panel (部分機器翻譯) 元素無法達成之版面配置的應用程式,可以藉由繼承自 Panel (部分機器翻譯) 並覆寫 MeasureOverride (英文) 和 ArrangeOverride (英文) 方法來達成自訂的版面配置行為。

版面配置效能考量

版面配置是遞迴程序。 每次叫用版面配置系統期間,都會處理 Children (英文) 集合中的每個子元素。 因此,不需要時,應該避免觸發配置系統。 下列考量可協助您達到更佳的效能。

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

    使用公用旗標,可標記其值可以初始化配置系統的相依性屬性。 AffectsMeasure (部分機器翻譯) 和 AffectsArrange (部分機器翻譯) 提供有關哪些屬性值變更將由版面配置系統強制執行遞迴更新的有用線索。 一般而言,任何可能影響元素周框方塊大小的屬性都應該將 AffectsMeasure (部分機器翻譯) 旗標設定為 true。 如需詳細資訊,請參閱相依性屬性概觀

  • 盡可能使用 RenderTransform (部分機器翻譯) (而非 LayoutTransform (部分機器翻譯))。

    LayoutTransform (部分機器翻譯) 對於影響使用者介面 (UI) 內容來說是非常有用的方式。 不過,如果轉換的效果不一定要影響其他元素的位置,最好改為使用 RenderTransform (部分機器翻譯),因為 RenderTransform (部分機器翻譯) 不會叫用版面配置系統。 LayoutTransform (部分機器翻譯) 會套用其轉換,並強制執行遞迴版面配置更新以將受影響元素的新位置納入考慮。

  • 避免不必要地呼叫 UpdateLayout (部分機器翻譯)。

    UpdateLayout (部分機器翻譯) 方法會強制執行遞迴版面配置更新,但通常不需要。 除非您確定需要完整更新,否則請依賴配置系統來呼叫此方法。

  • 使用大型 Children (英文) 集合時,請考慮使用 VirtualizingStackPanel (部分機器翻譯),而不是一般的 StackPanel (部分機器翻譯)。

    透過將子集合虛擬化,VirtualizingStackPanel (部分機器翻譯) 只會將目前位於父檢視區內的物件保留在記憶體中。 因此,在大部分情況下,都可以大幅改善效能。

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

WPF 圖形系統會使用裝置獨立的單位來啟用解析度和裝置獨立性。 每個裝置獨立像素都會依系統的 DPI 設定自動進行調整。 這讓 WPF 應用程式針對不同的 DPI 設定進行適當調整,並自動讓應用程式感知 DPI。

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

預設會停用版面配置進位。 若要啟用版面配置進位,請在任何 FrameworkElement (部分機器翻譯) 上將 UseLayoutRounding (部分機器翻譯) 屬性設定為 true。 因為這是相依性屬性,所以值將會傳播到視覺樹狀結構中的所有子系。 若要針對整個 UI 啟用版面配置進位,請在根容器上將 UseLayoutRounding (部分機器翻譯) 設定為 true。 如需範例,請參閱 UseLayoutRounding

後續步驟

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

另請參閱