WPF编程基础入门 ——— 第三章 布局(七)布局面板Grid
WPF布局——布局面板GridWPF——Grid布局控件Grid简单实例——按钮排列Grid简单实例——登录界面WPF——Grid布局控件尽管上一篇讲解的UniformGrid能够布局统一单元格,但是很多布局中需要构建单元格大小不等,具有跨越式单元格(Span Cells) 和空白列等功能。而Grid能实现上述这些功能,它是一个使用灵活、能构建复杂UI的布局控件。Grid最简单的用法是通过设置Ro
WPF布局——布局面板Grid
WPF——Grid布局控件
尽管上一篇讲解的UniformGrid能够布局统一单元格,但是很多布局中需要构建单元格大小不等,具有跨越式单元格(Span Cells) 和空白列等功能。而Grid能实现上述这些功能,它是一个使用灵活、能构建复杂UI的布局控件。
Grid最简单的用法是通过设置RowDefinitions
和ColumnDefinitions
属性定义单元格的总数。其中,RowDefinitions
指行数,ColumnDefinitions
指列数。添加子元素时,使用Grid.Row
和Grid.Column
附加属性设定子元素在单元格中的位置。
Grid简单实例——按钮排列
此例设计要求:现在使用Grid布局控件来布局6个Button,单元格排列3行2列。
说了好多实例了,这次总算可以用默认的Grid布局了
依旧是在解决方案WpfApp3新建项目:命名项目名字GridButton6。
XAML代码如下:
<Window x:Class="GridButton6.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:GridButton6"
mc:Ignorable="d"
Title="MainWindow" Height="270" Width="480">
<Grid Background="YellowGreen">
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<Button Grid.Row="0" Grid.Column="0" Margin="2">One</Button>
<Button Grid.Row="0" Grid.Column="1" Margin="2">Two</Button>
<Button Grid.Row="1" Grid.Column="0" Margin="2">Three</Button>
<Button Grid.Row="1" Grid.Column="1" Margin="2">Four</Button>
<Button Grid.Row="2" Grid.Column="0" Margin="2">Five</Button>
<Button Grid.Row="2" Grid.Column="1" Margin="2">Six</Button>
</Grid>
</Window>
效果图
此例中,调节窗口大小时,Button大小随着窗口的变化而变换。
Grid简单实例——登录界面
在很多应用程序中都会有登录页面的设计,在此,作为一个Grid实例。
设计要求:现在使用Grid布局控件来设计一个简单的登录界面,使用多种控件。
依旧是在解决方案WpfApp3新建项目:命名项目名字GridLogin。
XAML代码如下:
<Window x:Class="GridLogin.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:GridLogin"
mc:Ignorable="d"
Title="MainWindow" Height="270" Width="480">
<Grid Background="Gray">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<TextBlock Text="Username:" VerticalAlignment="Center" HorizontalAlignment="Center" FontSize="20" FontFamily="Times New Roman"/>
<TextBlock Text="Password:" Grid.Row="1" VerticalAlignment="Center" HorizontalAlignment="Center" FontSize="20" FontFamily="Times New Roman"/>
<TextBox Grid.Column="1" Margin="25" FontSize="20"/>
<TextBox Grid.Column="1" Grid.Row="1" Margin="25" FontSize="20"/>
<Button Grid.Row="2" Margin="25" Content="Login" FontSize="20" FontFamily="Times New Roman" Background="LightBlue"/>
<Button Grid.Row="2" Margin="25" Content="Cancel" Grid.Column="1" FontSize="20" FontFamily="Times New Roman"/>
</Grid>
</Window>
效果图
该登录页面含有两个TextBlock
、两个TextBox
和两个Button
,共有6个子元素。当窗口大小变化时,页面子元素会随着窗口的大小动态调整。
上面的两个实例显示了基本的Grid布局网格的用法,但并没有充分体现Grid的重要特征。
Grid特征——从结构中分离布局
前面讲到的WPF布局面板,都是通过改变元素的声明顺序来改变布局结构,因为声明顺序不同,所以布局结构也会不同。WPF除Grid以外的布局面板,首先,将布局面板融入可视化树中,便于调用布局算法。然后,根据声明子控件声明的顺序,设定布局结构。
Grid能从结构中分离布局,指的是不依赖子元素的声明顺序来改变布局结构。因为子元素可以通过两个附加属性进行定位。
重新设计以上做好GridButton6项目,对6个Button通过设置Grid.Row
和Grid.Column
两个附加属性值,让页面显示与Button的定义顺序相反。
XAML代码如下:
<!-- 保留Window代码 -->
<Button Grid.Row="2" Grid.Column="1" Margin="2">One</Button>
<Button Grid.Row="2" Grid.Column="0" Margin="2">Two</Button>
<Button Grid.Row="1" Grid.Column="1" Margin="2">Three</Button>
<Button Grid.Row="1" Grid.Column="0" Margin="2">Four</Button>
<Button Grid.Row="0" Grid.Column="1" Margin="2">Five</Button>
<Button Grid.Row="0" Grid.Column="0" Margin="2">Six</Button>
</Grid>
</Window>
效果图
运行上述代码所显示的效果而言,6个Button
布局效果与Button
定义顺序相反,是通过设置附加属性Grid.Row
和Grid.Column
的值来改变Button
的输出。
我们再给一个UniformGrid
的示例对比,新建项目UniformAndGrid
XAML代码如下:
<Window x:Class="UniformAndGrid.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:UniformAndGrid"
mc:Ignorable="d"
Title="MainWindow" Height="150" Width="200">
<Border BorderBrush="Black" BorderThickness="1"
HorizontalAlignment="Center" VerticalAlignment="Center">
<UniformGrid Columns="2" Background="Green" >
<Button Content="Six" Margin="1"/>
<Button Content="Five" Margin="1"/>
<Button Content="Four" Margin="1"/>
<Button Content="Three" Margin="1"/>
<Button Content="Two" Margin="1"/>
<Button Content="One" Margin="1"/>
</UniformGrid>
</Border>
</Window>
效果图
通过对比发现,使用UniformGrid
布局时,只能通过改变子元素定义顺序改变布局结构。Grid
布局页面时,在不影响代码的情况下,通过设置Grid.Row
和Grid.Column
两个附加属性值,调整布局结构,这种从结构中分离布局使Grid
能够创建控件结构。
Grid特征——尺寸模型
在Canvas布局中,要使用元素绝对值来分割空间;而Grid引入了百分比尺寸,也就是列或行的高度能设置为星花( * )单位。星花允许行和列在按照内容尺寸或绝对尺寸分配空间后,占用网格空间的一个百分比值。
接下来理解*的用法,新建项目GridPer
XAML代码如下:
<Window x:Class="GridPer.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:GridPer"
mc:Ignorable="d"
Title="MainWindow" Height="270" Width="480">
<Grid Background="GreenYellow">
<Grid.RowDefinitions>
<RowDefinition Height="50"/>
<RowDefinition Height="1*"/>
<RowDefinition Height="2*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="80"/>
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Button Grid.Row="0" Grid.Column="0" Margin="2">One</Button>
<Button Grid.Row="0" Grid.Column="1" Margin="2">Two</Button>
<Button Grid.Row="1" Grid.Column="0" Margin="2">Three</Button>
<Button Grid.Row="1" Grid.Column="1" Margin="2">Four</Button>
<Button Grid.Row="2" Grid.Column="0" Margin="2">Five</Button>
<Button Grid.Row="2" Grid.Column="1" Margin="2">Six</Button>
</Grid>
</Window>
效果图
第0行第0列的Button One
的高度(Height=“50”) 是50像素,宽度(Width=“80”) 是80像素。Button Three
所在行占余下空间的1/3, Button Five
所在行占余下空间的2/3。还使用了Grid.Row
、Grid.Column
附加属性把子元素定位到单元格。在此,需要声明的是,Grid
的行和列都是从0开始计数的,如果没有指定子元素的行列值,则子元素默认位于第0行第0列。
接下来更改设计,将上述代码中的Height与Width全改成星花( * )单位的XAML代码如下:
<!-- 保留Window代码 -->
<Grid Background="GreenYellow">
<Grid.RowDefinitions>
<RowDefinition Height="1*"/>
<RowDefinition Height="2*"/>
<RowDefinition Height="3*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="1*"/>
</Grid.ColumnDefinitions>
<Button Grid.Row="0" Grid.Column="0" Margin="2">One</Button>
<Button Grid.Row="0" Grid.Column="1" Margin="2">Two</Button>
<Button Grid.Row="1" Grid.Column="0" Margin="2">Three</Button>
<Button Grid.Row="1" Grid.Column="1" Margin="2">Four</Button>
<Button Grid.Row="2" Grid.Column="0" Margin="2">Five</Button>
<Button Grid.Row="2" Grid.Column="1" Margin="2">Six</Button>
</Grid>
效果图
第0行第0列的高度(Height=“1*”) 是加权百分比,表示占总数 (1+2+3=6) 中的1份,即六分之一;第1行第1列的高度占六分之二,第2行第2列的高度占六分之三。同理,可求Width宽度所占的百分比,各为二分之一。
Height
属性和Width
属性可以被设置成绝对值、百分比和Auto。当采用绝对值时,像素是默认单位,可省略。
设置行高或者列宽时,除了可以使用像素作为单位外,还能使用厘米(Centimeter)、英寸(Inch)和点(Point)。它们与像素之间的换算关系如下:
- 1cm = (96/2.54)pixel
- 1in = 96pixel
- 1pt = (96/72)pixel
Grid特征——共享尺寸组
共享尺寸组是使用Grid布局时,位于同一列的子元素具有相同的尺寸大小。若在一个窗体中,允许一组控件具有相同的尺寸,尺寸大小由最宽的那个控件来决定。
设置共享尺寸,需要两个步骤。首先,在某一列或行上设置SharedSizeGroup属性;在控件上设置 “IsSharedSizeScope = “True”” 。
演示这个效果,新建一个项目GridSSG,对位于同一窗口的4个Button实现共享尺寸。
XAML代码如下:
<Window x:Class="GridSSG.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:GridSSG"
mc:Ignorable="d"
Title="MainWindow" Height="270" Width="480">
<StackPanel>
<Grid Grid.IsSharedSizeScope="True">
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" SharedSizeGroup="a"/>
<ColumnDefinition Width="Auto" SharedSizeGroup="a"/>
</Grid.ColumnDefinitions>
<Button Grid.Row="0" Grid.Column="0">One</Button>
<Button Grid.Row="0" Grid.Column="1">Two</Button>
<Button Grid.Row="1" Grid.Column="0">Three (Which is Longer)</Button>
<Button Grid.Row="1" Grid.Column="1">Four</Button>
</Grid>
</StackPanel>
</Window>
效果图
尽管列中设置了Width= "Auto"
,但是4个Button全与最宽的那个Button保持一致,因为列上设置了共享尺寸分组(SharedSizeGroup= "a")
,并且在Grid中设置"Grid.IsSharedSizeScope="True""
。
将代码中的“Grid.IsSharedSizeScope="True""
和“SharedSizeGroup="a""
去掉,运行后,页面显示效果如下土所示。*Button(Two)与Button(Four)*不再与最宽的Button保持。
Grid特征——跨越行和列
前面的例子,用Grid.Row
和Grid.Column
附加属性把子元素定位到单元格。还可以使用另外两个附加属性Grid.RowSpan
和Grid.ColumnSpan
,让子元素跨越多个单元格。
下面使用Grid设计计算器的页面,新建项目GridCalculator
XAML代码如下:
<Window x:Class="GridCalculator.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:GridCalculator"
mc:Ignorable="d"
Title="MainWindow" Height="360" Width="520">
<Grid Background="GreenYellow">
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBox Grid.Row="0" Grid.ColumnSpan="4" Margin="2,4,2,2"/>
<Button Grid.Row="1" Grid.Column="0" Margin="2">1</Button>
<Button Grid.Row="1" Grid.Column="1" Margin="2">2</Button>
<Button Grid.Row="1" Grid.Column="2" Margin="2">3</Button>
<Button Grid.Row="1" Grid.Column="3" Margin="2">+</Button>
<Button Grid.Row="2" Grid.Column="0" Margin="2">4</Button>
<Button Grid.Row="2" Grid.Column="1" Margin="2">5</Button>
<Button Grid.Row="2" Grid.Column="2" Margin="2">6</Button>
<Button Grid.Row="2" Grid.Column="3" Margin="2">-</Button>
<Button Grid.Row="3" Grid.Column="0" Margin="2">7</Button>
<Button Grid.Row="3" Grid.Column="1" Margin="2">8</Button>
<Button Grid.Row="3" Grid.Column="2" Margin="2">9</Button>
<Button Grid.Row="3" Grid.Column="3" Margin="2">*</Button>
<Button Grid.Row="4" Grid.ColumnSpan="2" Margin="2">0</Button>
<Button Grid.Row="4" Grid.Column="2" Margin="2">.</Button>
<Button Grid.Row="4" Grid.Column="3" Margin="2">/</Button>
<Button Grid.Row="5" Grid.ColumnSpan="2" Margin="2">Del</Button>
<Button Grid.Row="5" Grid.Column="2" Grid.ColumnSpan="2" Margin="2">=</Button>
</Grid>
</Window>
效果图
Grid
布局6行4列网格。页面是由1个TextBox
、17个Button
构成。
其中,<TextBox Grid.Row="0" Grid.ColumnSpan="4" Margin="2,4,2,2"/>
代码中的Grid.Row=“0"
用来设置TextBox位于第0行;Grid.ColumnSpan="4
设置TextBox占据4列,即从第0列~第3列;Margin= "2,4,2,2"
设置TextBox与四周边界的左、上、右、下相距四周的间隙分别是2像素、4像素、2像素、2像素。
修改上述代码中的两条语句:
<Button Grid.Row="4" Grid.ColumnSpan="2" Margin="2">0</Button>
<Button Grid.Row="5" Grid.ColumnSpan="2" Margin="2">Del</Button>
修改后的XAML语句:
<Button Grid.Row="4" Grid.RowSpan="2" Margin="2">0</Button>
<Button Grid.Row="4" Grid.RowSpan="2" Grid.Column="1" Margin="2">Del</Button>
效果图
Grid.RowSpan="2"
实现跨越2行。
Grid特征——GridSplitter
GridSplitter是网格分割线。它支持用户在运行时编辑行或列,当用户移动分割线时,还可以改变行高列宽。在很多聊天软件的页面布局都含有分割线。
接下来,设计常用的“一上二下式”布局。新建一个项目命名为GridSplitter
该布局的上面是一个菜单栏,下面是两列网格,两列网格间是条分割线。在布局中的子元素全部用Button。
XAML代码如下:
<Window x:Class="GridSplitter.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:GridSplitter"
mc:Ignorable="d"
Title="MainWindow" Height="270" Width="480">
<DockPanel>
<Button Height="25" DockPanel.Dock="Top"> Menu </Button>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="3*"/>
<ColumnDefinition Width="7*"/>
</Grid.ColumnDefinitions>
<Button VerticalContentAlignment="Center" HorizontalContentAlignment="Center">ColumnLeft</Button>
<GridSplitter Width="3"/>
<Button VerticalContentAlignment="Center" HorizontalContentAlignment="Center" Grid.Column="1">ColumnRight</Button>
</Grid>
</DockPanel>
</Window>
效果图
当程序处在运行状态下,用户可以根据需要,移动ColumnLeft与ColumnRight之间的GridSplitter(分割线).
在ColumnRight中再加上一条垂直分割线,XAML代码如下:
<Window x:Class="GridSplitter.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:GridSplitter"
mc:Ignorable="d"
Title="MainWindow" Height="270" Width="480">
<DockPanel>
<Button Height="25" DockPanel.Dock="Top"> Menu </Button>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="3*"/>
<ColumnDefinition Width="7*"/>
</Grid.ColumnDefinitions>
<Button VerticalContentAlignment="Center" HorizontalContentAlignment="Center">ColumnLeft</Button>
<GridSplitter Width="3"/>
<Grid x:Name="GridRightColumn" Grid.Row="0" Grid.Column="1">
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Button>ColumnRightUp</Button>
<GridSplitter Height="3" HorizontalAlignment="Stretch" VerticalAlignment="Bottom"/>
<Button Grid.Row="1">ColumnRightDown</Button>
</Grid>
</Grid>
</DockPanel>
</Window>
效果图
分析布局结构发现,在DockPanel下是Grid,Grid下还有一个Grid。其中,语句<Grid x:Name="GridRightColumn" Grid.Row="0" Grid.Column="1">
指的是为第2个Grid命名,并使用了第一个Grid的附加属性定位;<GridSplitter Height="3" HorizontalAlignment="Stretch" VerticalAlignment="Bottom"/>
语句定义垂直分割线水平拉伸。
本章小结
本章重点介绍了WPF布局原则以及布局面板中的Canvas、DockPanel、StackPanel、WrapPanel和UniformGrid的适用场合;详细介绍了Grid 从结构中分离布局、尺寸模型,共享尺寸组和跨越行列等特征,并演示了Grid的多种用法。本章中的案例涉及Windows窗口页面、用户搜索页面、用户登录页面、计算器页面及常用布局。但是布局内容远不止于此,当了解更多的控件以后,可以做出个性化的布局。在以后的章节中还会补充布局的相关知识。
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)