佈局和常用Panel學習

一、簡介

所有的WPF佈局容器都派生自System.Windows.Controls.Panel。Panel繼承自FrameworkElement。 在Panel中有一個比較重要的屬性是UIElementCollection 型別的Children屬性,UIElementCollection是一個有序的集合。我們可以使用繼承自Panel的類來重寫MeasureOverride(),ArrangeOverride()實現自定義面板。

常用的佈局容器:
Border不是佈局面板,但是經過幾個大佬的提醒,用好他真的很重要,所以我就放在第一個了。
StackPanel: 堆疊面板,水平或垂直放置元素。
WrapPanel:可換行的行中放置元素,在水平方向上從左向右放置元素,換行後也是從左向右。在垂直方向上,從上到下放置元素,在切換列後也是從上到下。
DockPanel: 根據容器的整個邊界調整元素。
Grid:在行列表格中排列元素。
UniformGrid:強制所有單元格具有相同尺寸。
Canvas:使用固定座標絕對定位元素。
ScrollViewer:通過新增滾動條可以使當前過長佈局內的內容縱向或者橫向滾動。再有限的區域內可以通過滾動呈現更多的內容。

二、程式碼案例

1.Border

Border不是佈局面板,但是經常與佈局類的面板一起配合使用,所以先介紹Border。Border的主要作用是給元素新增邊框,這個元素可以理解為一個佈局面板,一個控制元件等等。他包含設定邊框的2個屬性,BorderBrush用來設定顏色,BorderThickness用來設定寬度。CornerRadius設定圓角。而Padding和Margin一個設定邊框和裡面元素的間距,一個設定邊框和其他臨近元素的間距。而Background則是設定border所有內容的顏色。

案例XAML程式碼:

<Window x:Class="Wpf_Panel.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Wpf_Panel"
mc:Ignorable="d"
Title="MainWindow" Height="500" Width="800">
<StackPanel>
<!--BorderBrush用來設定顏色-->
<!--BorderThickness用來設定寬度-->
<!--CornerRadius設定圓角-->
<!--Padding設定邊框和裡面元素的間距-->
<!--Margin設定邊框和其他臨近元素的間距-->
<!--Background則是設定border所有內容的顏色-->
<Border Background="Bisque" BorderBrush="Coral" BorderThickness="3">
<Button Content="Button A" Width="120"/>
</Border>
<Border BorderBrush="Red" BorderThickness="3" Margin="5">
<Button Content="Button B"/>
</Border>
<Border BorderBrush="DarkRed" BorderThickness="3" Background="Red" Padding="5">
<Button Content="Button C"/>
</Border>
</StackPanel>
<!--<Grid> </Grid>-->
</Window>

執行效果:

2.StackPanel

StackPanel面板可以在單行或者單列以堆疊的形式排列子元素。預設情況下StackPanel面板按從上到下的順序排列元素。如果不指定寬度、則預設寬度和StackPanel面板寬度一致,如果StackPanel寬度發生變化,則按鈕也會拉伸以適應變化。而如果設定了寬度、就不會跟面板寬度變更發生變化。但是想要設計出自己想要的好看佈局,還需要更多的元素,先看一個基本的例子。

案例XAML程式碼:

<Window x:Class="Wpf_Panel.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Wpf_Panel"
mc:Ignorable="d"
Title="MainWindow" Height="500" Width="800">
<StackPanel x:Name="root_spanel" >
<Button Content="點我切換方向" Click="OrientationTranslator_Click"/>
<Button Content="點我新增元素到面板中" Click="AddElementToPanel_Click"/>
<Button x:Name="btn_FixedWidth" Content="點我手動設定寬度為120" Click="SetCurrentWidth_Click"/>
<Button Content="大家一定要努力學習C#!!!"/>
</StackPanel>
</Window>

後臺邏輯程式碼:

       private void OrientationTranslator_Click(object sender, RoutedEventArgs e)
{
root_spanel.Orientation = root_spanel.Orientation == Orientation.Horizontal ? Orientation.Vertical : Orientation.Horizontal;
} private void AddElementToPanel_Click(object sender, RoutedEventArgs e)
{
Button btn = new Button();
btn.Content = "我是新新增的Button";
root_spanel.Children.Add(btn);
} private void SetCurrentWidth_Click(object sender, RoutedEventArgs e)
{
btn_FixedWidth.Width = 120;
}

執行效果:

3.WrapPanel

WrapPanel面板可以一次排列一行或一列然後再換行繼續排列,和StackPanel一樣,可以通過設定Orientation屬性來設定當前是以水平還是垂直來排列子元素。因為是根據窗體變化演示佈局排列,這個只有XAML。

案例XAML程式碼:

<Window x:Class="Wpf_Panel.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Wpf_Panel"
mc:Ignorable="d"
Title="MainWindow" Height="500" Width="800">
<WrapPanel>
<Button Content="《老黃牛》" Width="120"/>
<Button Content="臧克家" Width="120"/>
<Button Content="塊塊荒田水和泥," Width="120"/>
<Button Content="深翻細作走東西。" Width="120"/>
<Button Content="老牛亦解韶光貴," Width="120"/>
<Button Content="不待揚鞭自奮蹄。" Width="120"/>
</WrapPanel>
</Window>

執行效果:

4.DockPanel

DockPanel面板與StackPanel面板類似,但是DockPanel可以通過設定Dock附加屬性設定子元素停靠的邊。Dock的值為Left、Right、Top、Bottom。通過設定子元素再DockPanel面板中的Dock屬性。可以修改子元素再DockPanel面板內的位置。可以通過LastChildFill設定為true來告訴DockPanel面板使最後一個元素佔滿剩餘控制元件。而設定的停靠順序會影響佈局結果。

案例XAML程式碼:

<Window x:Class="Wpf_Panel.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Wpf_Panel"
mc:Ignorable="d"
Title="MainWindow" Height="500" Width="900">
<DockPanel LastChildFill="True">
<Button DockPanel.Dock="Top" Content="今天學習了嗎?"/>
<Button DockPanel.Dock="Left" Content="今天寫程式碼了?"/>
<Button DockPanel.Dock="Right" Content="隨便放點東西"/>
<Button DockPanel.Dock="Right" VerticalAlignment="Center" Content="真的理解了嗎?要不要再多敲幾遍。"/>
<Button DockPanel.Dock="Bottom" Content="程式設計師不學習寫程式碼,還能幹什麼呢?"/>
<Button DockPanel.Dock="Bottom" Content="程式設計師不學習寫程式碼,還能幹什麼呢?"/> </DockPanel>
</Window>

執行效果:

5.Grid

Grid面板是把顯示內容分割到不可見的行列網格中。通過設定行列和對應的寬高佔比。來進行網格佈局。Grid佈局再平時使用的比較多。大部分都是用來做佈局的巢狀,設計外框各個部分的比例,然後在子元素中巢狀其他佈局控制元件。來實現區域的劃分。
在使用Grid面板時,需要用到一個叫做附加依賴項屬性的引數。在佈局相關的內容裡不會去講什麼是附加依賴項屬性,這個會在依賴項屬性中去講解,這裡只有瞭解就行。因為這個針對於Grid佈局來說是固定寫法。
我們新增一個三行三列的Grid面板。Grid.RowDefinitions和Grid.ColumnDefinitions裡面的內容是我們設定的Gird的行列數。各有3個,代表我們是一個三行三列的網格。我們沒有設定寬高。就會預設為是等分的。

案例XAML程式碼:

<Window x:Class="Wpf_Panel.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Wpf_Panel"
mc:Ignorable="d"
Title="MainWindow" Height="500" Width="900">
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<!--各列平分寬度-->
<ColumnDefinition/>
<ColumnDefinition />
<ColumnDefinition /> <!--要求左邊一列寬度固定,右邊一列以文字內容寬度適配,剩下的寬度區域都給中間的列。為了提高程式碼可讀性,不建議省略Width="*"雖然都是一樣的。-->
<!--<ColumnDefinition Width="140"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>-->
</Grid.ColumnDefinitions>
<!--網格佈局設計好之後。我們需要往裡面放置內容。我們要使用Grid.Column、Grid.Row這2個附加依賴項屬性來實現把Button放置到不同的網格中-->
<Button Grid.Column="0" Grid.Row="0" Content="Button Row 1 Column 1"/>
<Button Grid.Column="1" Grid.Row="0" Content="Button Row 1 Column 2"/>
<Button Grid.Column="2" Grid.Row="0" Content="Button Row 1 Column 3"/>
<Button Grid.Column="0" Grid.Row="1" Content="Button Row 2 Column 1"/>
<Button Grid.Column="1" Grid.Row="1" Content="Button Row 2 Column 2"/>
<Button Grid.Column="2" Grid.Row="1" Content="Button Row 2 Column 3"/>
<Button Grid.Column="0" Grid.Row="2" Content="Button Row 3 Column 1"/>
<Button Grid.Column="1" Grid.Row="2" Content="Button Row 3 Column 2"/>
<Button Grid.Column="2" Grid.Row="2" Content="Button Row 3 Column 3"/>
</Grid>
</Window>

執行效果:

我們嘗試執行程式碼。就會得到9個一樣大小的Button。我們嘗試拖動窗體大小。不管怎麼拖動,應該都是等量變化的。

6.UniformGrid

UniformGrid面板的特點是每個單元格始終保持一致的大小。通過設定行列數量來分割佈局。元素通過放入的前後順序被分配到不同的位置。這個再某些特定場景配合資料虛擬化和列表虛擬化使用的還是比較多的。

案例XAML程式碼:

<Window x:Class="Wpf_Panel.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Wpf_Panel"
mc:Ignorable="d"
Title="MainWindow" Height="500" Width="900">
<UniformGrid Columns="2" Rows="2">
<!--按照先來後到的順序,先行後列的放入到行列單元格-->
<Button Content="Button A"/>
<Button Content="Button B"/>
<Button Content="Button C"/>
<Button Content="Button D"/>
</UniformGrid>
</Window>

執行效果:

7.Canvas

Canvas是一個基於座標的佈局面板。他主要用於構建圖形工具的繪圖、Canvas知識再指定的位置放置子元素。並且子元素要提供精確的尺寸。再Canvas中需要設定Canvas.Left和Canvas.Top屬性。用來設定相對於原點的left和top。
也可以使用Canvas.Right和Canvas.Bottom。但是Canvas.left和Right不能同時使用,Canvas.Top和Canvas.Bottom也不能同時使用。使用Panel.ZIndex來設定子元素的層級關係。

案例XAML程式碼:

<Window x:Class="Wpf_Panel.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Wpf_Panel"
mc:Ignorable="d"
Title="MainWindow" Height="500" Width="900">
<Canvas>
<!--Panel.ZIndex="1"設定子元素的層級關係,哪個數字大,哪個在上面-->
<Button Content="Button A" Canvas.Left="255" Canvas.Top="70" Panel.ZIndex="2" Width="80px" Height="30px"/>
<Button Content="Button B" Canvas.Left="110" Canvas.Top="100" Width="80px" Height="30px"/>
<Button Content="Button C" Canvas.Left="295" Canvas.Top="81" Panel.ZIndex="1" Width="80px" Height="30px"/>
</Canvas>
</Window>

執行效果:

Button A和Button C的重疊關係使用Panel.ZIndex來設定。

8.ScrollViewer

如果要再一個比較小的區域內顯示特別多的內容,就需要使用ScrollViewer來進行橫向或縱向滾動了,但是再實際使用過程中往往配合資料虛擬化和列表虛擬化來實現支援更多內容的滾動效果。不然如果內容一旦特別多,ScrollViewer下的內容又特別長,每次滾動都會觸發佈局的重新測量和排列。如果不使用虛擬化,會全部重新計算所有的佈局元素,會特別卡,導致使用困難。

案例XAML程式碼:

<Window x:Class="Wpf_Panel.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Wpf_Panel"
mc:Ignorable="d"
Title="MainWindow" Height="500" Width="900">
<Grid>
<StackPanel>
<ScrollViewer Name="scroll" Width="480" Height="350" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Visible" >
<TextBlock Name="txtShowArticle" Foreground="Gray" Margin="20,10" Loaded="txtShowArticle_Loaded" />
</ScrollViewer>
</StackPanel>
</Grid>
</Window>

後臺邏輯程式碼:

  string content = @"中國青年網6月22日電 據“健康廣東”微信公眾號訊息,6月21日0-24時,全省新增2例本土確診病例,深圳報告1例,東莞報告1例。
全省新增境外輸入確診病例5例,廣州報告2例,分別來自法國和阿曼;深圳報告1例,來自印度尼西亞;珠海報告1例,來自孟加拉國;東莞報告1例,來自阿聯酋。新增境外輸入無症狀感染者7例,廣州報告3例,2例來自柬埔寨,1例來自阿聯酋;佛山報告1例,來自柬埔寨;中山報告1例,來自加彭;肇慶報告2例,均來自印度尼西亞。新增出院16例。
截至6月21日24時,全省累計報告新冠肺炎確診病例2706例(境外輸入1140例)。目前在院221例。
(來源:中國青年網)";
private void ShowArticle()
{
//獲取私信資訊
StringBuilder strMessage = new StringBuilder();
strMessage.Append("標題:" + "廣東昨日新增2例本土確診病例,深圳、東莞各1例" + "\r\n");
strMessage.Append("來源:" + "中國青年網" + "\r\n");
strMessage.Append("傳送時間:" + "2021-06-22 15:31:32" + "\r\n");
strMessage.Append("傳送內容:" + content + "\r\n\n");
txtShowArticle.Text = strMessage.ToString();
} private void txtShowArticle_Loaded(object sender, RoutedEventArgs e)
{
ShowArticle();
}

執行效果: