《深刻浅出WPF》学习总结之控件与布局

1、控件究竟是什么

  控件的本质是“数据+算法”——用户输入原始数据,算法处理原始数据并获得结果数据。问题就在于程序如何将结果数据展现给用户。一样一组数据,你可使用LED阵列显示出来,或者是以命令行模式借助各类控制字符(如Tab)对其并输出,但这些都不如图形化用户界面(Graphics User Interface ,GUI)来的友好和方便。GUI是程序界的优胜者,但在Windows上实现图形化界面有不少中方法。每种方法又拥有本身的一套开发理念和工具。每种GUI开发与它的里理念和工具共同组成一种方法论。常见的有:程序员

  1. Windows API (Win API):调用Windows底层绘图函数,使用C语言,最原始也最基础。
  2. Microsoft Foundation Class(MFC):使用C++语法将原始的Win32 API封装成控件类。
  3. Visual Component Library(VCL):Delphi 和C++Builder使用的与MFC相近的控件类库
  4. Vistal Basic+ActiveX控件(VB6):使用组件化的思想把Win API 封装成UI控件,已其多语言共用
  5. Java Swing/AWT:Java SDK中用于跨平台开发GUI程序的控件类库
  6. Windows Form:.NET平台上进行GUI开发的老牌劲旅,彻底组件化但须要.NET运行时支持。
  7. Windows Presentation Foundation(WPF):后起之秀,使用全新的数据驱动UI理念。

  咱们能够对以上方法论分为四代:算法

  1. Win API时代:函数调用+Windows消息处理。
  2. 封装时代:使用面向对象理念把Win API 封装成类:由来自UI的消息驱动程序处理数据。
  3. 组件化时代:使用面向组件理念在类的基础上封装成组件;消息被封装成事件,变成事件驱动。
  4. WPF时代:在组件化的基础上,使用专门的UI设计语言并引入数据驱动UI的理念。

  WPF之因此可以称上最新的一代在于两点:第一,以前几代GUI方法论只能使用编程语言进行UI设计,而WPF具备专门的UI设计的XAML;第二,前几代在UI与数据交互方面是由Windows消息到控件事件一脉相承,始终是把UI控件放在主导地位而把数据放在被动地位,用UI来驱动数据的改变,WPF在事件驱动的基础上引入了数据驱动界面的理念,让数据重回核心地位而让UI回归数据表达者的位置编程

在从Winform转到WPF的学习过程当中,心中必定要树立起这样一个概念——WPF中是数据驱动UI,数据是核心、是主动的。UI从属于数据并表达数据、是被动的。也能够这么理解,当咱们想改变控件上的显示内容时,只须要改变该控件绑定的数据源内容便可。app

  UI的功能始让用户观察和操做数据,为了让用户观察数据,咱们须要用UI元素来显示数据;为了让用户操做数据,咱们须要用UI元素响应用户的操做。WPF把那些可以展现数据、响应用户操做的UI元素称为控件(Control)。控件所展现的数据,咱们称之为控件的“数据内容”,控件在响应用户的操做后会执行本身的一些方法以事件(Event)的形式通知应用程序(开发人员就能够决定如何处理这些事件),咱们称之为控件的“行为”或“算法”内容。可见,WPF中的控件扮演者双重角色、是个很是抽象的概念——Control是数据和行为的载体,而无需具备固定的形象。换句话说,Button之因此是Button不是由于它长得方方正正、显示一串文字而且可以响应用户单击,而是应该倒过来想——凡是符合“能显示一些提示文字(能够是文字、也能够是图片、动画甚至是视频)并能响应用户单击”这一抽象概念的UI元素均可以是Button,至于Button具体长成什么样子(是方是圆、是显示文字仍是显示动画)彻底由它的风格(Style)和模板(Template)来决定。编程语言

在平常的开发工做中咱们打交道的控件大体分为6类,即:函数

  1.   布局控件:能够容纳多个控件或嵌套其余布局控件,用于在UI上组织和排列控件,Grid、StackPanel、DockPanel等控件都属于此类,他们拥有共同的父类Panel。
  2.   内容控件:只能容纳一个其余控件或布局控件做为他的内容。Window、Button等控件属于此类,由于只能容纳一个控件做为其内容,因此常常须要借助布局控件来规划其内容。它们的共同父类是ContentControl。
  3.   带标题内容控件:至关于一个内容控件,可是可加标题(Header),标题部分亦可容纳一个控件或布局。GroupBox、TabItem等是这类控件的典型表明。它们的共同父类是HeaderedContentControl
  4.   条目控件:能够显示一列数据,一把状况下这列数据类型相同。此类控件包括ListBox、Combobox等。它们的共同基类是ItemsControl。此类控件在显示集合类型数据方面功能很是强大。
  5.   带标题条目控件:至关于在一个条目控件加上一个标题显示区。TreeViewItem、MenuItem都属于此类控件。这类控件每每用于显示层级关系数据,结点显示在其Header区域,子级节点则显示在其条目控件区域。此类控件的共同基类是HeaderedItemsControl。
  6.   特殊内容控件:好比TextBox容纳的是字符串、Textblock能够容纳可自由控制格式的文本,Image容纳图片类型数据....这类控件相对比较独立

2、WPF的内容模型

   WPF能够分为以下几类工具

名称 注释
ContentControl 单一内容控件
HeaderedContentControl 带标题的单一内容控件
ItemsControl 以条目集合为内容的控件
HeaderedItemsControl 带标题的以条目集合为内容的控件
Decorator    控件装饰的元素
Panel     面板类元素
Adorner  文字点缀元素
Flow Text 流式文本元素
TextBox 文本输入框
TextBlock 静态文字
Shape 图形元素

 

 

 

 

 

 

 

 

 

 

 

 

 

 


下面咱们逐一剖析这些元素的内部结构,了解内容与内容属性。组件化

  咱们能够把控件想象成一个容器,容器里面装的东西就是它的内容,控件的内容能够直接是数据,也能够是控件。当控件的内容仍是控件的时候就造成了控件的嵌套,因此WPF的UI会造成一个树形结构。若是不考虑控件内部的组成结构,只观察由控件组成的“树”,那么这棵树称为“逻辑树(Logicol Tree)”;WPF控件每每是由更基本的控件构成的。即控件自己就是一棵树,若是连控件自己的树也考虑在内,则这颗比逻辑树更“繁茂”的树称为可视元素树(Visual Tree)。布局

  控件时内存中的对象,控件的内容也是内存中的对象。控件经过本身的某个属性引用着做为其控件的对象,这个属性称为内容属性(Content Property)。“内容属性”是个统称,具体到每种控件上,内容属性都有本身确切的名字——有的直接叫Content,有的叫Child;有些控件的内容能够是集合,其内容属性叫Items或Children的。学习

3、各类内容模型详解

  咱们把符合某类内容模型的UI元素称为一个族,每一个族用它们共同的基类来命名

3.一、ContentControl族

本类元素的特色以下:

  1. 均派生自ContentControl类。
  2. 它们都是控件(Control)。
  3. 内容属性的名称为Content。
  4. 只能由单一元素充当其内容。  

怎么理解“只能由单一元素充当其内容”这句话呢?看下例子。

Button控件属于ContentControl族,因此下面两个Button代码都是正确的——第一个Button的内容是一个静态文字,第二个Button的内容是一张图片。

    <StackPanel>
        <Button>
            <TextBlock>Hello World</TextBlock>
        </Button>
        <Button>
            <Image Source=".\1.jpg" Height="30" Width="30"></Image>
        </Button>
    </StackPanel>

但若是你想让Button的内容即包含文字又包含图片是不行的:

    <StackPanel>
        <Button>
            <TextBlock>Hello World</TextBlock>
            <Image Source=".\1.jpg" Height="30" Width="30"></Image>
        </Button>
    </StackPanel>

编译器会报错“对象“Button”已经具备子级且没法添加“Image”。“Button”只能接受一个子级。”但是若是咱们真的须要一个带图标的Button那怎么办呢?咱们只须要先用一个能够包含多个元素的布局控件把图片和文字包装起来,再把这个布局控件做为Button的内容就行了

ContentControl族包含的控件以下

Button ButtonBase CheckBox ComboboxItem
ContentControl Frame GridViewColumnHeader GroupItem
Label ListBoxItem ListViewItem NavigationWindow
RadioButton RepeatButton ScrollViewer StatusBarItem
ToggleButton ToolTip UserControl Window

 

 

 

 

 

3.二、HeaderedContentControl族

本族元素的特色以下:

  1. 它们都派生自HeaderedContentControl类,HeaderedContentControl是ContentControl类的派生类。
  2. 它们都是控件,用于显示带标题的数据。
  3. 除了用于显示主体内容的区域外,控件还具备一个显示标题(Header)的区域。
  4. 内容属性为Content和Header
  5. 不管是Content仍是Header都只能容纳一个元素做为其内容。

HeaderedContentControl族包含的控件以下

Expander GroupBox HeaderedContentControl TabItem

 

 

下面这个例子是一个以图标为Header、以文字为内容主体的GroupBox

    <StackPanel>
        <GroupBox Margin="10">
            <GroupBox.Header>
                <Image Source=".\1.jpg" Height="30"></Image>
            </GroupBox.Header>
            <TextBlock TextWrapping="WrapWithOverflow" Margin="10">
                愿你慢慢长大,愿你有好运,若是没有,但愿你在不幸中学会慈悲;愿你被不少人爱,若是没有,但愿你在寂寞中学会宽容。
            </TextBlock>
        </GroupBox>
    </StackPanel>

 3.三、ItemsControl族

本族元素特色以下:

  1. 均派生自ItemsControl类。
  2. 它们都是控件,用于显示列表化的数据。
  3. 内容属性为Items或ItemsSource。
  4. 每种ItemsControl都对应有本身的条目容器(Item Container)。

本族的包含控件以下所示

Menu MenuBase ContextMenu Combobox
ItemsControl ListBox ListView TabControl
TreeView Selector StatusBar  

 

 

 

 

本族控件具备特点的一点就是会自动使用条目容器对提交给它的内容进行包装。合法的ItemsControl内容必定是个集合,当咱们把这个集合做为内容提交给ItemsControl时,ItemsControl不会把这个集合直接拿来用,而是使用本身对应的条目容器把集合中的条目逐个包装,而后再把包装好的条目序列看成本身的内容。这种自动包装的好处就是容许程序员向ItemsControl提交各类数据类型的集合,程序员在思考问题时会天然而然的感受到ItemsControl控件直接装载着数据,若是须要进行增长、删除、更新或者排序,那么直接去操做数据集合就能够,UI会自动将改变展示出来,这正体现了在WPF开发时数据直接驱动UI再进行显示。

ListBox是典型的ItemsControl,下面将以它为例,研究一下ItemsControl。

  首先,咱们看看ListBox的自动包装。WPF的ListBox在显示功能上比Windows Form或者ASP.NET的ListBox要强大的多。传统的ListBox只能将条目以字符串的形式显示,而WPF的ListBox除了能够显示中规中矩的字符串条目还能显示更多的元素,如CheckBox、RadioButton、TextBox等,这样一来,咱们就能制做出更加丰富的UI,代码以下

 <ListBox>
            <CheckBox x:Name="ckBoxTim" Content="Tim"/>
            <CheckBox x:Name="ckBoxTom" Content="Tom"/>
            <CheckBox x:Name="ckBoxSimple" Content="Simple"/>
            <Button x:Name="Mess" Content="Mess"/>
            <Button x:Name="Ownen" Content="Ownen"/>
            <Button x:Name="Victor" Content="Victor"/>
        </ListBox>

运行效果以下

 表面看上去是ListBox直接包含了一些CheckBox和Button,实际并不是这样。咱们为Ownen这个按钮添加一个Click事件,看看它的父容器是什么。

        private void Ownen_Click(object sender, RoutedEventArgs e)
        {
            var invoker = sender as Button;
            var parent = VisualTreeHelper.GetParent( VisualTreeHelper.GetParent(VisualTreeHelper.GetParent(invoker)));
            MessageBox.Show(parent.GetType().ToString());
        }

 VisualTreeHelper类是帮助咱们在这颗由可视化元素构成的树上进行导航的辅助类。咱们沿着被单击的Button一层一层向上找,找到第三层发现它是一个ListBoxItem。ListBoxItem就是ListBox对应的Container,也就是说,不管你把什么样的数据集合交给ListBox,他都会以这种方式进行自动包装。

 上面这个例子就是单纯的为了说明ItemsControl可以使用对应的Item Container自动包装数据。实际工做中,除非列表里的元素自始至终都是固定的咱们才使用这种直接把UI元素做为ItemControl内容的方法,好比一年由十二个月、一周有七天等。大多数状况下,UI上的列表会用于显示动态的后台数据,这时候咱们交给ItemsControl的就是程序逻辑中的数据而非控件了。

假设程序中定义有Person类:

    public class Person
    {
        public int ID { get; set; }
        public string Name { get; set; }
        public int Age { get; set; }
        // ...
    }

而且有一个Person类型的集合:

var lstPerson = new List<Person>()
            {
                new Person() { Age = 20, Name = "Simple", ID = 1 },
                new Person() { Age = 22, Name = "Tim", ID = 2 },
                new Person() { Age = 22, Name = "Tom", ID = 3 },
                new Person() { Age = 22, Name = "jrrey", ID = 3 }
            };

在主程序中有一个名为lsbPerson的ListBox,咱们只须要这样写:

            var lstPerson = new List<Person>()
            {
                new Person() { Age = 20, Name = "Simple", ID = 1 },
                new Person() { Age = 22, Name = "Tim", ID = 2 },
                new Person() { Age = 22, Name = "Tom", ID = 3 },
                new Person() { Age = 22, Name = "jrrey", ID = 3 }
            };
            lsbPerson.DisplayMemberPath = "Name";
            lsbPerson.SelectedValuePath = "ID";
            lsbPerson.ItemsSource = lstPerson;

   DisplayMemberPath这个属性告诉ListBox显示每条数据的哪一个属性,换句话说,ListBox会去调用这个属性的ToString()方法,把获得的字符串放入一个TextBlock(最简单的文本控件),而后再按前面说的办法把TextBlock包装进一个ListBoxItem里。

  ListBox的SelectedValuePath属性将与其SelectedValue属性配合使用。当你调用SelectedValue属性是,ListBox先找到选中的Item所对应的数据对象,而后把SelectedValuePath的值看成数据对象的属性名称并把这个属性的值取出来。

  DisplayMemberPath和SelectedValuePath是两个至关简化的属性。DisplayMemberPath只能显示简单的字符串,想用更加复杂的形式显示数据须要使用DataTemplate。SelectedValuePath也只能返回单一的值,若是想进行一些复杂的操做,不妨直接使用ListBox的SelectedItem和SelectedItems属性,这两个属性返回的就是数据集中的对象,获得原始的数据对象后就职由程序员操做了。

  理解了ListBox的自动包装机制,我把所有ItemsControl对应的Item Container列在下面

Items名称 对应的Item Container
ComboBox ComboBoxItem
ContextMenu MenuItem
ListBox ListBoxItem
ListView ListViewItem
Menu MenuItem
StatusBar StatusBarItem
TabControl TabItem
TreeView TreeViewItem

 

 

 

 

 

 

 

 

 

 

 

 3.四、HeaderedItemsControl族

顾名思义,本族控件除了具备ItemsControl的特性外,还具显示标题的能力。

本族元素的特色以下

  1. 均派生自HeaderedItemsControl类
  2. 它们都是控件,用于显示列表化的数据,同时能够显示一个标题。
  3. 内容属性为Items、ItemsSource和Header
  4. 由于它与ItemsControl很是类似,在这就不作演示了

 3.五、Decorator族

在本族中的元素,在UI上是其装饰做用的。如可使用Border元素为一些组织在一块儿的内容加个边框。若是须要组织在一块儿的内容可以自由缩放,则可使用ViewBox元素。

本元素的特色以下:

  1. 均派生自Decorator类。
  2. 起UI装饰做用。
  3. 内容属性为Child。
  4.  只能由单一元素充当内容。

本族元素以下

ButtonChrome ClassicBorderDecorator ListBoxChrome SystemDropShadowChrome
Border InkPresenter BulletDecorator ViewBox
AdornerDectorator      

 

 

 

 

3.六、TextBlock和TextBox

  这两个控件最主要的功能就是显示文本。TextBlock只能显示文本,不能编辑,因此又称静态文本。TextBox则容许用户编辑其中的内容。TextBlock虽然不能编辑内容,但可使用丰富的印刷级的格式控制标记显示专业的排版效果。

  TextBox不须要太多的显示格式,因此它的内容是简单的字符串,内容属性为Text。

  TextBlock因为须要操纵格式,因此内容属性是InLines(印刷中的“行”),同时TextBlock也保留一个名为Text的属性,当简单的显示一个字符串时,可使用这个属性。

3.七、Shape族元素

  友好的界面离不开各类图形的搭配,Shape族元素(它们只是简单的视觉元素,不是控件)就是专门用来在UI上绘制图形的一类元素。这类元素没有本身的内容,咱们可使用Fill属性为它们设置填充效果,还可使用Stroke属性为它们设置边线效果。

本族的元素特色以下:

  1. 均派生自Shape类。
  2. 用于2D图形绘制。
  3. 无内容属性。
  4.  使用Fill属性设置填充,使用Stroke属性设置边线。

3.八、Panel族元素

  之因此把Panel元素放在最后是由于这一族控件实在是过重要了——全部用于UI布局的元素都属于这一族。

  本族元素的特色以下:

  1. 均派生自Panel抽象类
  2. 主要功能是控制UI布局。
  3. 内容属性为Children。
  4. 内容能够是多个元素,Panel元素将控制它们的布局。

  对比ItemsControl和Panel元素,虽然内容均可以是多个元素,但ItemsControl强调以列表的形式来展示数据而Panel则强调对包含元素进行布局。因此ItemsControl的内容属性是Items和ItemsSource而Panel的内容属性名为Children。

本族元素以下所示

Canvas DockPanel Grid TabPanel
ToolBarOverflowPanel StackPanel ToolBarPanel UniformGrid
VirtualizingPanel VirtualizingStackPanel WrapPanel
相关文章
相关标签/搜索