LayoutLayout

本主題說明 WPF) 配置系統的 Windows Presentation Foundation (。This topic describes the Windows Presentation Foundation (WPF) layout system. 瞭解在 WPF 中建立使用者介面時,如何和何時進行版面配置計算是不可或缺的。Understanding how and when layout calculations occur is essential for creating user interfaces in WPF.

本主題包含下列幾節:This topic contains the following sections:

項目週框方塊Element Bounding Boxes

在 WPF 中考慮版面配置時,請務必瞭解圍繞所有元素的周框方塊。When thinking about layout in WPF, it is important to understand the bounding box that surrounds all elements. 配置系統所耗用的每個都可視為作為配置的 FrameworkElement 矩形。Each FrameworkElement consumed by the layout system can be thought of as a rectangle that is slotted into the layout. 類別會傳回 LayoutInformation 元素版面配置配置或位置的界限。The LayoutInformation class returns the boundaries of an element's layout allocation, or slot. 矩形的大小取決於計算可用的螢幕空間、任何條件約束的大小、配置特定屬性 (例如邊界和填補) ,以及父元素的個別行為 PanelThe size of the rectangle is determined by calculating the available screen space, the size of any constraints, layout-specific properties (such as margin and padding), and the individual behavior of the parent Panel element. 處理此資料,配置系統能夠計算特定的所有子系的位置 PanelProcessing this data, the layout system is able to calculate the position of all the children of a particular Panel. 請務必記住,在父元素上定義的大小調整特性(例如 Border )會影響其子系。It is important to remember that sizing characteristics defined on the parent element, such as a Border, affect its children.

下圖顯示簡單的版面配置。The following illustration shows a simple layout.

顯示一般方格的螢幕擷取畫面,沒有重迭的周框方塊。

使用下列 XAMLXAML 可達成這個版面配置。This layout can be achieved by using the following XAMLXAML.

<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 于內 GridA single TextBlock element is hosted within a Grid. 雖然文字只會填滿第一個資料行的左上角,但的配置空間其實更 TextBlock 大。While the text fills only the upper-left corner of the first column, the allocated space for the TextBlock is actually much larger. 您可以使用方法來取出任何的周框方塊 FrameworkElement GetLayoutSlotThe bounding box of any FrameworkElement can be retrieved by using the GetLayoutSlot method. 下圖顯示元素的周框方塊 TextBlockThe following illustration shows the bounding box for the TextBlock element.

螢幕擷取畫面:顯示 [TextBlock 周框] 方塊現在是可見的。

如黃色矩形所示,元素配置的空間 TextBlock 其實遠超過它所顯示的空間。As shown by the yellow rectangle, the allocated space for the TextBlock element is actually much larger than it appears. 當額外的元素加入至時 Grid ,此配置可能會壓縮或展開,取決於所加入專案的類型和大小。As additional elements are added to the Grid, this allocation could shrink or expand, depending on the type and size of elements that are added.

的版面配置位置 TextBlock 會使用方法轉譯為 Path GetLayoutSlotThe layout slot of the TextBlock is translated into a Path by using the GetLayoutSlot method. 這種方式可用於顯示項目的週框方塊。This technique can be useful for displaying the bounding box of an element.

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

配置系統The Layout System

簡單來說,版面配置是可調整項目大小、定位項目並在螢幕上繪製項目的遞迴系統。At its simplest, layout is a recursive system that leads to an element being sized, positioned, and drawn. 更具體來說,版面配置會描述測量和排列元素集合成員的程式 Panel ChildrenMore specifically, layout describes the process of measuring and arranging the members of a Panel element's Children collection. 版面配置是需要大量處理的程序。Layout is an intensive process. Children集合越大,必須進行的計算數目就愈大。The larger the Children collection, the greater the number of calculations that must be made. 也可以根據擁有集合的專案所定義的版面配置行為來引進複雜性 PanelComplexity can also be introduced based on the layout behavior defined by the Panel element that owns the collection. 相對較簡單(例如 Panel Canvas )的效能可能會比更複雜的更好 Panel GridA relatively simple Panel, such as Canvas, can have significantly better performance than a more complex Panel, such as Grid.

每次子系 UIElement 變更其位置時,就有可能會由配置系統觸發新的傳遞。Each time that a child UIElement changes its position, it has the potential to trigger a new pass by the layout system. 因此,請務必了解可叫用配置系統的事件,因為不必要的叫用可能會導致不良的應用程式效能。Therefore, it is important to understand the events that can invoke the layout system, as unnecessary invocation can lead to poor application performance. 下列描述在叫用配置系統時所發生的程序。The following describes the process that occurs when the layout system is invoked.

  1. 子系會 UIElement 先測量其核心屬性以開始配置處理。A child UIElement begins the layout process by first having its core properties measured.

  2. 中定義的調整大小屬性 FrameworkElement 會進行評估,例如 WidthHeightMarginSizing properties defined on FrameworkElement are evaluated, such as Width, Height, and Margin.

  3. Panel-套用特定邏輯,例如 Dock 方向或堆疊 OrientationPanel-specific logic is applied, such as Dock direction or stacking Orientation.

  4. 測量所有子系之後,會排列內容。Content is arranged after all children have been measured.

  5. Children集合會在螢幕上繪製。The Children collection is drawn on the screen.

  6. 如果將額外的 Children 加入至集合、套用 LayoutTransform ,或呼叫方法,則會重新叫用進程 UpdateLayoutThe process is invoked again if additional Children are added to the collection, a LayoutTransform is applied, or the UpdateLayout method is called.

下列各節會詳述定義此程序和其叫用方式。This process and how it is invoked are defined in more detail in the following sections.

測量和排列子系Measuring and Arranging Children

配置系統會完成集合中每個成員的兩個傳遞 Children 、量值傳遞和排列行程。The layout system completes two passes for each member of the Children collection, a measure pass and an arrange pass. 每個子系都 Panel 提供自己的 MeasureOverrideArrangeOverride 方法,以達成其專屬的版面配置行為。Each child Panel provides its own MeasureOverride and ArrangeOverride methods to achieve its own specific layout behavior.

在量值傳遞期間,會評估集合的每個成員 ChildrenDuring the measure pass, each member of the Children collection is evaluated. 進程會從呼叫 Measure 方法開始。The process begins with a call to the Measure method. 這個方法是在父元素的實中呼叫 Panel ,不需要明確呼叫,就會發生版面配置。This method is called within the implementation of the parent Panel element, and does not have to be called explicitly for layout to occur.

首先,會評估的原生大小屬性 UIElement ,例如 ClipVisibilityFirst, native size properties of the UIElement are evaluated, such as Clip and Visibility. 這會產生一個名為的值,此值 constraintSize 會傳遞至 MeasureCoreThis generates a value named constraintSize that is passed to MeasureCore.

其次,在上定義的 framework 屬性 FrameworkElement 會進行處理,而這會影響的值 constraintSizeSecondly, framework properties defined on FrameworkElement are processed, which affects the value of constraintSize. 這些屬性通常會描述基礎的調整大小特性 UIElement ,例如其 HeightWidthMarginStyleThese properties generally describe the sizing characteristics of the underlying UIElement, such as its Height, Width, Margin, and Style. 所有這些屬性都可以變更顯示項目所需的空間。Each of these properties can change the space that is necessary to display the element. MeasureOverride 接著會使用 constraintSize 做為參數來呼叫。MeasureOverride is then called with constraintSize as a parameter.

注意

和和的屬性之間有差異 Height Width ActualHeight ActualWidthThere is a difference between the properties of Height and Width and ActualHeight and ActualWidth. 例如, ActualHeight 屬性是以其他高度輸入和版面配置系統為基礎的計算值。For example, the ActualHeight property is a calculated value based on other height inputs and the layout system. 此值是由配置系統本身根據實際轉譯行程設定的,因此可能會稍微落後屬性的設定值(例如 Height ,輸入變更的基礎)。The value is set by the layout system itself, based on an actual rendering pass, and may therefore lag slightly behind the set value of properties, such as Height, that are the basis of the input change.

因為 ActualHeight 是一個計算值,所以您應該要注意,由於配置系統有各種作業,因此可能會有多個或累加的報告變更。Because ActualHeight is a calculated value, you should be aware that there could be multiple or incremental reported changes to it as a result of various operations by the layout system. 配置系統可能會計算子項目所需的測量空間、父項目的條件約束,依此類推。The layout system may be calculating required measure space for child elements, constraints by the parent element, and so on.

量值傳遞的最終目標是要讓子系判斷其 DesiredSize 在呼叫期間發生的 MeasureCoreThe ultimate goal of the measure pass is for the child to determine its DesiredSize, which occurs during the MeasureCore call. DesiredSize值是由儲存, Measure 以供內容排列階段使用。The DesiredSize value is stored by Measure for use during the content arrange pass.

排列傳遞一開始會先呼叫 Arrange 方法。The arrange pass begins with a call to the Arrange method. 在排列行程期間,父 Panel 元素會產生一個代表子系界限的矩形。During the arrange pass, the parent Panel element generates a rectangle that represents the bounds of the child. 此值會傳遞給 ArrangeCore 方法進行處理。This value is passed to the ArrangeCore method for processing.

ArrangeCore方法會評估子系的, DesiredSize 並評估可能影響元素轉譯大小的任何其他邊界。The ArrangeCore method evaluates the DesiredSize of the child and evaluates any additional margins that may affect the rendered size of the element. ArrangeCore 產生 arrangeSize ,它會傳遞至的 ArrangeOverride 方法 Panel 做為參數。ArrangeCore generates an arrangeSize, which is passed to the ArrangeOverride method of the Panel as a parameter. ArrangeOverride 產生子系的 finalSizeArrangeOverride generates the finalSize of the child. 最後, ArrangeCore 方法會執行位移屬性的最終評估,例如邊界和對齊,並將子系放在其配置位置內。Finally, the ArrangeCore method does a final evaluation of offset properties, such as margin and alignment, and puts the child within its layout slot. 子系不需要 (通常也不會) 填入整個已配置的空間。The child does not have to (and frequently does not) fill the entire allocated space. 控制權接著會傳回父代 Panel ,而且版面配置程式已完成。Control is then returned to the parent Panel and the layout process is complete.

面板項目和自訂版面配置行為Panel Elements and Custom Layout Behaviors

WPF 包含一個衍生自的元素群組 PanelWPF includes a group of elements that derive from Panel. 這些 Panel 元素可提供許多複雜的版面配置。These Panel elements enable many complex layouts. 例如,您可以使用專案輕鬆地達成堆疊專案 StackPanel ,而使用可以更複雜且自由流動的版面配置 CanvasFor example, stacking elements can easily be achieved by using the StackPanel element, while more complex and free flowing layouts are possible by using a Canvas.

下表摘要說明可用的版面配置 Panel 元素。The following table summarizes the available layout Panel elements.

面板名稱Panel name DescriptionDescription
Canvas 定義一個區域,您可以在其中明確地依相對於區域的座標來放置子專案 CanvasDefines an area within which you can explicitly position child elements by coordinates relative to the Canvas area.
DockPanel 定義一個區域,可供您在其中以子元素彼此間相對的水平或垂直方式排列子元素。Defines an area within which you can arrange child elements either horizontally or vertically, relative to each other.
Grid 定義彈性的格線區域,由欄與列組成。Defines a flexible grid area that consists of columns and rows.
StackPanel 將子元素排成單一行,以水平或垂直方式排列。Arranges child elements into a single line that can be oriented horizontally or vertically.
VirtualizingPanel 提供 Panel 項目的架構,這些項目會虛擬化其子資料集合。Provides a framework for Panel elements that virtualize their child data collection. 這是 abstract 類別。This is an abstract class.
WrapPanel 將子項目置放於由左至右的連續位置,其中會在包含方塊的邊緣將內容換至下一行。Positions child elements in sequential position from left to right, breaking content to the next line at the edge of the containing box. 根據屬性的值而定,後續順序會依序從上到下或從右至左。 OrientationSubsequent ordering occurs sequentially from top to bottom or right to left, depending on the value of the Orientation property.

針對需要無法使用任何預先定義之元素進行配置的應用程式 Panel ,可以藉由繼承和覆 PanelMeasureOverride 和方法來達成自訂版面配置行為 ArrangeOverrideFor applications that require a layout that is not possible by using any of the predefined Panel elements, custom layout behaviors can be achieved by inheriting from Panel and overriding the MeasureOverride and ArrangeOverride methods.

版面配置效能考量Layout Performance Considerations

版面配置是遞迴程序。Layout is a recursive process. 集合中的每個子專案都是在 Children 版面配置系統的每個調用期間處理。Each child element in a Children collection gets processed during each invocation of the layout system. 因此,不需要時,應該避免觸發配置系統。As a result, triggering the layout system should be avoided when it is not necessary. 下列考量可協助您達到更佳的效能。The following considerations can help you achieve better performance.

  • 請注意哪些屬性值變更將會由配置系統強制遞迴更新。Be aware of which property value changes will force a recursive update by the layout system.

    使用公用旗標,可標記其值可以初始化配置系統的相依性屬性。Dependency properties whose values can cause the layout system to be initialized are marked with public flags. AffectsMeasureAffectsArrange 提供實用的線索,以供配置系統強制執行遞迴更新的屬性值變更。AffectsMeasure and AffectsArrange provide useful clues as to which property value changes will force a recursive update by the layout system. 一般而言,任何可能影響元素周框方塊大小的屬性,都應該 AffectsMeasure 將旗標設為 true。In general, any property that can affect the size of an element's bounding box should have a AffectsMeasure flag set to true. 如需詳細資訊,請參閱相依性屬性概觀For more information, see Dependency Properties Overview.

  • 可能的話,請使用 RenderTransform 而不是 LayoutTransformWhen possible, use a RenderTransform instead of a LayoutTransform.

    LayoutTransform可以是影響內容的非常實用方式 使用者介面 (UI)user interface (UI)A LayoutTransform can be a very useful way to affect the content of a 使用者介面 (UI)user interface (UI). 但是,如果轉換的效果不需要影響其他元素的位置,最好改用 RenderTransform ,因為不會叫用 RenderTransform 版面配置系統。However, if the effect of the transform does not have to impact the position of other elements, it is best to use a RenderTransform instead, because RenderTransform does not invoke the layout system. LayoutTransform 套用它的轉換,並強制執行遞迴版面配置更新,以考慮受影響專案的新位置。LayoutTransform applies its transformation and forces a recursive layout update to account for the new position of the affected element.

  • 避免不必要的呼叫 UpdateLayoutAvoid unnecessary calls to UpdateLayout.

    UpdateLayout方法會強制執行遞迴版面配置更新,而且通常不需要。The UpdateLayout method forces a recursive layout update, and is frequently not necessary. 除非您確定需要完整更新,否則請依賴配置系統來呼叫此方法。Unless you are sure that a full update is required, rely on the layout system to call this method for you.

  • 使用大型 Children 集合時,請考慮使用 VirtualizingStackPanel 而非一般 StackPanelWhen working with a large Children collection, consider using a VirtualizingStackPanel instead of a regular StackPanel.

    藉由虛擬化子集合, VirtualizingStackPanel 就只會將物件保留在目前位於父項的區內的記憶體中。By virtualizing the child collection, the VirtualizingStackPanel only keeps objects in memory that are currently within the parent's ViewPort. 因此,在大部分情況下,都可以大幅改善效能。As a result, performance is substantially improved in most scenarios.

子像素轉譯和版面配置進位Sub-pixel Rendering and Layout Rounding

WPF 圖形系統使用與裝置無關的單位來啟用解析度和裝置獨立性。The WPF graphics system uses device-independent units to enable resolution and device independence. 每個與裝置無關的圖元都會自動以系統的每英寸點比例調整 (DPI) 設定。Each device independent pixel automatically scales with the system's dots per inch (dpi) setting. 這可為 WPF 應用程式提供適用于不同 DPI 設定的適當調整,並讓應用程式自動感知 DPI。This provides WPF applications proper scaling for different dpi settings and makes the application automatically dpi-aware.

不過,這種 DPI 獨立性可能會因為消除鋸齒而建立不規則的邊緣轉譯。However, this dpi independence can create irregular edge rendering because of anti-aliasing. 如果邊緣位置落在裝置像素中間,而不是裝置像素之間,則這些成品一般會看起來模糊或具有半透明邊緣。These artifacts, typically seen as blurry or semi-transparent edges, can occur when the location of an edge falls in the middle of a device pixel instead of between device pixels. 配置系統提供一種方式,利用版面配置來進行這項的調整。The layout system provides a way to adjust for this with layout rounding. 版面配置進位是配置系統會在版面配置階段期間將任何非整數像素值四捨五入。Layout rounding is where the layout system rounds any non-integral pixel values during the layout pass.

預設會停用版面配置進位。Layout rounding is disabled by default. 若要啟用版面配置舍入,請將 UseLayoutRounding 屬性設定為 [ true 開啟] FrameworkElementTo enable layout rounding, set the UseLayoutRounding property to true on any FrameworkElement. 因為這是相依性屬性,所以值將會傳播到視覺樹狀結構中的所有子系。Because it is a dependency property, the value will propagate to all the children in the visual tree. 若要啟用整個 UI 的版面配置舍入,請 UseLayoutRounding true 在根容器上將設定為。To enable layout rounding for the entire UI, set UseLayoutRounding to true on the root container. 如需範例,請參閱 UseLayoutRoundingFor an example, see UseLayoutRounding.

後續步驟What's Next

了解如何測量和排列項目是了解版面配置的第一個步驟。Understanding how elements are measured and arranged is the first step in understanding layout. 如需可用元素的詳細資訊 Panel ,請參閱 面板總覽For more information about the available Panel elements, see Panels Overview. 若要進一步了解會影響版面配置的各種定位屬性,請參閱對齊、邊界和填補概觀To better understand the various positioning properties that can affect layout, see Alignment, Margins, and Padding Overview. 當您準備好將其全部放在輕量應用程式中時,請參閱 逐步解說:我的第一個 WPF 桌面應用程式When you are ready to put it all together in a lightweight application, see Walkthrough: My first WPF desktop application.

另請參閱See also