在UI設(shè)計(jì)中,頁(yè)面布局非常重要,良好的布局不僅可以有效地利用空間,還能提升交互體驗(yàn),以達(dá)到事半功倍的效果。所以對(duì)于Avalonia UI初學(xué)者來說,布局控件的了解與學(xué)習(xí)也非常的重要,今天以一些小例子,簡(jiǎn)述Avalonia UI框架中布局控件的使用,僅供學(xué)習(xí)分享使用,如有不足之處,還請(qǐng)指正。 布局系統(tǒng)布局系統(tǒng)描述了容器對(duì)元素集合的成員進(jìn)行測(cè)量和排列的過程。布局是一個(gè)密集的過程,Children集合越大,進(jìn)行的計(jì)算越多。每次對(duì)子控件更改位置,都可能觸發(fā)布局系統(tǒng)的新計(jì)算,所以不必要的調(diào)用可能會(huì)導(dǎo)致應(yīng)用程序的性能下降。布局系統(tǒng)對(duì)子元素集合的每個(gè)成員完成兩個(gè)傳遞:測(cè)量傳遞和排列傳遞。每個(gè)Panel提供自己的MeasureOverride和ArrangeOverride方法,以實(shí)現(xiàn)其特定的布局行為。常用的布局面板有以下幾種:- Panel 將所有子元素布局以填充 Panel 的邊界
- Canvas 定義一個(gè)區(qū)域,您可以在其中使用相對(duì)于 Canvas 區(qū)域的坐標(biāo)明確定位子元素
- DockPanel 定義一個(gè)區(qū)域,其中您可以相對(duì)于彼此將子元素水平或垂直地排列
- Grid 定義由列和行組成的靈活網(wǎng)格區(qū)域
- RelativePanel 將子元素相對(duì)于其他元素或面板本身排列
- StackPanel 將子元素排列成一行,可以是水平或垂直方向
- WrapPanel 將子元素按從左到右的順序放置,當(dāng)內(nèi)容到達(dá)容器框的邊緣時(shí),將在下一行中斷。后續(xù)排序順序是順序從上到下或從右到左,這取決于 Orientation 屬性的值。
注意:在 WPF 中,Panel 是一個(gè)抽象類,通常使用沒有行/列的 Grid 來布局多個(gè)控件以填充可用空間。在 Avalonia 中,Panel 是一個(gè)可用的控件,其布局行為與沒有行/列的 Grid 相同,但運(yùn)行時(shí)占用更少的資源。對(duì)齊與邊距在Avalonia UI中,提供了四個(gè)常用屬性(HorizontalAlignment、Margin、Padding和VerticalAlignment),用于精確定位子元素。它們是控制元素在應(yīng)用程序中位置的基礎(chǔ)。主要如下:- HorizontalAlignment屬性聲明了要應(yīng)用于子元素的水平對(duì)齊特性,常用的屬性值有四個(gè):
- Left 子元素與父元素分配的布局空間的左側(cè)對(duì)齊。
- Center 子元素與父元素分配的布局空間的中心對(duì)齊。
- Right 子元素與父元素分配的布局空間的右側(cè)對(duì)齊。
- Stretch (默認(rèn)) 子元素被拉伸以填充父元素分配的布局空間。明確的Width和Height值優(yōu)先級(jí)更高。
- VerticalAlignment屬性聲明了要應(yīng)用于子元素的垂直對(duì)齊特性,常用的屬性值有四個(gè):
- Top 子元素與父元素分配的布局空間的頂部對(duì)齊。
- Center 子元素與父元素分配的布局空間的中心對(duì)齊。
- Bottom 子元素與父元素分配的布局空間的底部對(duì)齊。
- Stretch(默認(rèn)) 子元素被拉伸以填充父元素分配的布局空間。明確的Width和Height值優(yōu)先級(jí)更高。
- Margin屬性描述了元素與其子元素或同級(jí)元素之間的距離。Margin值可以是相同的,通過使用像Margin="20"的語(yǔ)法,元素將使用相同的20個(gè)設(shè)備獨(dú)立像素的Margin。Margin值也可以是四個(gè)不同的值,每個(gè)值描述了應(yīng)用于左、上、右和下的不同邊距(按順序),如Margin="0,10,5,25"。在許多情況下,統(tǒng)一的邊距是不合適的。在這些情況下,可以應(yīng)用非統(tǒng)一間距。正確使用Margin屬性可以非常精確地控制元素的渲染位置及其相鄰元素和子元素的渲染位置。
- Padding屬性描述了元素與其內(nèi)容之間的距離,在大多數(shù)方面與Margin相似。Padding屬性僅暴露在少數(shù)類上,主要是為了方便。Border、TemplatedControl、和TextBlock是暴露了Padding屬性的類的范例。Padding屬性通過指定的Thickness值擴(kuò)大了子元素的有效尺寸。
注意:HorizontalAlignment和VerticalAlignment屬性描述了一個(gè)子元素應(yīng)該如何在父元素分配的布局空間內(nèi)定位。元素上明確設(shè)置的Height和Width屬性的優(yōu)先級(jí)高于Stretch屬性。如果明確設(shè)置了Height和Width,再將HorizontalAlignment設(shè)置為Stretch,這樣會(huì)忽略Stretch屬性。對(duì)齊與邊距應(yīng)用示例如下所示:<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>
PanelPanel是Avalonia提供的布局支持的元素的基類,默認(rèn)情況下,Panel和沒有行列的Grid相同,可以采用對(duì)齊或邊距(HorizontalAlignment、Margin、Padding和VerticalAlignment)進(jìn)行定位,但相對(duì)于Grid等復(fù)雜布局容器,Panel運(yùn)行時(shí)占用的資源較少。示例如下所示:<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中的元素?fù)Q成Button,將會(huì)有不同的效果,示例代碼如下:<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,為什么效果會(huì)不同呢?CanvasCanvas(畫布容器)允許按絕對(duì)坐標(biāo)(x,y)定位子元素。子元素可以繪制在不同的位置,也可以繪制在同一個(gè)位置,如果兩個(gè)控件在Canvas容器中的位置相同,則依據(jù)出現(xiàn)的順序進(jìn)行顯示。Canvas提供了最靈活的布局支持,Height和Width屬性定義畫布的大小,其中的元素被賦予相對(duì)于Canvas區(qū)域的絕對(duì)坐標(biāo)。通過四個(gè)附加屬性 (Canvas.Left、Canvas.Top、Canvas.Right 和 Canvas.Bottom )來精確地控制對(duì)象在 Canvas 內(nèi)的位置,從而使開發(fā)人員可以精確定位和排列元素在屏幕上的位置。<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中,如果沒有明確設(shè)置子元素在容器中的位置,則默認(rèn)位于左上角,即Left,Top為0。另外,Canvas的默認(rèn)行為是允許子元素繪制在父Canvas界限之外,如果不希望出現(xiàn)這種情況,可以設(shè)置ClipToBounds屬性為True,會(huì)自動(dòng)裁剪界限之外的內(nèi)容。在上述示例中,Canvas.Left,Canvas.Top,Canvas.Bottom,Canvas.Right,作為Canvas的附加屬性,應(yīng)用在Canvas容器中的子元素上,則前綴“Canvas.”不可以省略。如果省略,且子元素本身也有Left,Top等屬性,則會(huì)造成編譯器無法準(zhǔn)確識(shí)別。DockPanelDockPanel(??棵姘澹┦褂酶郊訉傩訢ockPanel.Dock來設(shè)置子內(nèi)容元素在容器邊緣的位置。當(dāng) DockPanel.Dock 設(shè)置為 Top 或 Bottom 時(shí),它會(huì)將子元素放置在彼此的上方或下方。當(dāng) DockPanel.Dock 設(shè)置為 Left 或 Right 時(shí),它會(huì)將子元素放置在彼此的左側(cè)或右側(cè)。LastChildFill 屬性決定了最后一個(gè)作為 DockPanel 子元素添加的元素的位置。如果沒有指定DockPanel的Width和Height屬性,它的大小根據(jù)內(nèi)容來確定。大小可以根據(jù)其子元素的大小進(jìn)行增長(zhǎng)或減小。然而,當(dāng)指定了這些屬性并且沒有足夠的空間來容納下一個(gè)指定的子元素時(shí),DockPanel 不會(huì)顯示該子元素或隨后的子元素,并且不會(huì)對(duì)隨后的子元素進(jìn)行測(cè)量。<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> 注意,默認(rèn)情況下,DockPanel 元素的最后一個(gè)子元素將“填充”剩余的未分配空間。如果不希望出現(xiàn)這種情況,可以將 LastChildFill 屬性設(shè)置為 false。GridGrid容器結(jié)合了絕對(duì)定位和表格數(shù)據(jù)控件的功能。Grid允許定義靈活的行和列分組,并且可以在多個(gè)Grid之間共享大小信息。在Grid控件中,通過ColumnDefinitions="Auto,*,*,*,*" RowDefinitions="Auto,Auto,*,Auto"實(shí)現(xiàn)定義行列,多行多列用逗號(hào)隔開,子元素通過Grid.Row,Grid.Column附加屬性進(jìn)行定位。在XAML中,行列的大小可以通過幾種方式進(jìn)行設(shè)置:- 通過Star(*)進(jìn)行設(shè)置,如Width="*"或Width="2*",則該行列會(huì)按比例獲得剩余可用空間。
- 通過Auto進(jìn)行設(shè)置,如Width="Auto",則行或列會(huì)根據(jù)內(nèi)容的大小進(jìn)行分配空間。
- 絕對(duì)大小,默認(rèn)單位為像素值,如Width="100",表示寬度為100px。
<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>
StackPanelStackPanel(棧面板)允許在指定的方向上堆疊元素,默認(rèn)的堆疊方法是垂直的,如果想要水平方向排列,可以通過Orientation屬性進(jìn)行設(shè)置,來控制子元素內(nèi)容的排列方向。<StackPanel HorizontalAlignment="Center" VerticalAlignment="Top" Spacing="25"> <Button Content="Button 1" /> <Button Content="Button 2" /> <Button Content="Button 3" /> </StackPanel> WrapPanel用于按照從指定的順序定位子元素,并在其父容器的邊緣到達(dá)時(shí)將內(nèi)容換行。WrapPanel和StackPanel一樣,可以通過Orientation屬性指定子元素排列的方向,默認(rèn)為水平排列。<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提供統(tǒng)一的網(wǎng)格布局面板,此網(wǎng)格布局面板中的所有單元格大小相同,與Grid不同,此網(wǎng)格布局面板不支持顯示設(shè)置行,和列,也沒有Grid.Row,Grid.Column等附加屬性,它主要用于顯示大小相同的子元素控件。UniformGrid常用屬性如下:- 行和列:UniformGrid 使用 Rows 和 Columns 屬性來確定其子元素的布局。如果只設(shè)置其中一個(gè)屬性,UniformGrid 將自動(dòng)計(jì)算另一個(gè)屬性,以創(chuàng)建適合子元素總數(shù)的網(wǎng)格。如果兩個(gè)屬性都不設(shè)置,UniformGrid 默認(rèn)為 1 行 1 列的網(wǎng)格。
- FirstColumn:FirstColumn 屬性允許您在網(wǎng)格的第一行中留下一定數(shù)量的空單元格。
<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> 在上面的示例中,每個(gè) Rectangle 被自動(dòng)分配到網(wǎng)格中的一個(gè)單元格,按照它們被添加的順序進(jìn)行分配。示例效果如下所示:在實(shí)際應(yīng)用中,不同的Panel可以相互嵌套,以創(chuàng)建復(fù)雜的布局。雖然理論上可以無限嵌套面板元素,但嵌套越多,性能越低。所以熟練掌握各種布局容器的特點(diǎn),才能根據(jù)業(yè)務(wù)需求,采用合理的布局面板,這樣不僅能提升性能,還會(huì)起到事半功倍的效果。本文主要參考官方文檔: https://docs./zh-Hans/docs/basics/user-interface/building-layouts/ 以上就是《Avalonia系列文章之布局簡(jiǎn)介》的全部?jī)?nèi)容,關(guān)于更多詳細(xì)內(nèi)容,可參考官方文檔。希望能夠一起學(xué)習(xí),共同進(jìn)步。學(xué)習(xí)編程,從關(guān)注【老碼識(shí)途】開始,為大家分享更多文章!??!
|