1:控件到底是什么
(1)布局控件:可以容纳多个控件或嵌套其他布局空间,用于在UI上组织和排列的控件、Grid、StackPanel、DockPanel等控件都属此类,它们拥有共同的父类Panel。
(2)内容控件:只能容纳一个其他控件或布局控件作为它的内容。Window、Button等控件属于此类,因为只能容纳一个控件作为其内容,所以经常需要布局空间来规划其内容。它们的共同父类是ContentControl。
(3)带标题内容控件:相当于一个内容控件,但可以加一个标题(Header),标题部分亦可容纳一个控件或布局。CroupBox、TabItem等是这类控件的典型代表。它们共同的父类是HeaderedContentControl。
(4)条目控件:可以显示一列数据,一般情况下这列数据的类型相同。此类控件包括ListBox、ComboBox等。他们共同的基类是ItemControl。此类控件在显示集合类型数据方面功能非常强大。
(5)带标题条目控件:相当于一个条目控件加上一个标题显示区。TreeViewItem、MenuItem都属于此类控件。这类控件往往用于显示层级关系数据,节点显示在其Header区域,子级节点则显示在其条目控件区域。此类控件的共同基类是HeaderedItemControl。
(6)特殊内容控件:比如TextBox容纳的是字符串、TextBlock可以容纳可自由控制格式的文本、Image容纳图片类型数据,这类控件相对比较独立。
2:WPF的内容模型
我们可以把控件想象成一个容器,容器里装的的东西就是它的内容。控件的内容可以直接是数据,也可以是控件。当控件的内容还是控件的时候就形成了控件的嵌套。我们把被嵌套的控件称为子级控件,这种控件嵌套在UI布局时尤为常见。因为允许控件嵌套,所以WPF的UI会形成一个树形结构。如果不考虑控件内部的组成结构,只观察由控件组成的“树”,那么这棵树称为逻辑树;WPF控件往往是由更基本的控件构成,即控件本身就是一棵树,如果连控件本身的树也在考虑之内,则这棵比逻辑树更“繁茂”的树称为可视元素树。
控件是内存中的对象,控件的内容也是内存中的对象。控件通过自己的某个属性引用着作为其内容的对象,这个属性称为内容属性。“内容属性”是个统称,具体到每种控件上,内容属性都有自己确切的名字——有的直接叫Content,有的叫Child;有些控件内容可以是集合,其内容属性有叫Items或Children的。
控件的内容属性与XAML标签的内容存在一定的对应关系。
3:各类内容模型详解
我们把符合某类内容模型的UI元素称为一个族,每个族用它们共同的基类来命名。
1:ContentControl族
本族元素的特点如下:
(1)均派生自ContentControl类。
(2)它们都是控件。
(3)内容属性的名称为Content
(4)只能由单一元素充当其内容。
本族元素的特点如下:
(1)它们都派生自HeaderedContentControl类,HeaderedContentControl是ContentControl类的派生类。
(2)它们都是控件,用于显示带标题的数据。
(3)除了用于显示主体内容的区域外,控件还具有一个显示标题(Header)的区域。
(4)内容属性为Content和Header。
(5)无论是Content还是Header都只能容纳一个元素作为其内容。
3:ItemsControl族
本族元素的特点如下:
(1)均派生自ItemsControl类。
(2)它们都是控件,用于显示列表化的数据。
(3)内容属性为Items或ItemsSource。
(4)每种ItemsControl都对应有自己的条目容器。
本族控件最有特色的一点就是会自动使用条目容器对提交给它的内容进行包装。合法的ItemsControl内容一定是个集合,当我们把这个集合作为内容提交给ItemsControl时,ItemsControl不会把这个集合直接拿来用,而是使用自己对用的条目容器把集合中的条目逐个包装,然后再把包装好的条目序列当做自己的内容。
DisplayMemberPath这个属性告诉ListBox显示每条数据的哪个属性,换句话说,ListBox会去调用这个属性的ToString()方法,把得到的字符串放入一个TextBlock,然后再把TextBlock包装进一个ListBoxItem里。
ListBox的SelectValuePath属性将与其SelectValue属性配合使用。当你调用SelectValue属性时,ListBox先找到选中的Item所对应的数据对象,然后把SelectValuePath的值当作数据对象的属性名称并把这个属性的值取出来。
DisplayMemberPath和SelectValuePath是两个相当简化的属性。DisplayMemberPath只能显示简单的字符串,想用更加复杂的形式显示数据需要使用DataTemplate。
SelectedValuePath也只能返回单一的值,如果想进行一些复杂的操作,不妨直接使用ListBox的SelectedItem和SelectedItems属性,这两个属性返回的就是数据集合中的对象。
4:HeaderedItemsControl族
本族元素的特点如下:
(1)均派生自HeaderedItemsControl类。
(2)它们都是控件,用于显示列表化的数据,同时可以显示一个标题。
(3)内容属性为Items、ItemsSource和Header。
5:Decorator族
本族中的元素是在UI上起装饰效果的。如可以使用Border元素为一些组织在一起的内容加个边框。如果需要组织在一起的内容能够自由缩放,则可使用ViewBox元素。
本族元素的特点如下:
(1)均派生自Decorator类。
(2)起UI装饰作用。
(3)内容属性为Child。
(4)只能由单一元素充当内容。
6:TextBlock和TextBox
这两个控件最主要的功能是显示文本。TextBlock只能显示文本,不能编辑,所以又称静态文本。TextBox则允许用户编辑其中的内容。TextBlock虽然不能编辑内容,但可以使用丰富的印刷级的格式控制标记显示专业的排版效果。
TextBox不需要太多的格式显示,所以它的内容是简单的字符串,内容属性为Text。
TextBlock由于需要操纵格式,所以内容属性是Inlines,同时,TextBlock也保留一个名为Text的属性,当简单地显示一个字符串时,可以使用这个属性。
7:Shape族元素
友好的用户界面离不开各种图形的搭配,Shape族元素就是专门用来在UI上绘制图形的一类元素。这类元素没有自己的内容,我们可以使用Fill属性为它们设置填充效果,还可以使用Stroke属性为它们设置边线效果。
本族元素的特点如下:
(1)均派生自Shape类。
(2)用于2D图形绘制。
(3)无内容属性。
(4)使用Fill属性设置填充,使用Stroke属性设置边线。
8:Panel族元素
本族元素的特点如下:
(1)均派生自Panel抽象类。
(2)主要功能是控制UI布局。
(3)内容属性为Children。
(4)内容可以是多个元素,Panel元素将控制它们的布局。
对比ItemsControl和Panel元素,虽然内容都可以是多个元素,但ItemsControl强调以列表的形式来展现数据而Panel则强调对包含的元素进行布局,所以ItemsControl的内容属性是Items和ItemsSource而Panel的内容属性为Children。
4:UI布局
WPF作为专门的用户界面技术,布局功能是它的核心功能之一。友好的用户界面和良好的用户体验离不开设计精良的布局。
1:布局元素
WPF的控件有了Content的概念,所以控件与控件之间又多出一种关系——包含。也正是这种以窗体为根的包含关系,整个WPF的UI才形成树形结构,我们称之为可视化树。
WPF中的布局元素有如下几个:
(1)Grid:网格。可以自定义行和列并通过行列的数量、行高和列宽来调整控件的布局。近似于HTML中的Table。
(2)StackPanel:栈式面板。可将包含的元素在竖直或水平方向上排成一条直线,当移除一个元素后,后面的元素会自动向前移动以填充空缺。
(3)Canvas:画布。内部元素可以使用以像素为单位的绝对坐标进行定位。
(4)DockPanel:泊靠式面板。内部元素可以选择泊靠方向。
(5)WrapPanel:自动折行面板。内部元素元素在排满一行后能够进行自动折行。
2:Grid
Grid元素会以网格的形式对内容元素进行布局。
Grid的特点如下:
(1)可以定义任意数量的行和列,非常灵活。
(2)行的高度和列的宽度可以使用绝对数值、相对比例和自动调整的方式进行精确设定,并可设置最大和最小值。
(3)内部元素可以设置自己所在的行和列,还可以设置自己纵向跨几行、横向跨几行。
(4)可以设置Children元素的对齐方向。
Grid适用的场合:
(1)UI布局的大框架设计。
(2)大量UI元素需要成行或成列对齐的情况。
(3)UI整体尺寸改变时,元素需要保持固有的高度和宽度比例。
(4)UI后期可能有较大变更或扩展。
Grid类具有ColumnDefinitions和RowDefinitions两个属性,它们分别是ColumnDefinition和RowDefinition的集合,表示Grid定义了多少列、多少行。
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="160"/>
<ColumnDefinition Width="160"/>
<ColumnDefinition Width="160"/>
<ColumnDefinition Width="160"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="100"/>
<RowDefinition Height="100"/>
<RowDefinition Height="100"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
</Grid>
宽度和高度单位:像素(px)、英寸(in)、厘米(cm)、点(pt),像素是默认单位,可直接写数字。
对于Grid的行高和列宽,可以设置三类值:
(1)绝对值:double数值加单位后缀。
(2)比例值:double数值后加一个星号(*)。
(3)自动值:字符串Auto,
为控件指定行和列遵循以下规则:
(1)行和列都是从0开始计数的。
(2)指定一个控件在某行,就为这个控件的标签添加Grid.Row="行编号"。
(3)指定一个控件在某列,就为此控件添加Grid.Column="列编号"。
(4)若控件需要跨多个行或列,使用Grid.RowSpan="行数"和Grid.ColumnSpan="列数"。
3:StackPanel
StackPanel可以把内部元素在纵向或横向紧凑排列、形成栈式布局。
StackPanel适合的场合有:
(1)同类元素需要紧凑排列。
(2)移除其中的元素后能够自动补缺的布局或者动画。
在实际工作中,我们可以使用Orientation、HorizontalAlignment和VerticalAlignment三个属性组合出各种排列和对齐方式。
4:Canvas
Canvas译成中文就是“画布”,显然,在Canvas里布局就像在画布上火控件一样。
Canvas适用的场合包括:
(1)一经设计基本上不会再有改动和小型布局。
(2)艺术性比较强的布局。
(3)需要大量使用横纵坐标进行绝对点定位的布局。
(4)依赖于横纵坐标的动画。
5:DockPanel
DockPanel内的元素会被附加上DockPanel.Dock这个属性,这个属性的数据类型为Dock枚举。Dock枚举可取Left、Top、Right和Bottom四个值。根据Dock属性值,DockPanel内的元素会向指定方向累积、切分DockPanel内部的剩余可用空间,就像船舶靠岸一样。
DockPanel还有个重要属性——bool类型的LastChildFill,它的默认值为True。当LastChildFill属性的值为True时,DockPanel内最后一个元素的DockPanel.Dock属性值会被忽略,这个元素会把DockPanel内部所有剩余空间充满。
6:WrapPanel
WrapPanel内部采用的是流式布局。WrapPanel使用Orientation属性来控制流延伸的方向,使用HorizontalAlignment和VerticalAlignment两个属性控制内部控件的对齐。在流延伸的方向上,WrapPanel会排列尽可能多的控件,排不下的控件将会新起一行或一列继续排列。
参考教材书:深入浅出WPF 刘铁猛 著