布局原則
1、不顯式設置元素大小。
2、不使用絕對定位。
元素應該根據容器的內容來進行排列。絕對定位在開發前期會帶來一些便捷,但擴展性比較差。一旦顯示器尺寸或分辨率發生改變,界面的顯示效果可能會達不到預期的效果。
3、布局容器可以嵌套使用
常用布局容器
WPF中的布局控件繼承自System.Windows.Controls.Panel抽象類。
常用的布局容器如下:
Grid | 網格容器。可以創建不可見的行和列,然后通過設置行、列來進行元素排列。這是最靈活也是最常用的容器之一。 |
StackPanel | 堆棧式面板。 在水平或垂直的堆棧中放置元素。當移除一個元素后,后面的元素會自動向前移動以填充空缺 |
WrapPanel | 自動換行面板 |
DockPanel | 停靠式面板。這個面板可以設置控件停靠的方式,類似Winform中的Dock屬性 |
Canvas | 畫布。Canvas使用絕對定位來控制元素的位置。動畫時會經常用到這個容器 |
Grid
Grid容器是WPF中最常用的布局容器。創建一個WPF工程后,系統會自動添加Grid標簽作為頂級容器。
Grid容器通過定義行和列來設置控件的位置
1 <!--ShowGridLines屬性可以開啟行列顯示--> 2 <Grid ShowGridLines="True"> 3 <!--添加行列--> 4 <Grid.RowDefinitions> 5 <RowDefinition/> 6 <RowDefinition/> 7 </Grid.RowDefinitions> 8 9 <Grid.ColumnDefinitions> 10 <ColumnDefinition/> 11 <ColumnDefinition/> 12 </Grid.ColumnDefinitions> 13 14 <Label Content="Row=0,Column=0"/> 15 <Label Content="Row=1,Column=1"/> 16 </Grid>
說明:
如果沒有指定行和列,元素會默認放置在Grid的0行0列。
在Grid容器中,行和列的尺寸支持三種模式:
1、指定尺寸
1 <!--指定尺寸--> 2 <Grid Grid.Column="1" ShowGridLines="True"> 3 <Grid.ColumnDefinitions> 4 <ColumnDefinition Width="120"/> 5 <ColumnDefinition/> 6 </Grid.ColumnDefinitions> 7 8 <Label Content="寬度120"/> 9 <Label Content="寬度為Grid容器大小 - 120" Grid.Column="1"/> 10 </Grid>
這種模式可以指定行和列的高度或寬度。
說明:
1、WPF中默認寬高單位是像素(Pixel),完整的寫法應該是Width="120px",但是這個px可以省略。
2、WPF還支持英寸(in)、厘米(cm)、點(pt)等單位,這里不做詳細介紹。可參閱推薦閱讀。
3、這種模式不建議使用,因為這種方式是使用設備無關單位准確的設置尺寸。
2、指定比例
1 <Grid Grid.Column="2" ShowGridLines="True"> 3 <Grid.RowDefinitions> 4 <RowDefinition Height="*"/> 5 <RowDefinition Height="3*"/> 6 </Grid.RowDefinitions> 7 8 <!--不指定比例,會等比拆分行列--> 9 <Grid.ColumnDefinitions> 10 <ColumnDefinition/> 11 <ColumnDefinition/> 12 </Grid.ColumnDefinitions> 13 </Grid>
可通過(*)來指定行列所占比例。未使用(*)指定比例的,按等比划分。
上面的示例代碼中,定義了兩行,兩列。
第一行占Grid容器高度的1/4,第二行占Grid容器高度的3/4。
在定義列的時候,未指定比例,所以兩列各占Grid容器寬度的1/2
3、自動設置
自動設置就是在指定寬高時使用Auto,使用了Auto的行列寬高是根據內部所放置元素來決定。
1 <Grid ShowGridLines="True"> 2 <Grid.RowDefinitions> 3 <RowDefinition/> 4 <RowDefinition/> 5 <RowDefinition Height="Auto"/> 6 </Grid.RowDefinitions> 7 8 <Label Content="高度 50" Grid.Row="2"/> 9 </Grid>
上面的示例代碼中,在Grid中定義了三行。
第三行的高度使用了Auto。
如果不在第三行放置任何元素,第一行和第二行會各占Grid元素的1/2,即 Grid.Height /2
當在第三行放置一個高度為50的元素后,第三行有了50的高度。
這個時候,前面兩行的高度就會發生變化,變成 (Grid.Height - 50 ) / 2
說明:
1、指定為Auto時,可以通過MinHeight來指定最小高度,MinHeight來指定最小寬度。
2、在布局時,通常會混合使用以上各種模式。
3、可以使用 Grid.UseLayoutRounding="True"來啟用抗鋸齒功能。啟用后,會將容器內所有內容對齊到最近的像素邊界。
跨越行和列
在定義行和列以后,有時候會需要跨越行來列來進行布局。這個時候可以使用Grid.RowSpan和Grid.ColumnSpan這兩個附加屬性來進行設置。
1 <Grid> 2 <Grid.RowDefinitions> 3 <RowDefinition/> 4 <RowDefinition/> 5 </Grid.RowDefinitions> 6 7 <Grid.ColumnDefinitions> 8 <ColumnDefinition/> 9 <ColumnDefinition/> 10 </Grid.ColumnDefinitions> 11 12 <Label Name="label1" Content="不跨越列" Grid.Row="0" Grid.Column="0" HorizontalAlignment="Center"/> 13 <Label Name="label2" Content="跨越兩列" Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2" HorizontalAlignment="Center"/> 14 15 <Label Name="label3" Content="不跨越行" Grid.Row="0" Grid.Column="0" VerticalAlignment="Center"/> 16 <Label Name="label4" Content="跨越兩行" Grid.Row="0" Grid.Column="0" Grid.RowSpan="2" VerticalAlignment="Center"/> 17 </Grid>
說明:
Grid布局技巧:
1、根據需要動態變化的內容,設置行列寬高來進行布局。
2、針對長度或寬度不固定的區域,可以設置寬度為Auto
3、WPF提供了一個GridSplitter類,可以動態的調整Grid的行高和列寬
1 <Grid> 2 <Grid.RowDefinitions> 3 <RowDefinition/> 4 <RowDefinition Height="auto"/> 5 <RowDefinition/> 6 </Grid.RowDefinitions> 7 8 <Grid.ColumnDefinitions> 9 <ColumnDefinition/> 10 <ColumnDefinition Width="auto"/> 11 <ColumnDefinition/> 12 </Grid.ColumnDefinitions> 13 14 <Grid Grid.Row="0" Grid.Column="0" Background="Green"></Grid> 15 <Grid Grid.Row="0" Grid.Column="2" Background="Silver"></Grid> 16 <Grid Grid.Row="2" Grid.Column="0" Background="Pink"></Grid> 17 <Grid Grid.Row="2" Grid.Column="2" Background="LightSkyBlue"></Grid> 18 19 <GridSplitter HorizontalAlignment="Stretch" VerticalAlignment="Center" Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="3" Height="2"></GridSplitter> 20 <GridSplitter VerticalAlignment="Stretch" HorizontalAlignment="Center" Grid.Column="1" Grid.Row="0" Grid.RowSpan="3" Width="2"></GridSplitter> 21 </Grid>
StackPanel
StackPanel容器的使用比較簡單,它可以在單行或單列中以堆棧形式放置元素。
使用方法如下:
1 <!--垂直堆放元素(默認)--> 2 <StackPanel Grid.Column="0"> 3 <Button Content="Button1" Margin="10"/> 4 <Button Content="Button2" HorizontalAlignment="Left"/> 5 <Button Content="Button3" HorizontalAlignment="Right"/> 6 </StackPanel>
1 <!--水平堆放元素 --> 2 <!--需要指定屬性 Orientation="Horizontal"--> 3 <StackPanel Grid.Column="1" Orientation="Horizontal"> 4 <Button Content="Button1" Margin="10"/> 5 <Button Content="Button2" VerticalAlignment="Top"/> 6 <Button Content="Button3" VerticalAlignment="Bottom"/> 7 </StackPanel>
通過FlowDirection屬性可以指定元素的浮動方向
1 <!--FlowDirection可以指定元素的浮動方向--> 2 <!--LeftToRight是默認值,表示元素從左向右浮動--> 3 <StackPanel FlowDirection="LeftToRight"> 4 <Button Content="Button1" Width="120" Height="28" HorizontalAlignment="Left"/> 5 </StackPanel> 6 7 <!--RightToLeft表示元素從右向左浮動--> 8 <StackPanel FlowDirection="RightToLeft" Grid.Row="1"> 9 <Button Content="Button1" Width="120" Height="28" HorizontalAlignment="Left"/> 10 </StackPanel>
WrapPanel
WrapPanel和StackPanel差不多,它采用了流式 布局,控件也是一次一行或一列的方式布置。但它與StackPanel又有區別,WrapPanel的 控件從左向右或從上往下進行排列,排列滿了以后再在下一行或下一列排列。
WrapPanel可用於工具欄類似的場景,因為控件可以依次排列下去。
1 <!--默認水平排列--> 2 <WrapPanel Grid.Row="0" Grid.Column="0"> 3 <Button Content="abc"/> 4 <Button Content="abc"/> 5 <Button Content="abc"/> 6 <Button Content="abc"/> 7 <Button Content="abc"/> 8 <Button Content="abc"/> 9 <!--當水平方向排列不下時,自動排列到下一行--> 10 <Button Content="abc"/> 11 </WrapPanel>
1 <!--設置為垂直排列--> 2 <WrapPanel Grid.Row="0" Grid.Column="1" Orientation="Vertical"> 3 <Button Content="abc"/> 4 <Button Content="abc"/> 5 <Button Content="abc"/> 6 <!--當垂直方向排列不下時,自動排列到下一列--> 7 <Button Content="abc"/> 8 </WrapPanel>
DockPanel
DockPanel是一種停靠式布局容器。通過附加屬性DockPanel.Dock設置元素的停靠方向。
當元素被指定為停靠在頂部時,元素會占據布局容器的整個寬度,高度會根據內容和MinHeight屬性而定。當元素被停靠在左邊時,元素會占據整個布局容器的高度,而寬度會根據內容和MinWidth屬性而定。
1 <DockPanel Grid.Row="0" Margin="10"> 2 <Button Content="左停靠" DockPanel.Dock="Left" MinWidth="80"/> 3 <Button Content="右停靠" DockPanel.Dock="Right"/> 4 <Button Content="上停靠" DockPanel.Dock="Top"/> 5 <Button Content="下停靠" DockPanel.Dock="Bottom"/> 6 </DockPanel>
默認情況下DockPanel.LastChildFill屬性值為true,該屬性會設置最后一個元素否占滿整個布局容器。如果設置LastChildFill為false,可以看到
使用DockPanel容器進行布局時,元素的先后順序影響會很大。在上面的示例中,最先放置向左停靠的元素,所以【上停靠】這個按鈕會占據整個面板的高度,其它的元素會放置在它的右邊。
如果最先放置向上停靠的元素,那這個向上停靠的元素會占據整個面板的寬度,其它的元素會放置在它的下面
1 <!--可以看到元素的先后順序對布局會產生很大的影響--> 2 <DockPanel Grid.Row="2" Margin="10"> 3 <Button Content="左停靠" DockPanel.Dock="Top" MinWidth="80"/> 4 <Button Content="右停靠" DockPanel.Dock="Right"/> 5 <Button Content="上停靠" DockPanel.Dock="Left"/> 6 <Button Content="下停靠" DockPanel.Dock="Bottom"/> 7 </DockPanel>
重復上面的過程,可以更清楚的看到元素的先后對布局的影響以及DockPanel元素排列的規則
1 <DockPanel Grid.Row="3" Margin="10"> 2 <Button Content="左停靠" DockPanel.Dock="Left" MinWidth="80"/> 3 <Button Content="右停靠" DockPanel.Dock="Right"/> 4 <Button Content="上停靠" DockPanel.Dock="Top"/> 5 <Button Content="下停靠" DockPanel.Dock="Bottom"/> 6 <Button Content="上停靠" DockPanel.Dock="Top" MinWidth="80"/> 7 <Button Content="右停靠" DockPanel.Dock="Right"/> 8 <Button Content="左停靠" DockPanel.Dock="Left"/> 9 <Button Content="下停靠" DockPanel.Dock="Bottom"/> 10 </DockPanel>
Canvas
Canvas面板,畫布面板,它可以通過使用精確的坐標來放置元素。大多數情況下,我們不需要絕對定位來放置元素,因為顯示器的大小和分辨率有可能會改變。但是如果需要設計繪圖相關的界面,或需要對元素進行動畫時,就要用到Canvas面板。
Canvas面板是最輕量級的布局容器,因為它不包含任何復雜的布局邏輯。
通過Canvas.Left附加屬性,設置元素左邊和Canvas面板左邊的單位數。不指定單位時,默認是px。通過Canvas.Top附加屬性,設置元素上邊和Canvas面板上邊的單位數。
也可以設置Canvas.Bottom和Canvas.Right附加屬性來指定距離面板底部和右邊的單位數(設置無關單位,當將系統DPI指定為96dpi時,設置無關單位恰好等於通常使用的像素)。
說明:
1、不能同時指定Canvas.Left和Canvas.Right,只能選擇一種指定。
2、不能同時指定Canvas.Top和Canvas.Bottom,只能選擇一種指定。
在Canvas面板中,需要指定元素的大小,即指定Width和Height屬性的值 。如果未指定元素寬高,元素的大小就是剛好能展示其全部內容的大小
1 <Canvas> 2 <!--基本使用--> 3 <!--未指定寬高--> 4 <Button Content="Canvas" Canvas.Left="100" Canvas.Top="200"/> 5 <Button Content="Canvas" Canvas.Left="200px" Canvas.Top="200px"/> 6 <Button Content="Canvas" Canvas.Left="2in" Canvas.Top="2.5in"/> 7 <Button Content="Canvas" Canvas.Right="100" Canvas.Bottom="200"/> 8 9 <!--指定寬高 --> 10 <Button Content="Canvas" Canvas.Left="50" Canvas.Top="100" Width="150" Height="28"/> 11 </Canvas>
說明:
當Canvas面板的大小改變時,面板內放置的元素大小不會改變。
元素疊放的順序Z-Order
如果Canvas面板中有重疊的元素,可以通過設置Canvas.ZIndex附加屬性來控制層疊方式。
默認每個元素會有默認的ZIndex值0,具有更高ZIndex值的元素會始終顯示在較低ZIndex值元素的上面。ZIndex值可以為負數
1 <!--指定疊放順序--> 2 <Button Content="ZIndex=0" Canvas.Left="300" Canvas.Top="190"/> 3 <Button Content="ZIndex=1" Canvas.Left="310" Canvas.Top="200"/> 4 <Button Content="ZIndex=2" Canvas.Left="290" Canvas.Top="200"/> 5 6 <!--ZIndex值都為0的情況下,后放置的元素會顯示在上面--> 7 <Button Content="ZIndex=0-1" Canvas.Left="380" Canvas.Top="190"/> 8 <Button Content="ZIndex=0-2" Canvas.Left="380" Canvas.Top="190"/>
推薦閱讀
寬高支持的單位
WPF控件庫示例
https://github.com/Microsoft/WPF-Samples/tree/master/Getting%20Started/ControlsAndLayout
本文示例代碼:
https://github.com/zhaotianff/DotNetCoreWPF/tree/master/五、WPF中的布局容器