WPF作为专门的用户界面技术,布局功能是它的核心功能之一。友好的用户界面和良好的用户体验离不开设计精良的布局。日常工作中,WPF设计师工作量最大的两部分就是布局和动画,除了点缀性的动画外,大部分动画也是布局间的转换,UI布局的重要性可见一斑。布局是静态的,动画是动态的,用户体验就是用户在这动静之中与软件功能产生交互时的感受。
每个布局元素都有自己的特点,有优点也有缺点。
1.布局元素总体介绍
<Canvas>
<Label Canvas.Left="10" Canvas.Right="10" Canvas.Top="0" Content="Left:10,Top:0,Right:10" Background="Gray" />
<Label Canvas.Right="10" Canvas.Top="0" Content="Right:10,Top:0" Background="Gray" />
</Canvas> 效果 英文名 中文名 简写 换算 Pixel 像素 px(默认单位,可省略) Inch 英寸 in 1in=96px Centimeter 厘米 cm 1cm=(96/2.54)px Point 点 pt 1pt=(96/72)px 自动尺寸:对应的RowDefinition和ColumnDefinition尺寸由其子元素决定,为子元素所需空间。 比例尺寸:将Grid剩余空间(Grid中空间-绝对尺寸和自动尺寸所占空间)按比例分配。 不得为空; 必须只包含字母、数字和下划线字符; 不得以数值开头。 如需SharedSizeGroup共享大小生效,需设置IsSharedSizeScope属性,该属性是Grid的依赖属性,也是附加属性。由于一个共享组可以在多个Grid间使用,为避免可能的名称冲突(也为了减少需要遍历的逻辑树数量),同一个SharedSizeGroup必须在一个IsSharedSizeScope为true的共同的父元素下使用。 参与大小共享的列和行不遵循 Star 大小(比例尺寸)调整。 在大小共享方案中,Star 大小调整将作为 Auto 处理。 如果在某个资源模板内将 IsSharedSizeScope 设置为 true,并将 SharedSizeGroup 定义为在该模板的外部,则 Grid 大小共享不起作用。 使用自定义面板: 效果 代码
<!--添加ScrollViewer以至出现滚动条-->
<ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto">
<StackPanel Orientation="Horizontal" FlowDirection="RightToLeft">
<Label Content="item1" Width="100" Height="50" Background="Gray" Margin="5" HorizontalAlignment="Left" FlowDirection="LeftToRight" />
<Label Content="item2" Width="100" Height="50" Background="Gray" Margin="5" HorizontalAlignment="Left" />
<Label Content="item3" Width="100" Height="50" Background="Gray" Margin="5" HorizontalAlignment="Left" />
<Label Content="item4" Width="100" Height="50" Background="Gray" Margin="5" HorizontalAlignment="Left" />
<Label Content="item5" Width="100" Height="50" Background="Gray" Margin="5" HorizontalAlignment="Left" />
<Label Content="item6" Width="100" Height="50" Background="Gray" Margin="5" HorizontalAlignment="Left" />
</StackPanel>
</ScrollViewer>
<WrapPanel Orientation="Horizontal" ItemHeight="50" ItemWidth="140" FlowDirection="RightToLeft">
<Border BorderBrush="Red" BorderThickness="1">
<Label Content="item1" Width="100" Height="50" Background="Gray" Margin="5" HorizontalAlignment="Left" FlowDirection="LeftToRight" />
</Border>
<Label Content="item2" Width="100" Height="50" Background="Gray" Margin="5" HorizontalAlignment="Left" />
<Label Content="item3" Width="100" Height="50" Background="Gray" Margin="5" HorizontalAlignment="Left" />
<Label Content="item4" Width="100" Height="50" Background="Gray" Margin="5" HorizontalAlignment="Left" />
<Label Content="item5" Width="100" Height="50" Background="Gray" Margin="5" HorizontalAlignment="Left" />
<Label Content="item6" Width="100" Height="50" Background="Gray" Margin="5" HorizontalAlignment="Left" />
</WrapPanel>
<DockPanel LastChildFill="False">
<Label Content="Left" DockPanel.Dock="Left" Background="Gray" />
<Label Content="Top" DockPanel.Dock="Top" Background="Blue" />
<Label Content="Right" DockPanel.Dock="Right" Background="LightBlue" />
<Label Content="Bottom" DockPanel.Dock="Bottom" Background="LightGray" />
<!--默认DockPanel.Dock="Left"-->
<Label Content="Last" Background="LightSkyBlue"/>
</DockPanel>//绝对尺寸
GridLength gl1 = new GridLength(100);
GridLength gl2 = new GridLength(100,GridUnitType.Pixel);
//自动尺寸
GridLength gl3 = new GridLength(0,GridUnitType.Auto);
GridLength gl4 = GridLength.Auto;
//比例尺寸
GridLength gl5 = new GridLength(1, GridUnitType.Star);
1.5.2 GridSplitter的使用
GridSplitter的HorizontalAlignment默认值为Right,VerticalAlignment的默认值为Stretch。要使GridSplitter显示,必须在某一方向上是Stretch,不然就是一个小圆点;另外还需要有一定的高度或宽度。如果 HorizontalAlignment 和 VerticalAlignment 属性的设置没有实现所需的 GridSplitter 行为,则可以更改 ResizeDirection 和 ResizeBehavior 属性的设置。
GridSplitter 可能会被 Grid 的 Children 集合中包含的其他对象遮盖。通过设置Panel.ZIndex附加属性来控制z方向的位置。1.5.3 共享行和列的尺寸
SharedSizeGroup 属性值必须符合下列规则:
注意点:<StackPanel Orientation="Vertical" Grid.IsSharedSizeScope="True">
<Grid ShowGridLines="True">
<Grid.ColumnDefinitions>
<!--绝对尺寸-->
<ColumnDefinition Width="100" />
<!--自动尺寸-->
<ColumnDefinition Width="auto" />
<!--比例尺寸-->
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="3*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="1in" SharedSizeGroup="myshare" />
<RowDefinition Height="96" />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Label Grid.Column="0" Grid.Row="1" Content="长度固定" Background="Gray"/>
<Label Grid.Column="1" Grid.Row="1" Content="长度不定,我有多宽列有多宽" Background="LightBlue"/>
<Label Grid.Column="2" Grid.Row="1" Content="长度不定,占据余下宽带的1/4" Background="LightGray"/>
<GridSplitter Height="5" Grid.ColumnSpan="3" Grid.Row="1" Background="Red"
VerticalAlignment="Bottom" HorizontalAlignment="Stretch"
/>
<GridSplitter Height="5" Grid.Column="3" Grid.Row="1" Background="Black" VerticalAlignment="Bottom" HorizontalAlignment="Stretch"/>
<GridSplitter Width="5" Grid.Column="1" Grid.Row="1" Background="Blue"
VerticalAlignment="Stretch" HorizontalAlignment="Right"
ResizeBehavior="BasedOnAlignment"/>
<Label Grid.Row="3" Content="与第一行共享高度" Background="Gray" />
<Label Content="share row" />
<!--拖动此GridSplitter,查看共享行效果-->
<GridSplitter VerticalAlignment="Bottom" HorizontalAlignment="Stretch" Height="5" Background="Red"
ToolTip="拖动此GridSplitter,查看共享行效果"/>
</Grid>
<Grid ShowGridLines="True">
<Grid.RowDefinitions>
<RowDefinition SharedSizeGroup="myshare" />
<RowDefinition Height="100" />
</Grid.RowDefinitions>
<Label Content="share row" />
</Grid>
</StackPanel>
<!--定义4列-->
<UniformGrid Columns="4" FirstColumn="1">
<Label Content="item1" Background="Gray" Margin="5" HorizontalAlignment="Stretch" FlowDirection="RightToLeft" />
<Label Content="item2" Background="Gray" Margin="5" HorizontalAlignment="Stretch" />
<Label Content="item3" Background="Gray" Margin="5" HorizontalAlignment="Stretch" />
<Label Content="item4" Background="Gray" Margin="5" HorizontalAlignment="Stretch" />
<Label Content="item5" Background="Gray" Margin="5" HorizontalAlignment="Stretch" />
<Label Content="item6" Background="Gray" Margin="5" HorizontalAlignment="Stretch" />
</UniformGrid>
//斜对角排列子元素
public class MyPanel : Panel
{
public MyPanel()
: base()
{
}
//测量(Measure)阶段,即父元素询问子元素所期望的尺寸,从而确定自身的尺寸
//MeasureOverride传递的参数为Size类型,实际是上一级父元素告知当前元素可分配的空间(availableSize);返回的参数Size类型,是该元素所期望的空间(desiredSize)
protected override Size MeasureOverride(Size availableSize)
{
double maxWidth = 0.0;
double maxHeight = 0.0;
double sumHeight = 0.0;
double l = Math.Sqrt(availableSize.Height * availableSize.Height + availableSize.Width * availableSize.Width);
double heightratio = availableSize.Height / l;
double widthratio = availableSize.Width / l;
foreach (UIElement child in InternalChildren)
{
child.Measure(availableSize);
maxWidth = Math.Max(child.DesiredSize.Width, maxWidth);
maxHeight = Math.Max(child.DesiredSize.Height, maxHeight);
sumHeight += child.DesiredSize.Height;
}
Size ideal = new Size(maxWidth * widthratio + sumHeight * heightratio, maxWidth * heightratio + sumHeight * widthratio);
Size desired = ideal;
if (!double.IsInfinity(availableSize.Width))
{
if (availableSize.Width < desired.Width)
desired.Width = availableSize.Width;
}
if (!double.IsInfinity(availableSize.Height))
{
if (availableSize.Height < desired.Height)
desired.Height = availableSize.Height;
}
return desired;
}
//布置(Arrange)阶段,在这个期间每个父元素会告知子元素的尺寸和位置
//ArrangeOverride传递和返回的参数同样是Size类型,传递的参数指定是该元素摆放所用的尺寸(finalSize);返回参数同为该元素及其子元素所占用的尺寸。
protected override Size ArrangeOverride(Size finalSize)
{
Rect layoutRect = new Rect(0, 0, finalSize.Width, finalSize.Height);
double angle = -1 * Math.Atan(finalSize.Height / finalSize.Width) * 180 / Math.PI;
double l = Math.Sqrt(finalSize.Height * finalSize.Height + finalSize.Width * finalSize.Width);
double heightratio = finalSize.Height / l;
double widthratio = finalSize.Width / l;
double left = 0.0;
double top = 0.0;
double maxWidth = 0.0;
foreach (UIElement child in InternalChildren)
{
maxWidth = Math.Max(child.DesiredSize.Width, maxWidth);
}
top = maxWidth * heightratio;
foreach (UIElement child in InternalChildren)
{
Point childLocation = new Point(left + (maxWidth - child.DesiredSize.Width) * widthratio / 2, top);
left += (layoutRect.Width - maxWidth * widthratio) / InternalChildren.Count;
top += (layoutRect.Height - maxWidth * heightratio) / InternalChildren.Count;
//旋转中心点没定好,显示效果不好
//child.RenderTransform = new RotateTransform
//(angle, childLocation.X, childLocation.Y);
child.Arrange(new Rect(childLocation, child.DesiredSize));
}
return finalSize;
}
}<local:MyPanel>
<Button Background="#00000000" Width="100">1</Button>
<Button Background ="#FFFFCCFF" Width="150">2</Button>
<Button Background ="#FFFF9BFF" Width="120">3</Button>
<Button Background ="#FFFF00FF" Width="50">4</Button>
<Button Background="#FFFFCCFF" Width="80">5</Button>
</local:MyPanel>
评论回复