Avalonia系列文章之布局簡介
在UI設計中,頁面布局非常重要,良好的布局不僅可以有效的利用空間,還能提升交互體驗,以達到事半功倍的效果。所以對于Avalonia UI初學者來說,布局控件的了解與學習也非常的重要,今天以一些小例子,簡述Avalonia UI框架中布局控件的使用,僅供學習分享使用,如有不足之處,還請指正。

布局系統
布局系統描述了容器對元素集合的成員進行測量和排列的過程。布局是一個密集的過程,Children集合越大,進行的計算越多。每次對子控件更改位置,都可能觸發布局系統的新計算,所以不必要的調用可能會導致應用程序的性能下降。布局系統對子元素集合的每個成員完成兩個傳遞:測量傳遞和排列傳遞。每個Panel提供自己的MeasureOverride和ArrangeOverride方法,以實現其特定的布局行為。常用的布局面板有以下幾種:
- Panel 將所有子元素布局以填充 Panel 的邊界
- Canvas 定義一個區域,您可以在其中使用相對于 Canvas 區域的坐標明確定位子元素
- DockPanel 定義一個區域,其中您可以相對于彼此將子元素水平或垂直地排列
- Grid 定義由列和行組成的靈活網格區域
- RelativePanel 將子元素相對于其他元素或面板本身排列
- StackPanel 將子元素排列成一行,可以是水平或垂直方向
- WrapPanel 將子元素按從左到右的順序放置,當內容到達容器框的邊緣時,將在下一行中斷。后續排序順序是順序從上到下或從右到左,這取決于 Orientation 屬性的值。
注意:在 WPF 中,Panel 是一個抽象類,通常使用沒有行/列的 Grid 來布局多個控件以填充可用空間。在 Avalonia 中,Panel 是一個可用的控件,其布局行為與沒有行/列的 Grid 相同,但運行時占用更少的資源。
對齊與邊距
在Avalonia UI中,提供了四個常用屬性(HorizontalAlignment、Margin、Padding和VerticalAlignment),用于精確定位子元素。它們是控制元素在應用程序中位置的基礎。主要如下:
- HorizontalAlignment屬性聲明了要應用于子元素的水平對齊特性,常用的屬性值有四個:
-
- Left 子元素與父元素分配的布局空間的左側對齊。
- Center 子元素與父元素分配的布局空間的中心對齊。
- Right 子元素與父元素分配的布局空間的右側對齊。
- Stretch (默認) 子元素被拉伸以填充父元素分配的布局空間。明確的Width和Height值優先級更高。
- VerticalAlignment屬性聲明了要應用于子元素的垂直對齊特性,常用的屬性值有四個:
-
- Top 子元素與父元素分配的布局空間的頂部對齊。
- Center 子元素與父元素分配的布局空間的中心對齊。
- Bottom 子元素與父元素分配的布局空間的底部對齊。
- Stretch(默認) 子元素被拉伸以填充父元素分配的布局空間。明確的Width和Height值優先級更高。
- Margin屬性描述了元素與其子元素或同級元素之間的距離。Margin值可以是相同的,通過使用像Margin="20"的語法,元素將使用相同的20個設備獨立像素的Margin。Margin值也可以是四個不同的值,每個值描述了應用于左、上、右和下的不同邊距(按順序),如Margin="0,10,5,25"。在許多情況下,統一的邊距是不合適的。在這些情況下,可以應用非統一間距。正確使用Margin屬性可以非常精確地控制元素的渲染位置及其相鄰元素和子元素的渲染位置。
- Padding屬性描述了元素與其內容之間的距離,在大多數方面與Margin相似。Padding屬性僅暴露在少數類上,主要是為了方便。Border、TemplatedControl、和TextBlock是暴露了Padding屬性的類的范例。Padding屬性通過指定的Thickness值擴大了子元素的有效尺寸。
注意:HorizontalAlignment和VerticalAlignment屬性描述了一個子元素應該如何在父元素分配的布局空間內定位。元素上明確設置的Height和Width屬性的優先級高于Stretch屬性。如果明確設置了Height和Width,再將HorizontalAlignment設置為Stretch,這樣會忽略Stretch屬性。
對齊與邊距應用示例如下所示:
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="AvaloniaApplication2.MainWindow"
Title="AvaloniaApplication2">
<Border Background="LightBlue"
BorderBrush="Black"
BorderThickness="2"
Padding="15">
<StackPanel Background="White"
HorizontalAlignment="Center"
VerticalAlignment="Top">
<TextBlock Margin="5,0"
FontSize="18"
HorizontalAlignment="Center">
Alignment, Margin and Padding Sample
</TextBlock>
<Button HorizontalAlignment="Left" Margin="20">Button 1</Button>
<Button HorizontalAlignment="Right" Margin="10">Button 2</Button>
<Button HorizontalAlignment="Stretch">Button 3</Button>
</StackPanel>
</Border>
</Window>
示例效果如下圖所示:

Panel控件
Panel是Avalonia提供的布局支持的元素的基類,默認情況下,Panel和沒有行列個Grid相同,可以采用對齊或邊距(HorizontalAlignment、Margin、Padding和VerticalAlignment)進行定位,但相對于Grid等復雜布局容器,Panel運行時占用的資源較少。示例如下所示:
<Panel>
<TextBlock Background="LightGray" Text="Left" Width="100" Height="20" HorizontalAlignment="Left" />
<TextBlock Background="LightGray" Text="Right" Width="100" Height="20" HorizontalAlignment="Right"/>
<TextBlock Background="LightGray" Text="Top" Width="100" Height="20" VerticalAlignment="Top" />
<TextBlock Background="LightGray" Text="Bottom" Width="100" Height="20" VerticalAlignment="Bottom"/>
</Panel>
示例效果如下所示:

如果將Panel中的元素換成Button,將會有不同的效果,示例代碼如下:
<Panel>
<Button Background="LightGray" Content="Left" Width="100" Height="30" HorizontalAlignment="Left" />
<Button Background="LightGray" Content="Right" Width="100" Height="30" HorizontalAlignment="Right"/>
<Button Background="LightGray" Content="Top" Width="100" Height="30" VerticalAlignment="Top" />
<Button Background="LightGray" Content="Bottom" Width="100" Height="30" VerticalAlignment="Bottom"/>
</Panel>
示例效果如下所示:

思考一下:都是Panel布局,將TextBlock換成Button,為什么效果會不同呢?
Canvas
Canvas(畫布容器)允許按絕對坐標(x,y)定位子元素。子元素可以繪制在不同的位置,也可以繪制在同一個位置,如果兩個控件在Canvas容器中的位置相同,則依據出現的順序進行顯示。Canvas提供了最靈活的布局支持,Height和Width屬性定義畫布的大小,其中的元素被賦予相對于Canvas區域的絕對坐標。通過四個附加屬性 (Canvas.Left、Canvas.Top、Canvas.Right 和 Canvas.Bottom )來精確地控制對象在 Canvas 內的位置,從而使開發人員可以精確定位和排列元素在屏幕上的位置。
Canvas布局示例代碼,如下所示:
<Canvas>
<Button Background="Red" Content="Red" Width="100" Height="100" />
<Button Background="Green" Content="Green" Width="100" Height="100" Canvas.Left="100" Canvas.Top="100"/>
<Button Background="Blue" Content="Blue" Width="100" Height="100" Canvas.Left="200" Canvas.Top="200" />
<Button Background="Yellow" Content="Yellow" Width="100" Height="100" Canvas.Right="100" Canvas.Bottom="100"/>
</Canvas>
示例效果如下所示:

在Canvas中,如果沒有明確設置子元素在容器中的位置,則默認位于左上角,即Left,Top為0。另外,Canvas的默認行為是允許子元素繪制在父Canvas界限之外,如果不希望出現這種情況,可以設置ClipToBounds屬性為True,會自動裁剪界限之外的內容。
在上述示例中,Canvas.Left,Canvas.Top,Canvas.Bottom,Canvas.Right,作為Canvas的附加屬性,應用在Canvas容器中的子元素上,則前綴“Canvas.”不可以省略。如果省略,且子元素本身也有Left,Top等屬性,則會造成編譯器無法準確識別。
DockPanel
DockPanel(停靠面板)使用附加屬性DockPanel.Dock來設置子內容元素在容器邊緣的位置。當 DockPanel.Dock 設置為 Top 或 Bottom 時,它會將子元素放置在彼此的上方或下方。當 DockPanel.Dock 設置為 Left 或 Right 時,它會將子元素放置在彼此的左側或右側。LastChildFill 屬性決定了最后一個作為 DockPanel 子元素添加的元素的位置。
如果沒有指定DockPanel的Width和Height屬性,它的大小根據內容來確定。大小可以根據其子元素的大小進行增長或減小。然而,當指定了這些屬性并且沒有足夠的空間來容納下一個指定的子元素時,DockPanel 不會顯示該子元素或隨后的子元素,并且不會對隨后的子元素進行測量。
DockPanel示例源碼,如下所示:
<DockPanel LastChildFill="True">
<Border Height="25" Background="SkyBlue" BorderBrush="Black" BorderThickness="1" DockPanel.Dock="Top">
<TextBlock Foreground="Black">Dock = "Top"</TextBlock>
</Border>
<Border Height="25" Background="SkyBlue" BorderBrush="Black" BorderThickness="1" DockPanel.Dock="Top">
<TextBlock Foreground="Black">Dock = "Top"</TextBlock>
</Border>
<Border Height="25" Background="LemonChiffon" BorderBrush="Black" BorderThickness="1" DockPanel.Dock="Bottom">
<TextBlock Foreground="Black">Dock = "Bottom"</TextBlock>
</Border>
<Border Width="200" Background="PaleGreen" BorderBrush="Black" BorderThickness="1" DockPanel.Dock="Left">
<TextBlock Foreground="Black">Dock = "Left"</TextBlock>
</Border>
<Border Background="White" BorderBrush="Black" BorderThickness="1">
<TextBlock Foreground="Black">This content will "Fill" the remaining space</TextBlock>
</Border>
</DockPanel>
示例效果如下所示:

注意,默認情況下,DockPanel 元素的最后一個子元素將“填充”剩余的未分配空間。如果不希望出現這種情況,可以將 LastChildFill 屬性設置為 false。
Grid
Grid容器結合了絕對定位和表格數據控件的功能。Grid允許定義靈活的行和列分組,并且可以在多個Grid之間共享大小信息。在Grid控件中,通過ColumnDefinitions="Auto,*,*,*,*" RowDefinitions="Auto,Auto,*,Auto"實現定義行列,多行多列用逗號隔開,子元素通過Grid.Row,Grid.Column附加屬性進行定位。在XAML中,行列的大小可以通過幾種方式進行設置:
- 通過Star(*)進行設置,如Width="*"或Width="2*",則該行列會按比例獲得剩余可用空間。
- 通過Auto進行設置,如Width="Auto",則行或列會根據內容的大小進行分配空間。
- 絕對大小,默認單位為像素值,如Width="100",表示寬度為100px。
如下Grid代碼定義了4行5列,具體如下所示:
<Grid Background="Gainsboro" HorizontalAlignment="Left" VerticalAlignment="Top" Width="425" Height="165"
ColumnDefinitions="Auto,*,*,*,*" RowDefinitions="Auto,Auto,*,Auto">
<TextBlock Grid.Row="0" Grid.Column="1" Grid.ColumnSpan="4" Text="選擇打開的文件." TextWrapping="Wrap" />
<TextBlock Grid.Row="1" Grid.Column="0" Text="Open:" />
<TextBox Grid.Row="1" Grid.Column="1" Grid.ColumnSpan="5" />
<Button Grid.Row="3" Grid.Column="2" Content="OK" Margin="10,0,10,15" />
<Button Grid.Row="3" Grid.Column="3" Content="Cancel" Margin="10,0,10,15" />
<Button Grid.Row="3" Grid.Column="4" Content="Browse ..." Margin="10,0,10,15" />
</Grid>
示例效果如下所示:

StackPanel
StackPanel(棧面板)允許在指定的方向上堆疊元素,默認的堆疊方法是垂直的,如果想要水平方向排列,可以通過Orientation屬性進行設置,來控制子元素內容的排列方向。
StackPanel示例代碼如下所示:
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Top" Spacing="25">
<Button Content="Button 1" />
<Button Content="Button 2" />
<Button Content="Button 3" />
</StackPanel>
示例效果如下所示:

WrapPanel
WrapPanel用于按照從指定的順序定位子元素,并在其父容器的邊緣到達時將內容換行。WrapPanel和StackPanel一樣,可以通過Orientation屬性指定子元素排列的方向,默認為水平排列。
WrapPanel示例代碼如下所示:
<Border HorizontalAlignment="Left" VerticalAlignment="Top" BorderBrush="Black" BorderThickness="2">
<WrapPanel Background="LightBlue" Width="200" Height="100">
<Button Width="200">Button 1</Button>
<Button>Button 2</Button>
<Button>Button 3</Button>
<Button>Button 4</Button>
</WrapPanel>
</Border>
示例效果如下所示:

UniformGrid
UniformGrid提供統一的網格布局面板,此網格布局面板中的所有單元格大小相同,與Grid不同,此網格布局面板不支持顯示設置行,和列,也沒有Grid.Row,Grid.Column等附加屬性,它主要用于顯示大小相同的子元素控件。UniformGrid常用屬性如下:
- 行和列:UniformGrid 使用 Rows 和 Columns 屬性來確定其子元素的布局。如果只設置其中一個屬性,UniformGrid 將自動計算另一個屬性,以創建適合子元素總數的網格。如果兩個屬性都不設置,UniformGrid 默認為 1 行 1 列的網格。
- FirstColumn:FirstColumn 屬性允許您在網格的第一行中留下一定數量的空單元格。
UniformGrid示例如下所示:
<UniformGrid Rows="3" Columns="4">
<Rectangle Width="50" Height="50" Fill="#330000"/>
<Rectangle Width="50" Height="50" Fill="#660000"/>
<Rectangle Width="50" Height="50" Fill="#990000"/>
<Rectangle Width="50" Height="50" Fill="#CC0000"/>
<Rectangle Width="50" Height="50" Fill="#FF0000"/>
<Rectangle Width="50" Height="50" Fill="#FF3300"/>
<Rectangle Width="50" Height="50" Fill="#FF6600"/>
<Rectangle Width="50" Height="50" Fill="#FF9900"/>
<Rectangle Width="50" Height="50" Fill="#FFCC00"/>
<Rectangle Width="50" Height="50" Fill="#FFFF00"/>
<Rectangle Width="50" Height="50" Fill="#FFFF33"/>
<Rectangle Width="50" Height="50" Fill="#FFFF66"/>
</UniformGrid>
在上面的示例中,每個 Rectangle 被自動分配到網格中的一個單元格,按照它們被添加的順序進行分配。示例效果如下所示:

布局嵌套
在實際應用中,不同的Panel可以相互嵌套,以創建復雜的布局。雖然理論上可以無限嵌套面板元素,但嵌套越多,性能越低。所以熟練掌握各種布局容器的特點,才能根據業務需求,采用合理的布局面板,這樣不僅能提升性能,還會起到事半功倍的效果。
以上就是《Avalonia系列文章之布局簡介》的全部內容,希望可以拋磚引玉,一起學習,共同進步。
作者:老碼識途
出處:http://www.rzrgm.cn/hsiang/
本文版權歸作者和博客園共有,寫文不易,支持原創,歡迎轉載【點贊】,轉載請保留此段聲明,且在文章頁面明顯位置給出原文連接,謝謝。
關注個人公眾號,定時同步更新技術及職場文章

浙公網安備 33010602011771號