我们首先来了解一下图形化用户界面(Graphic User Interface)也就是我们常常听到的GUI。举个简单的例子,同样是数据,我们可以用控制台程序加格式控制符等输出,但是这些都不如GUI来的友好和方便。

WPF相对于其它只能使用编程语言进行UI设计,具有专门用于UI设计的XAML,并且能够确保界面布局能恰倒好处的适应不同的窗口尺寸。

我们来查看Window和Page的源码,发现Window的间接基类ContentControl和Page类都使用了一个object类型的Content属性。所以它只能包含一个元素,但是我们想放置多个元素怎么办呢?这个时候我们就需要使用布局容器。

为什么布局容器又可以添加多个元素呢?所有的WPF布局容器都派生自System.Windows.Controls.Panel这个抽象类。我们从类层次结构图中就不难发现它所包含的一系列布局容器。我们再来看看这个Panel类的源代码,就会发现该类有一个ContentProperty("Children")特性,所以我们会在该类中找到一个UIElementCollection类型(该类实现了IList接口)的Children属性。只要是继承自UIElement的类,都可以添加到布局容器中。

下面让我们来看看其中的一些布局容器以及与之相关联的控件属性。

StackPanel

它是最简单的布局容器,允许子元素在单行或者单列以堆栈的形式放置。

    <StackPanel Orientation="Vertical" Background="LightCyan">
<Label Background="LemonChiffon">A Button Stack</Label>
<Button>Button A</Button>
<Button>Button B</Button>
<Button>Button C</Button>
</StackPanel>

我们运行会发现,StackPanel容器会充满整个窗口,因为它的HorizontalAlignment、VerticalAlignment属性值都是Stretch。

子元素是按照自上而下的方式排列的,因为我们设置了Orientation属性(默认值就是Vertical,所以这里我们可以不设置该属性);如果设置成Horizontal,就会从左到右排列。在默认情况下,每个子元素的高度都适合与它们自己内容的高度。如果是Horizontal,每个子元素的宽度都适合它们自己内容的长度。

我们拉伸运行的WPF程序,会发现StackPanel始终会充满整个窗口,子元素也会根据设置做相应的变化。

我们可以设置子元素的一些属性配合布局容器的属性,来决定布局。常见的属性有:HorizontalAlignment、VerticalAlignment、Margin、MinWidth、MinHeight、MaxWidth、MaxHeight、Height、Width。

<Window x:Class="WPFDemo.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Width="200" Height="350"> <StackPanel Background="LightCyan">
<Label Background="LemonChiffon">A Button Stack</Label>
<Button HorizontalAlignment="Left">Button A</Button>
<Button HorizontalAlignment="Center">Button B</Button>
<Button HorizontalAlignment="Right">Button C</Button>
<Button Margin="5" Height="40" Width="100" HorizontalAlignment="Right">Button D</Button>
<Button MinWidth="320">Button E</Button>
</StackPanel> </Window>

运行这个程序,我们就会发现A、B、C三个Button分别位于水平方向上的不同位置;Button D距离四周的元素有5个单位的距离,并且指定了该按钮的高度和宽度;Button E指定了MinWidth,远大于Window的200,所以刚运行的时候左边被截断,需要我们通过拉伸才可完全看见。

Grid

顾名思义,Grid会以网格的形式对内容元素们进行布局。它具有如下的特点:

 · 可以通过RowDefinitions和ColumnDefinitions属性,它们分别是RowDefinition和ColumnDefinition集合。定义任意多的行和列。

 · 行的高度,列的宽度可以使用绝对数值(double数值加单位后缀px、in、cm、pt,不过在设置的时候最好不要加单位),比例值(double数值后加一个*号),自动值(字符串Auto)来灵活设置。

 · 内部元素可以使用Grid的附加属性Grid.Row、Grid.Column、Grid.RowSpan、Grid.ColumnSpan设置自己所在的行、列、纵向跨几行、横向跨几列。

 · 可以设置子元素的对齐方向。

    <Grid VerticalAlignment="Top" HorizontalAlignment="Left" ShowGridLines="True" Width="250" Height="100">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*" />
<RowDefinition Height="1.5*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions> <TextBlock FontSize="20" FontWeight="Bold" Grid.ColumnSpan="3" Grid.Row="0">2013 Products Shipped</TextBlock>
<TextBlock FontSize="12" FontWeight="Bold" Grid.Row="1" Grid.Column="0" HorizontalAlignment="Center">Quarter 1</TextBlock>
<TextBlock FontSize="12" FontWeight="Bold" Grid.Row="1" Grid.Column="1" HorizontalAlignment="Center">Quarter 2</TextBlock>
<TextBlock FontSize="12" FontWeight="Bold" Grid.Row="1" Grid.Column="2" HorizontalAlignment="Center">Quarter 3</TextBlock>
<TextBlock Grid.Row="2" Grid.Column="0" VerticalAlignment="Center">50000</TextBlock>
<TextBlock Grid.Row="2" Grid.Column="1" VerticalAlignment="Center">100000</TextBlock>
<TextBlock Grid.Row="2" Grid.Column="2" VerticalAlignment="Center">150000</TextBlock>
<TextBlock FontSize="16" FontWeight="Bold" Grid.ColumnSpan="3" Grid.Row="3">Total Units: 300000</TextBlock>
</Grid>

在这个示例中,我们还设置了ShowGridLines属性,它会生成虚线的边框。我们可以修改该边框,具体可以查看陈希章老师的这篇文章:为WPF和Silverlight的Grid添加边框。

我们还可以使用UseLayoutRounding属性来控制布局舍入。

我们还可以使用GridSplitter来拖动分割窗口。

    <Grid>
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="200"></ColumnDefinition>
<ColumnDefinition Width="Auto"></ColumnDefinition>
<ColumnDefinition Width="200"></ColumnDefinition>
</Grid.ColumnDefinitions> <Button>1</Button>
<Button Grid.Column="2">2</Button>
<Button Grid.Row="2">3</Button>
<Button Grid.Column="2" Grid.Row="2">4</Button> <GridSplitter Grid.Column="1" Grid.Row="0" Grid.RowSpan="3" Width="2" VerticalAlignment="Stretch" HorizontalAlignment="Center" ShowsPreview="True"></GridSplitter> </Grid>

这是一个左右拖动的分割窗口,所以我们把GridSplitter竖立放置铺满(VerticalAlignment="Stretch"),并跨行(Grid.RowSpan="3"),还要设置它的宽度Width(这样才可见);最后还要设置列的宽度为自动值Auto。我们还看到ShowsPreview属性为True,这个是当我们拖动分割条时,会有一个会射的阴影跟随鼠标运行,以显示预览。同理,我可以设置上下拖动的分割线,这里就不过多叙述了。

还有一个比较有意思的是SharedSizeGroup特性。

    <Grid Margin="3">
<StackPanel>
<StackPanel Grid.IsSharedSizeScope="True">
<Grid Grid.Row="0" Margin="3" Background="LightYellow" ShowGridLines="True">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" SharedSizeGroup="TextLabel"></ColumnDefinition>
<ColumnDefinition Width="Auto"></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions> <Label Margin="5">A very long bit of text</Label>
<Label Grid.Column="1" Margin="5">More text</Label>
<TextBox Grid.Column="2" Margin="5">A text box</TextBox>
</Grid>
<Label Grid.Row="1" >Some text in between the two grids...</Label>
<Grid Grid.Row="2" Margin="3" Background="LightYellow" ShowGridLines="True">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" SharedSizeGroup="TextLabel"></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions> <Label Margin="5">Short</Label>
<TextBox Grid.Column="1" Margin="5">A text box</TextBox>
</Grid>
</StackPanel>
</StackPanel>
</Grid>

我们可以设置需要共享尺寸的列ColumnDefinition或者行RowDefinition的SharedSizeGroup属性,把他们都设置成同一个值;然后再把Grid的IsSharedSizeScope附加属性设置到父容器上,并设置为True。(本例嵌套了2个StackPanel就是为了说明IsSharedSizeScope的设置方式,其实完全没有必要嵌套2个StackPanel,直接就可以设置在Grid上)

还有一个更简单的网格布局容器,UniformGrid,它可以直接设置Rows、Columns属性,设置行列数;它没有Rows、Columns这些附加属性,是按照子元素的添加的顺序由左到右,由上到下排列的。

Canvas

直译就是画布的意思。它通常用于一些设计基本不会再有改动的小型布局。

Canvas布局容器是最轻量级的布局容器,它没有包含负责的布局逻辑,以改变其子元素的首选尺寸。

我们可以设置Canvas.Left、Canvas.Top(或者Canvas.Right、Canvas.Buttom)附加属性来定位子元素的位置。

    <Canvas>
<Button Canvas.Left="10" Canvas.Top="10">(10,10)</Button>
<Button Canvas.Left="120" Canvas.Top="30">(120,30)</Button>
<Button Canvas.Left="60" Canvas.Top="80" Width="50" Height="50" Canvas.ZIndex="1">(60,80)</Button>
<Button Canvas.Left="70" Canvas.Top="120" Width="100" Height="50">(70,120)</Button>
<Button Canvas.Left="80" Canvas.Top="150" Width="100" Height="50">(60,80)</Button>
</Canvas>

默认情况下,所有子元素的都具有相同的ZIndex属性值0;如果子元素的ZIndex值相同,就按照他们在Canvas中添加的先后顺序进行显示。所以上一个示例中,最后一个Button会显示在倒数第二个Button上面;第三个因为我们显示设置了ZIndex的值为1,大于默认值0,所以第三个在第四个上面。

这一节我们讲了3个非常具有代表性的布局容器,当然需要做好布局是一件非常困难的事情,需要考虑全面。所以还需要我们真正动手做的时候,不断的积累经验。这里只是一个简单的介绍布局容器。下一节,我们会来介绍一下WPF的控件。

浅谈 WPF布局的更多相关文章

  1. 浅谈WPF页间导航

    浅谈WPF页间导航 使用导航的目的是从一个页面进入到另一个页面.无论是预先决定的线性顺序(向导)还是基于层次的用户驱动程序(大部分网站的形式),或者动态生成的路径,主要有3种方法实现:调用Naviga ...

  2. 浅谈WPF依赖项属性

    浅谈WPF依赖项属性 0. 引言 依赖项属性虽然在使用上和CLR属性一样,但是它是WPF特有的,不同于CLR属性.只是封装为我们常用CLR的属性,在语法使用上和CLR属性一样.WPF中一些功能:动画, ...

  3. 浅谈 Qt 布局那些事

    Qt 布局那些事是本文介绍的内容,直接进入主题.GridLayout是一个非常强大的布局管理器,它可以实现很多复杂的布局,名字中暗示它将所有控件放置在类似网格的布局中.^__^GridLayout有两 ...

  4. 浅谈WPF中对控件的位图特效(WPF Bitmap Effects)

    原文:浅谈WPF中对控件的位图特效(WPF Bitmap Effects) -------------------------------------------------------------- ...

  5. 浅谈WPF本质中的数据和行为

    WPF缩写为Windows Presentation Foundation的缩写,本文所要谈的就是WPF本质中的数据和行为,希望通过本文能对大家了解WPF本质有所帮助. 如果自己来做一个UI框架,我们 ...

  6. 浅谈 WPF控件

    首先我们必须知道在WPF中,控件通常被描述为和用户交互的元素,也就是能够接收焦点并响应键盘.鼠标输入的元素.我们可以把控件想象成一个容器,容器里装的东西就是它的内容.控件的内容可以是数据,也可以是控件 ...

  7. 浅谈qt 布局器

    在一个颜值当道的今天,无论买衣服,买车还是追星,颜值的高低已经变成了大家最看重的(不管男性女性都一样,千万别和我说你不是):而对于程序猿来说,开发一款软件,不再只注重逻辑和稳定性,美观和用户友好性也是 ...

  8. 浅谈CSS布局

    在No.4中谈及了下盒子模型,引出布局模型 1.布局模型有三类: 1)流动模型  flow(默认) 2)浮动模型  float 3)层模型  layer 2.文档流 :指的是文本沿着从左到右的方向展开 ...

  9. 浅谈Android布局

    在前面的博客中,小编介绍了Android的极光推送以及如何实现登录的一个小demo,对于xml布局页面,摆控件这块的内容,小编还不是很熟练,今天小编主要简单总结一下在Android中的布局,学习过An ...

随机推荐

  1. CSS/CSS3 如何实现元素水平居中

    更新:可直接访问 [CSS/CSS3 如何实现元素水平居中] 查看效果,右键查看源代码 -------------------------------------------------分割线---- ...

  2. Ralink RT3290无线网卡驱动安装 (linux)

    Ralink RT3290无线网卡驱动安装 (linux, 笔记备忘) 1. 设备信息查看无线网卡设备信息 # lspci : 2. 驱动下载http://pan.baidu.com/s/1sjsHN ...

  3. php的一些简单算法程序(冒泡、快速等)

    冒泡排序: function buttle_sort($array) { $len=count($array); if($len<2){ return $array; } for($i=0;$i ...

  4. CF Set of Strings

    Set of Strings time limit per test 1 second memory limit per test 256 megabytes input standard input ...

  5. HDU 2571 命运 (DP)

    命运 Time Limit:1000MS     Memory Limit:32768KB     64bit IO Format:%I64d & %I64u Submit Status Pr ...

  6. python之supervisord启动脚本

    Supervisord是用Python实现的一款非常实用的进程管理工具,在批量服务化管理时特别有效.可以将非Daemon的应用转为daemon程序.关于supervisord的安装和配置,在网上已经有 ...

  7. GSS7 spoj 6779. Can you answer these queries VII 树链剖分+线段树

    GSS7Can you answer these queries VII 给出一棵树,树的节点有权值,有两种操作: 1.询问节点x,y的路径上最大子段和,可以为空 2.把节点x,y的路径上所有节点的权 ...

  8. 编译项目报错: Ignoring file / xxx , missing required architecture i386 in file / xxx (2 slices)

    .lib 或者 .a需用于真机版本,也就是ARM7的,如果你编译的是模拟器就会出现这个错误: 选择真机调试即可 .

  9. OpenShare常见问题及解答

    OpenShare常见问题及回答: Q:OpenShare可以整合SAP么? A:当然可以,OpenShare是真正完全开放的产品,但要进行二次开发,事实上我们帮我们大部分的客户都整合了SAP,包括数 ...

  10. Java类加载的时机_4种主动引用会触犯类加载+剩下的被动引用不会触发类的加载

    转载自:http://chenzhou123520.iteye.com/blog/1597597 Java虚拟机规范没有强制性约束在什么时候开始类加载过程,但是对于初始化阶段,虚拟机规范则严格规定了有 ...