WPF提供了一套面板,他们是用于排列他们所包含的元素的特殊用户界面元素。
3.1栈面板(StackPanel)
栈面板就是将其其包含的元素按照堆栈的形式排列,通过设置面板的Orientation属性设置了两种排列方式:默认的横排(Horizontal)和竖排(Vertical)。纵向的StatickPanel默认每个元素宽度与面板一样宽,反之横向亦然。如果包含的元素超过了面板空间,它只会截断内容。
< TextBlock Margin = " 3 " > Lock For: TextBlock >
< Button Margin = " 3,5 " HorizontalAlignment = " Left " > Search Button >
StackPanel >
元素的Margin属性用于使元素之间产生一定得间隔,当元素空间大于其内容的空间时,剩余空间将由HorizontalAlignment和VerticalAlignment属性来决定如何分配。
3.2环绕面板(WrapPanel)
将WrapPanel翻译成环绕面板个人觉得有待商榷,管他的就这么叫吧。大伙都知道在以往的TextBox中Wrap属性的意思就是在文本超过TextBox的长度时自动换行,这个面板也是如此。WrapPanel面板也提供了Orientation属性设置排列方式,这跟上面的StackPanel如出一辙。不同的是WrapPanel会自动换行。
3.3停靠面板(DockPanel)
停靠面板其实就是在2.0中类似于Dock属性的元素。DockPanel会对每个子元素进行排序,并停靠在面板的一侧,多个停靠在同侧的元素则按顺序排序,最后一个元素填充这个Panel(除非将LastChildFill属性设置为False,它才会使得没有填充的部分为空)。对于在DockPanel中的元素的停靠属性可以通过Panel.Dock的附加属性来设置,如下:
<DockPanel>
<Button DockPanel.Dock="Top">TopButton>
<Button DockPanel.Dock="Bottom">BottomButton>
<Button DockPanel.Dock="Left">LeftButton>
<Button DockPanel.Dock="Right">RightButton>
<Button>FillButton>
DockPanel>
效果如图
在这里顺带指明一下,WPF中所有元素都由FrameworkElement元素继承而来。
在DockPanel中,停靠边界交界处的元素定义顺序直接影响到最终的显示效果。如果将left和Bottom按钮对调定义,显示如下图
3.4网格面板(Grid)
Grid与其他面板不同的是,他的面板布局由
3.4.1Grid列宽与行高
Grid的列宽与行高可采用固定、自动、按比列三种方式定义
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
Grid.RowDefinitions>
<Grid.ColumnDefinitons>
<ColumnDefinition Width="50"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="2*"/>
Grid.ColumnDefinitons>
<TextBox Grid.Row=0 Grid.Column="0" Margin="5">
Grid>
主要说明一下按比例定义的方式,即代码中的1*和2*,*是指相对大小,也就是说2*的宽度是1*的2倍,上面的代码写成3*和6*是没有区别的。据个例子,如果整个Grid宽度为350,第一列定义为固定长度50,第二列自动长度,而放入第二列的元素宽度为100,则剩下的2列按照1比2分配给定义为1*和2*的列。另外,如果*之前没有加数值则表示1*。也可以用浮点型的数字来定义比例,如2.5*。
3.4.2跨越多行和多列
使用Grid.ColumnSpan和Grid.RowSpan附加属性可以让相互间隔的行列合并
<Rectangle Grid.Row="1" Grid.Column="2" Grid.RowSpan="3" Margin="5,3" Fill="Black" />
<Rectangle Grid.Column="0" Grid.Row="6" Grid.ColumnSpan="2" Grid.RowSpan="2" Margin="5,3" Fill="Black" />
<TextBlock Grid.Column="0" Grid.Row="0">Here Is Some WordsTextBlock>
由上实例可以看出Grid的单元格可以被多个元素使用,在这种情况系,元素将按照Panel.ZIndex的屏幕Z轴层叠元素;元素也可以跨越多个单元格。
3.4.3网格的连续性
网格的连续性要实现的效果是对于不同的Grid使得它们的行列一致,如将Grid放置在一个ScrollViewer中要显示Tilte等标题信息和明细信息,那么放置于同一Grid时会在明细太多的时候下拉滚动条,会使标题信息消失,那么就要用到2个Grid,一个用来显示标题信息,一个用来显示详细信息,那么2个Grid会出现列对齐不一致的问题。采用不同Grid之间的共享尺寸组,可以解决此问题
<DockPanel Grid.IsSharedSizeScope="True">
<Grid DockPanel.Dock="Top">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" SharedSizeGroup="Location" />
<ColumnDefinition Width="Auto" SharedSizeGroup="Rank" />
<ColumnDefinition Width="Auto" />
Grid.ColumnDefinitions>
<Border Grid.Column="0" Grid.Row="0" BorderThickness="1" Background="LightGray" BorderBrush="Gray">
<TextBlock>TitleTextBlock>
Border>
<Border Grid.Column="1" Grid.Row="0" BorderThickness="1" Background="LightGray" BorderBrush="Gray">
<TextBlock>LocationTextBlock>
Border>
<Border Grid.Column="2" Grid.Row="0" BorderThickness="1" Grid.ColumnSpan="2" Background="LightGray" BorderBrush="Gray">
<TextBlock>RankTextBlock>
Border>
<FrameworkElement Grid.Column="3"
Width="{DynamicResource {x:Static SystemParameters.ScrollWidthKey}}" />
Grid>
<ScrollViewer>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" SharedSizeGroup="Location" />
<ColumnDefinition Width="Auto" SharedSizeGroup="Rank" />
Grid.ColumnDefinitions>
<Grid>
ScrollViewer>
DockPanel>
开发人员有可能有意无意的将不同的Grid使用相同共享尺寸组的组名,为避免这类问题,Grid的共享尺寸组的作用范围是限制在其父元素之下,也就是在不同父元素之下的Grid即使共享尺寸组名称相同,那么它们也不会采用相同的格式。Grid.IsSharedSizeScope的附加属性定义在DockPanel中,用来标明DockPanle是一个公用的父元素。将SharedSizeGroup将不同列划分相同的组。
在头部标题Grid的尾列加入
3.5均布网格 UniformGrid
一笔带过,均布网格的是Grid的简化版本,每个单元格的大小相同,不用在定义行列集合。均布网格每个单元格只能容纳一个元素,将自动按照定义在其内部的元素个数,自动创建行列,并通常保持相同的行列数。
3.6画布Canvas
画布只是一个为元素的放置提供场所的一个容器,不会自动调整内部元素的排列及大小。不指定元素位置,元素将默认显示在画布的左上角。Canvas的主要用途是用来画图。Canvas不会自动裁减超过自身范围的内容,即多出的内容会显示在Canvas外面,那是因为默认ClipToBounds="False";如果设置ClipToBounds="True",则会裁剪多于内容。
3.7视图框ViewBox
ViewBox会自动缩放其内容以填充可用的空间。ViewBox由Derector派生而来,只能有一个子元素,严格上来说不是面板。由于Canvas不会随着画布大小个改变而调整画布内部的元素大小,所以当Canvas放置在ViewBox中是,画布内的元素就可以自动调整大小了。其中ViewBox的Stretch属性有Fill、None、和UniformToFill三种模式,Fill使得元素被拉伸填满ViewBox,None的视乎没有意义,因为设置了Stretch为None,则跟没有使用ViewBox没有区别。UniformToFill将保持元素大小,并在元素大于ViewBox的时候,在一个方向是截取元素内容。
3.8公共布局属性
公共属性比较多不一一介绍,先简要见一下Margin和Padding的区别,Margin是元素与其停放父元素的间距,Padding是指在本元素内部的元素内容与边缘的距离。FlowDirection属性标示元素的内容显示方向。Panel.ZIndex是相对于显示屏的Z轴坐标,用于调整层叠元素的现实先后。RenderTransform和LayoutTransform用来将缩放和旋转的变换应用到某个元素上
3.9自定义面板
布局系统的工作原理为:测量和排列,测量就是用探测面板需要多大空间的阶段,排列则定义其面板内子元素的排列规则。自定义面板要继承自Panel类并重写MeasureOverride和ArrangeOverride方法。
using System;
using System.Windows.Controls;
using System.Windows;
namespace CustomPanel {
public class DiagonalPanel : Panel {
protected override Size MeasureOverride( Size availableSize ) {
double totalWidth = 0;
double totalHeight = 0;
foreach( UIElement child in Children ) {
child.Measure( new Size( double.PositiveInfinity,double.PositiveInfinity ) );
Size childSize = child.DesiredSize;
totalWidth += childSize.Width;
totalHeight += childSize.Height;
}
return new Size( totalWidth, totalHeight );
}
protected override Size ArrangeOverride( Size finalSize ) {
Point currentPosition = new Point( );
foreach( UIElement child in Children ) {
Rect childRect = new Rect( currentPosition, child.DesiredSize );
child.Arrange( childRect );
currentPosition.Offset( childRect.Width, childRect.Height );
}
return new Size( currentPosition.X, currentPosition.Y );
}
}
}