一、WPF 排版基础

WPF使用控制面板来进行排版,控制面板实际上是一种可以放入WPF界面元素的容器。当用户把界面元素放入控制面板后,WPF会自动把这些界面元素放在它认为合适的地方。WPF开发人员需要根据自己对用户界面的要求来选择合适的控制面板。

WPF中的基本控制面板类如图3-1所示,这些类都是从Panel类中派生出来的,Panel本身是UIElement。

WPF中的StackPanel、DockPanel、WrapPanel及Grid则支持另外一种排版机制,使用这些排版类,不需要设置控件在视窗上的绝对位置,只需要说明其相对位置,WPF的控制面板类负责最终确定这些控件的位置。

控制面板中的Children属性是一个UIElement的集合,即所有从UIElement中派生出来的UI元素都可以加入到控制面板中,也就是说,控制面板中既可以放入各种图形,又可以放入像Button、TextBlock这样的常规控件。由于Panel本身是从UIElement中派生出来的,所以Panel可以含有Panel。

二、堆积面板(StackPanel)

StackPanel是最简单的一种控制面板,它把其中的UI元素按横向或纵向堆积排列。

其中,可以使用Orientation属性来操作子元素的排列。

Orientation的属性有两个值:Vertical和Horizontal。

当Orientation设为Vertical时,StackPanel中的元素占满StackPanel的水平方向空间,即其中元素的宽度Width是相同的。当Orientation设为Horizontal时,StackPanel中的元素则占满垂直方向空间,其中元素的高度Height都相同。

StackPanel里还有一个属性:LayoutTransform。这个属性可以把StackPanel内的UI元素一起放大、缩小或旋转。

1.在XAML中使用StackPanel

  1. <Window x:Class="WpfApp.MainWindow"
  2. xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  3. xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  4. xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
  5. xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
  6. xmlns:local="clr-namespace:WpfApp"
  7. Icon="https://demosc.chinaz.net/Files/pic/iconsico/8254/e10.ico"
  8. mc:Ignorable="d"
  9. Title="无题" Height="200" Width="200">
  10. <ScrollViewer VerticalScrollBarVisibility="Auto">
  11. <StackPanel Margin="5" Orientation="Vertical">
  12. <TextBlock FontSize="20" Text="昨夜星辰昨夜风," Background="Wheat"/>
  13. <TextBlock FontSize="20" Text="画楼西畔桂堂东。" Background="Wheat"/>
  14. <TextBlock FontSize="20" Text="身无彩凤双飞翼," Background="Wheat"/>
  15. <TextBlock FontSize="20" Text="心有灵犀一点通。" Background="Wheat"/>
  16. </StackPanel>
  17. </ScrollViewer>
  18. </Window>

2.在C#中使用StackPanel

StackPanel和其中元素间的父子关系(这里的父子关系指的是一种包容关系,而不是继承关系)更加清晰。

  1. public partial class MainWindow : Window
  2. {
  3. public MainWindow()
  4. {
  5. InitializeComponent();
  6. this.Title = "无题";
  7. StackPanel sp = new StackPanel();
  8. this.Content = sp;
  9. sp.Children.Add(CreatTextBlock("昨夜星辰昨夜风,"));
  10. sp.Children.Add(CreatTextBlock("画楼西畔桂堂东。"));
  11. sp.Children.Add(CreatTextBlock("身无彩凤双飞翼,"));
  12. sp.Children.Add(CreatTextBlock("心有灵犀一点通。"));
  13. }
  14. private TextBlock CreatTextBlock(String text)
  15. {
  16. TextBlock textBlock = new TextBlock();
  17. textBlock.FontSize = 20;
  18. textBlock.Foreground = new SolidColorBrush(Color.FromRgb(200, 100, 100));
  19. textBlock.Text = text;
  20. return textBlock;
  21. }
  22. }

有时候需要把视窗的大小根据其中的内容来自动调整,方法是设置视窗的SizeToContent属性. SizeToContent是枚举类型。

若你对StackPanel和窗口间或UI元素与UI元素间的距离不满意,可以调整StackPanel或界面元素的Margin属性。

Margin的类型为Thickness,可以对其左、右、上、下分别设置。

上面谈到当视窗里的内容比视窗小时,可以用设置视窗的SizeToContent的属性让视窗根据其中的内容进行自动调整。但更常遇到的是另外一种情况:视窗里的内容比计算机可用屏幕的尺寸还要大,这时候就要在视窗内加滚动条。

WPF提供ScrollBar和ScrollViewer来实现屏幕滚动,ScrollViewer比ScrollBar在滚动视窗中的内容时用起来方便。

ScrollViewer有两个控制滚动条显示方式的相关属性:HorizontalScrollBarVisibility和VerticalScrollBarVisibility。

VerticalScrollBarVisibility用来控制竖直滚动条,而HorizontalScrollBarVisibility则用来控制水平滚动条。

HorizontalScrollBarVisibility的默认值是Disbaled,而VerticalScrollBarVisibility的默认值是“auto”。

ScrollViewer提供了8个用于控制每次滚动范围的方法:LineUp、LineDown、LineLeft、LineRight、PageUp、PageDown、PageLeft和PageRight这些方法可以在程序中模拟人工操作滚动条。

当视窗的大小,滚动条的位置或视点发生改变时,ScrollViewer会产生ScrollChanged事件。

三、WrapPanel

WrapPanel是和StackPanel最相近的一个控制面板,StackPanel把其中的UI元素按行或列排列,而WrapPanel则可根据其中UI元素的尺寸和其自身可能的大小自动地把其中的UI元素排列到下一行或下一列。Windows操作系统中的文件管理器就是这样的例子,当选择图标显示模式时,文件管理器将根据视窗的宽度和文件图标的大小,自动布置文件在其中的位置。

WrapPanel中有三个属性ItemWidth、ItemHeight和Orientation,这三个属性来安排其中UI元素的位置:

● ItemWidth:定义所有子元素的宽度。每个子元素在其中显示的宽度由子元素自己的Width及HorizontalAlignment属性确定,若子元素的宽度大于ItemWidth,WrapPanel就会自动剪掉子元素超过ItemWidth的部分。ItemWidth的默认值为NaN,在这种情况下,WrapPanel使用其中最大子元素的宽度来作为列的宽度。

● ItemHeight:定义所有子元素的高度。每个子元素在其中显示的高度由子元素自己的Height及VerticalAlignment确定,若子元素的宽度大于ItemHeight,WrapPanel就会自动剪掉子元素超过ItemHeight的部分。ItemHeight的默认值为NaN,在这种情况下,WrapPanel使用其中最大子元素的高度来作为行的高度。

● Orientation:和StackPanel相同,唯一的区别是在WrapPanel中,Orientation的默认值为Horizontal(水平放置)。

对照一下与前面StackPanel的区别:

a.可用鼠标调整视窗的大小,当视窗的宽度只能放置1个矩形时,WrapPanel的效果和StackPanel的效果一样。

b.当视窗的宽度小于其中矩形宽度的总和时,排在右面的矩形会自动排列到下一行;当视窗的宽度大于其中矩形宽度的总和时,排在下一行最左边的矩形会自动进入上一行。

四、停靠面板(DockPanel)

停靠(Dock)这个概念并不是新的东西,在传统的视窗应用程序中,通常在视窗的顶端有菜单,最下面有状态行,有时还有工具条,工具条一般放在菜单的下面等。控制这些UI元素在视窗中的位置就是停靠,只不过在WPF之前,需要自己做大量工作,或使用第三方提供的工具。

DockPanel定义一个区域,在此区域中,您可以使子元素通过描点的形式排列,这些对象位于 Children 属性中。停靠面板类似于WinForm中控件的Dock属性。DockPanel会对每个子元素进行排序,并将根据指定的边进行停靠,多个停靠在同侧的元素则按顺序排序。在DockPanel中,指定停靠边的控件,会根据定义的顺序占领边角,所有控件绝不会交叠。

默认情况下,后添加的元素只能使用剩余空间,无论对DockPanel的最后一个子元素设置任何停靠值,该子元素都将始终填满剩余的空间。如果不希望最后一个元素填充剩余区域,可以将DockPanel属性LastChildFill设置为false,还必须为最后一个子元素显式指定停靠方向。

WPF的DockPanel定义了一个Dock附加属性,其类型为Dock,是枚举类型,可取Left、Right、Top和Bottom四个值。注意Dock并没有一个Fill或Center的值,当LastChildFill属性设为True时,DockPanel用最后一个加入的UI元素填充所有剩下的地方。

在XAML中设置UI元素的语法为(以TextBlock为例,其他UI元素都一样):

  1. <TextBlock DockPanel.Dock=Dock.Top>
  2. ...
  3. </TextBlock>

在C#里,可以用两种方法来设定UI元素在DockPanel中的停靠位置,一种是用UI元素中的SetValue()方法,比如设定控件myControl的停靠位置:

  1. myControl.SetValue(DockPanel.DockProperty,Dock.Top);

另外一种方法是在DockPanel里设置:

  1. DockPanel.SetDock(myControl,Dock.Top);

实例:

点击查看代码
  1. <Window x:Class="WpfApp.MainWindow"
  2. xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  3. xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  4. xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
  5. xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
  6. xmlns:local="clr-namespace:WpfApp"
  7. Icon="https://demosc.chinaz.net/Files/pic/iconsico/8254/e10.ico"
  8. mc:Ignorable="d"
  9. Title="无题" Height="500" Width="800" WindowStartupLocation="CenterScreen">
  10. <DockPanel>
  11. <Button DockPanel.Dock="Left" Content="ButtonLeft"></Button>
  12. <Button DockPanel.Dock="Top" Content="ButtonTop"></Button>
  13. <Button DockPanel.Dock="Right" Content="ButtonRight"></Button>
  14. <Button DockPanel.Dock="Bottom" Content="ButtonBottom"></Button>
  15. <Button Content="ButtonTop"></Button>
  16. </DockPanel>
  17. </Window>

六、表格式面板(Grid)

Grid控制面板是WPF中最常用的控制面板,当用Visual Studio或ExpressBlend创建WPF程序时,Visual Studio会自动在XAML中添加标记。

Grid排版和HTML表格排版是一样的,尽管现在有无数的网站,提供的内容丰富多彩,但网页版面设计都是基于表格排版的。

WPF中还有一个和Grid的类似的类——Table。Table类常用于文档,而Grid则多用于用户界面。

在XAML中定义Grid的语法为:

  1. <Grid>
  2. <Grid.RowDefinitions>
  3. <RowDefinition/>
  4. <RowDefinition/>
  5. <RowDefinition/>
  6. </Grid.RowDefinitions>
  7. <Grid.ColumnDefinitions>
  8. <ColumnDefinition/>
  9. <ColumnDefinition/>
  10. <ColumnDefinition/>
  11. </Grid.ColumnDefinitions>
  12. </Grid>

上面的这段XAML定义了3行3列的Grid 。在C#里创建Grid的语法和创建其他对象一样:

  1. Grid myGrid = new Grid();
  2. for(int i = 0; i < 3; i++)
  3. {
  4. RowDefinition rowdef = new RowDefinition();
  5. myGrid.RowDefinitions.Add(rowdef);
  6. }
  7. for(int j = 0; j < 3; j++)
  8. {
  9. ColumnDefinition coldef = new ColumnDefinition();
  10. myGrid.ColumnDefinitions.Add(coldef);
  11. }

Grid类中含有行容器RowDefinitions和列容器ColumnDefinitions。

行和列分别用RowDefinition和ColumnDefinition类来表达。

1.设定UI元素在Grid中的位置

设定Grid中UI元素在Grid中的相对位置要使用Grid的两个附加属性Row和Column,若Grid里面含有Label,则使用XAML设置Label的行列位置的语法为:

  1. <Label Grid.Row="1" Grid.Column="1" VerticalContentAlignment="Center" FontSize="20">I'm here.</Label>

上面的XAML句子把标签“I'm here.”放在Grid的第二行、第二列的位置。

注意:Grid中的行列号为从0开始的整数,设定UI元素在Grid中的行列号,该行列号一定要在Grid所定义的范围内;换句话说,若你定义了3行3列的Grid,则不能设定UI元素的行列号为大于或等于3。

2.设定Grid行或列的尺寸

和普通UI元素可以设定Height和Width属性不同,RowDefinition类和ColumnDefinition类中相应的属性的类型不是Double,而是System.Window.GridLength。

Grid调整Grid行列尺寸的方法有如下三种:

● 绝对尺寸 把Grid行列大小尺寸设为一个数值,这时Grid的行列尺寸不会随着其中UI元素的大小进行自动调整。

● 自动尺寸 把Grid的Height和Width设为Auto。这时WPF会根据Grid中的UI元素自动调整其行列的高度或宽度。其原则为:Grid的行高度由该行中元素的最大高度决定,Grid的列宽度由该列中元素的最大宽度决定。使用这种方法可以保证Grid中的UI元素不会只显示一部分。

● 按比例分割行列尺寸 把有限的平面大小按照一定的比例划分行的高度或列的宽度,其比例的数值可以是浮点数。如第一列的宽度设为“”、第二列的宽度设为“1.2”、第三列的宽度设为“2.5*”,等等。

WPF默认设置行的高度和列的宽度为1个*,即每行或每列的大小一样。在XAML中设定行列尺寸的语法:

  1. <RowDefinition Height="100"/> (绝对比例)
  2. <RowDefinition Height="Auto"/> (自动尺寸)
  3. <RowDefinition Height="2*"/> (按比例尺寸)

在C#中设定行列尺寸的语法:

  1. RowDefinition rowdef = new RowDefinition();
  2. rowdef.Height = new GridLength(100, GridUnitType.Pixel); (绝对比例)
  3. rowdef.Height = GridLength.Auto; (自动尺寸)
  4. rowdef.Height = new GridLength(2, GridUnitType.Star); (按比例尺寸)

实例:

3. 元素横跨多个行列时的设定

有时候需要设置某个UI元素横跨多行或多列,Grid支持这种操作。

用XAML设置跨行和跨列的例子如下:

  1. <Label Grid.Row="1" Grid.Column="1" Grid.ColumnSpan="2" Grid.RowSpan="2" VerticalContentAlignment="Center" FontSize="40">I'm here.~~~~~</Label>

用C#设置跨行和跨列的例子如下:

  1. TextBlock tb = new TextBlock();
  2. tb.Width = 100;
  3. tb.Height = 100;
  4. Grid.SetRowSpan(tb, 2); // 跨越两行
  5. Grid.SetColumnSpan(tb, 2); // 跨越两列

4.在Grid中保持多行或多列大小的一致性

有时候希望Grid中的一些行或列无论在什么情况下都保持相同的高度或宽度,使用RowDefinition和ColumnDefinition中的SharedSizedGroup可以达到这一目的。

首先在Grid层面设置了一个IsSharedSizeScope属性为True,表明在Grid中,会有行或列使用SharedSizeGroup属性。在ColumnDefinition中,笔者把第1列和第3列的SharedSizeGroup设为“mgroup1”,把第2列和第4列的SharedSize-Group设为“mgroup2”。SharedSizedGroup可以设为任何字符串,具有相同Shared-SizedGrou字符串的行或列,其高度或宽度相同。字符串大小写敏感,如mgroup1和mGroup1是不同的字符串。

设定SharedSizeGroup后,位于同一组中的列的宽度保持相同的比例。

六、UniformGrid

有时候觉得使用Grid过于烦琐的话,可以设置行列数,行列的宽度或高度等属性,实际上就是需要一种大小相等在平面上均匀排列的表格。UniformGrid支持这种表格的排版类,它是一般Grid的一个特例。这时,不必要定义行列的集合;不必设定每行的列数或行数,UniformGrid总是把行数和列数设为相等;每个单元只含有一个子元素,所以也不必用附加属性来说明哪个元素位于哪个单元。

七、画布面板(Canvas)

过去视窗界面排版都是用绝对坐标,比如,用户设计的对话框,每个控件在对话框中的位置,都是用x,y值确定的;又比如用户要在窗口内显示文字或图形,也要指出所要显示图素的确切位置。WPF也支持这种精确定位的排版,Canvas面板就是为此设计的。

Canvas中的坐标值是一种与设备无关的单位值,其坐标原点位于Canvas的左上角。X坐标轴从原点指向屏幕的右边,Y坐标轴从原点指向屏幕的下方。UI元素并没有指定其位置的坐标属性,要把某个元素放在Canvas上的某个位置,需要使用Canvas的Left和Top附加属性。

XAML:

  1. <Canvas>
  2. <TextBlock Canvas.Left="15" Canvas.Top="15" Background="Aquamarine">Location1</TextBlock>
  3. <TextBlock Canvas.Left="100" Canvas.Top="100" Background="Bisque">Location2</TextBlock>
  4. </Canvas>

C#:

  1. public MainWindow()
  2. {
  3. InitializeComponent();
  4. TextBlock tb1 = new TextBlock();
  5. tb1.Text = "Location1";
  6. tb1.Background = new SolidColorBrush(Color.FromRgb(0, 255, 0));
  7. Canvas.SetLeft(tb1, 15);
  8. Canvas.SetTop(tb1,15);
  9. TextBlock tb2 = new TextBlock();
  10. tb2.Text = "Location2";
  11. tb2.Background = new SolidColorBrush(Color.FromRgb(0, 255, 0));
  12. Canvas.SetLeft(tb2, 100);
  13. Canvas.SetTop(tb2, 100);
  14. Canvas cv = new Canvas();
  15. cv.Width = 400;
  16. cv.Height = 400;
  17. cv.Background = new SolidColorBrush(Color.FromRgb(255,200,0));
  18. cv.Children.Add(tb1);
  19. cv.Children.Add(tb2);
  20. this.AddChild(cv);
  21. }

在WPF中,Canvas排版常用在对图形元素的版面布置上。虽然可以设置图形元素在Canvas上的左上角或右下角的位置,但若设置了图形元素的左上角的位置,同时又设置了图形元素右下角的位置,WPF就会自动忽略所设的右下角的位置坐标。

七、小结

本节介绍了WPF中的排版系统,重点在StackPanel、WrapPanel、DockPanel、Grid、UniformGrid等元素的使用方法。WPF允许这些面板类可以互相包含,甚至在UI元素(如Button)中也可以包含面板元素作为其自身的子元素,这些排版元素组合起来使得WPF很容易支持各种版面设计。还有与排版相关的一些UI类,如LayoutTransform,该类支持FrameWorkElement在版面上的变换。还有一些处在控件和版面设计之间的类,如TabControl;使用TabControl的例子如IE 7.0。这个类可用作版面设计,但在属性上应把它归于UI控件元素。

WPF 排版基础的更多相关文章

  1. WPF排版布局经验总结(干货)简短不疲倦

    本文不过多讲述wpf的基础布局控件,本文只记录WPF排版的技巧,这是个人的总结,不能符合所有情况,如果有何不对的地方,请评论指正,谢谢. 1.区域划分 在接手一个界面的时候,先纵观全局,将眼见的区域划 ...

  2. 【笔记-前端】div+css排版基础,以及错误记录

    现在的网站对于前端的排版已经逐渐不使用<table>,而是使用div+css. 使用这种方法的最大好处就在于在维护页面时,可以只维护css而不去改动html. 可是这种方式对于初学者来说可 ...

  3. [WPF系列]基础学习(一) WPF是什么?

    引言 学习之前,我们首先大概了解下WPF诞生的背景以及它所能解决的问题或者新颖之处.WPF作为微软新一代的用户界面技术,   WPF简介 WPF的全称是WindowsPresentationFound ...

  4. Bootstrap文本排版基础--Bootsrap

    1.排版前的基础 (1)移动设备优先 <meta name="viewport" content="width=device-width, initial-scal ...

  5. WPF动画基础及实例

    1.介绍 在之前做winform中, 也做过一些动画效果, 但是整个动画都需要我们自己去编写, 利用计时器或线程去直接操作UI元素的属性, 然而在WPF中, 则是通过一种全新的基于属性的动画系统, 改 ...

  6. [WPF系列]基础Combox

    示例     参考 WPF combobox SelectedValue binding to string Confused with wpf ComboBox DisplayMemberPath, ...

  7. [WPF系列]-基础系列 Property Trigger, DataTrigger & EventTrigger

    So far, we worked with styles by setting a static value for a specific property. However, using trig ...

  8. [WPF系列]-基础系列 TabControl应用

    引言 Tabcontrol控件也是我们在项目中经常用到的一个控件,用它将相关的信息组织在一起分类显示. 简介     ========================================= ...

  9. WPF 命令基础

    1命令的组成 命令源:就是谁发送的命令. 命令目标:就是这个命令发送给谁,谁接受的命令. 命令:就是命令的内容. 命令关联:就是把命令和外围的逻辑关联起来,主要用来判断命令是否可以执行和执行完以后干点 ...

随机推荐

  1. Golang入门学习(五):异常处理

    文章目录 2.5 错误处理机制 2.5.1 基本说明 2.5.2 入门示例 2.5.3 自定义错误 2.5 错误处理机制 2.5.1 基本说明 Go语言追求简洁优雅,因此并不支持传统的try-catc ...

  2. python中的getpass模块问题,在pycharm中不能继续输入密码

    python中getpass模块   在pycharm中运行下面的代码: 1 import getpass 2 name = input('请输入你的名字:') 3 passwd = getpass. ...

  3. 聊一聊开闭原则(OCP).

    目录 简述 最早提出(梅耶开闭原则) 重新定义(多态开闭原则) 深入探讨 OCP的两个特点 对外扩展开放(Open for extension) 对内修改关闭 抽象 关闭修改.对外扩展? 简述 在面向 ...

  4. PHP设计模式之命令模式

    命令模式,也称为动作或者事务模式,很多教材会用饭馆来举例.作为顾客的我们是命令的下达者,服务员是这个命令的接收者,菜单是这个实际的命令,而厨师是这个命令的执行者.那么,这个模式解决了什么呢?当你要修改 ...

  5. 让PHP能够调用C的函数-FFI扩展

    在大型公司中,一般会有很我编程语言的配合.比如说让 Java 来做微服务层,用 C++ 来进行底层运算,用 PHP 来做中间层,最后使用 JS 展现效果.这些语言间的配合大部分都是通过 RPC 来完成 ...

  6. win8 连接到OneDrive时出现问题-感叹号

    显示最后更新时间是1970年... 还有感叹号,没法同步 解决办法: 管理员运行cmd命令: 输入"netsh int ip reset c: esetlog.txt",按下回车键 ...

  7. Linux系列(5) - 目录处理命令(2)

    删除空目录: rmdir rmdir [目录名] 删除文件或目录: rm rm  -rf  [文件或目录] 选项 -r            删除目录 -f                   强制 ...

  8. Android Kotlin协程入门

    Android官方推荐使用协程来处理异步问题.以下是协程的特点: 轻量:单个线程上可运行多个协程.协程支持挂起,不会使正在运行协程的线程阻塞.挂起比阻塞节省内存,且支持多个并行操作. 内存泄漏更少:使 ...

  9. 关于go mod 的使用和goland 配置 go mod

    一.关于go modules 1.1 go modules 是go1.11 新加的特性 现在已有go 1.13.4 了本人用了就是最新版的 1.2关于modules 官方定义 模块是相关Go包的集合. ...

  10. NOI.AC#2266-Bacteria【根号分治,倍增】

    正题 题目链接:http://noi.ac/problem/2266 题目大意 给出\(n\)个点的一棵树,有一些边上有中转站(边长度为\(2\),中间有一个中转站),否则就是边长为\(1\). \( ...