博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
WPF快速入门系列(7)——深入解析WPF模板
阅读量:6376 次
发布时间:2019-06-23

本文共 7795 字,大约阅读时间需要 25 分钟。

一、引言

   模板从字面意思理解是“具有一定规格的样板"。在现实生活中,砖块都是方方正正的,那是因为制作砖块的模板是方方正正的,如果我们使模板为圆形的话,则制作出来的砖块就是圆形的,此时我们并不能说圆形的”砖块“不是砖块吧。因为形状只是它们的外观,其制作材料还是一样的。所以,模板可以理解为表现形式。WPF中的模板同样是表现形式的意思。

  在WPF中包括三种模板:控件模板、数据模版和面板模板。它们都继承于基类,其继承层次结果如下图所示:

  从上图可以发现,FrameworkTemplate确实有三个子类,它们正是WPF中支持的三种模板。对于控件模板,即控件外观外衣,可以通过修改控件模板来自定义控件的外观表现,例如,可以通过修改按钮的控件模板使按钮表现为圆形;数据模板,即数据的外衣。用于从一个对象中提取数据,并在内容控件或列表控件的各个项中显示数据。面板模板即面板的外衣,而面板又用于进行布局的,所以面板的外衣也就是布局的外衣,通过修改面板模板可以自定义控件的布局。例如,ListBox默认是自从向下地显示每一项的,此时可以通过修改面板模板使其自左向右地显示每一项。

  WPF模板其实都是外观的表现形式,不管是控件模板、数据模板还是面板模板,其都是改变控件的表现形式。只不过这三种控件的作用点不一样罢了。控件模板是针对于控件本身,修改它可以改变控件本身表现的样子;数据模板针对控件的数据,修改它可以改变控件绑定的数据表现样子。既然是决定数据的表现,从而决定其一般应用于数据绑定控件,如ListBox、ListView等控件。面板模板则针对于控件的布局,修改它可以影响控件的布局方式。

二、控件模板

  在分别介绍这三种控件模板之前,我觉得你有必要先了解WPF的逻辑树和可视化树的内容,因为你要修改控件模板,则首先需要了解控件的组成。

2.1 WPF的逻辑树和可视化树

  在许多技术中,元素和组件都是按树结构的形式进行组织的。使用这样的结构,开发人员可以直接操作树中的对象节点来程序对象,从而通过操作该对象来修改程序的表现和行为(这是了解逻辑树和可视化树的主要原因)。在WPF中,同样使用了树结构来组织元素之间的关系。WPF中支持逻辑树和可视化树的概念,并且WPF公开了两个提供树形视图帮助器类: 和 。逻辑树指的是UI界面的组成元素的结构。先看下面的XAML代码的例子:

  上面XAML的逻辑树如下图所示:

  可视化树是逻辑树的扩展版本,它将元素分成更小的部分。上面XAML代码对应的可视化树如下图所示:

  从上面可视化树可以看出,Button由多个可视化元素组成——使按钮具有阴影背景特征的边框(由类表示)、内部的容器(一个ContentPresenter对象)以及存储按钮文本的文本块控件(由TextBlock表示)。上面的可视化树和逻辑树结构并不是我凭空想象出来的,而是有事实依据的,我们可以通过VisualTreeHelper类和LogicTreeHelper类提供的方法来查看窗口的可视化树和逻辑树,下面的例子实现了这个需求,具体的XAML实现如下所示:

  对应的后台代码实现入下所示:

public partial class Window1 : Window    {        public Window1()        {            InitializeComponent();        }        // 把公共代码抽象出一个方法,从而使代码重用        public void ProcessElement(object obj, TreeViewItem item, TreeViewItem previousItem)        {                     item.Header = obj.GetType().Name;            item.IsExpanded = true;            // 如果当前元素是第一个元素就添加到树集合上            // 如果是内嵌元素,则添加到它的父节点上            if (previousItem == null)            {                treeElements.Items.Add(item);            }            else            {                previousItem.Items.Add(item);            }        }        private void PrintLogicTree(object obj, TreeViewItem previousItem)        {            TreeViewItem item = new TreeViewItem();            ProcessElement(obj, item, previousItem);            // 如果不是DependencyObject,则返回            if (!(obj is DependencyObject))                return;            // 递归打印逻辑树            foreach(object child in LogicalTreeHelper.GetChildren(obj as DependencyObject))            {                // 这里为了避免死循环,因为TreeView的子元素包含Window1、StackPanel等控件                // 如果不加这个条件,控件会一直反复循环                if (child is TreeView)                    return;                PrintLogicTree(child, item);            }        }        private void PrintVisualTree(DependencyObject obj, TreeViewItem previousItem)        {            TreeViewItem item = new TreeViewItem();            ProcessElement(obj, item, previousItem);            //  递归输出视觉树            for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)            {                if (obj is TreeView)                    return;                PrintVisualTree(VisualTreeHelper.GetChild(obj, i), item);            }        }        private void ShowLogicTree(object sender, RoutedEventArgs e)        {            treeElements.Items.Clear();            PrintLogicTree(this, null);        }        private void ShowVisualTree(object sender, RoutedEventArgs e)        {            treeElements.Items.Clear();            PrintVisualTree(this, null);        }    }

  程序的运行效果如下图所示:

2.2 通过控件模板自定义控件外观

  控件模板既然是控件的外衣,自然我们可以创建的新的控件模板,然后把新的控件模板应用到需要应用的控件中,这时候应用了新控件模板的控件,将会使用新的控件模板来渲染自身,从而改变控件的外观。这也是自定义控件外观的要旨。在WPF中按钮的默认控件是长方形的,我们可以通过创建一个新的控件模板来改变按钮的外观,下面的例子就实现了通过控件模板的方式自定义了一个圆形的按钮。具体的XAML代码如下所示:

  此时,你就可以看到按钮是一个圆形的了,并且当鼠标移动到按钮上时,会触发模板触发器来改变Ellipse的填充色,具体的运行效果如下图所示:

  从上面的控件模板的使用可知,它和创建自定义控件不同,在很多情况下,你不需要编写自己的控件,你只是希望更改控件的外观。使用控件面板非常简单:

  • 首先在资源集合中创建一个ControlTemplate,并指定key标记
  • 然后赋值到控件的Template属性中。  

三、数据模板

  数据模板是数据的外衣,数据模板是一段定义如何绑定数据对象的XAML标记,有两种类型的控件支持数据模板:

  • 内容控件通过ContentTemplate属性支持数据模板。内容模板用于显示任何放在Content属性中的内容。
  • 列表控件,即继承自ItemsControl类的控件,通过ItemPlate属性支持数据模板。该模板用于显示由ItemSource提供集合中的每一项。

  基于列表的模板实际上是以内容控件模板为基础的,因为列表中的每一项由一个内容控件包装的。如ListBox控件的ListBoxItem元素。下面让我们具体看看如何去创建一个数据模板吧。

3.1 如何定义数据模板

  具体的XAML代码如下所示:

  其对应的后台代码如下所示:

public partial class DataTemplate : Window    {        ObservableCollection
persons = new ObservableCollection
() { new Student() { Name ="LearningHard", Age=25}, new Student() { Name ="HelloWorld", Age=22} }; public DataTemplate() { InitializeComponent(); lstPerson.ItemsSource = persons; } }public class Student : INotifyPropertyChanged { public string ID { get { return Guid.NewGuid().ToString(); } } public string Name { get; set; } public int Age { get; set; } public event PropertyChangedEventHandler PropertyChanged; public void OnPropertyChanged(PropertyChangedEventArgs e) { if (PropertyChanged != null) PropertyChanged(this, e); } }

  其运行效果如下图所示:

  从上面数据模板的创建可知,使用DataTemplate很简单:

  • 首先在资源集合中创建一个数据模板,并设置key标签。
  • 然后将key赋值到控件的CellTemplate或ContentTemplate或ItemTemplate属性上即可。

3. 2 数据模板与控件模板的关系

  从上面的介绍可知,控件只是数据和行为的载体,至于它本身长什么样子和数据长什么样子都是靠Template决定的。决定控件外观的是ControlTemplate,决定数据外观的是DataTemplate,它们正是类的Template和ContentTemplate两个属性的值。

  一般来说,ControlTemplate内都有一个ContentPresenter,这个ContentPresenter的ContentTemplate就是DataTemplate类型。所以数据模板和控件模板的关系如下图所示:

四、创建面板模板

  ItemsPanelTemplate用于指定项的布局。  类型具有一个类型为ItemsPanelTemplate 的  属性。

  每种ItemsControl都有其默认的ItemsPanelTemplate。对于 ,默认值使用 。 对于 ,默认值使用 。 对于 ,默认值使用 。

  自定义面板模板与自定义数据面板和数据面板一样简单,一样只需要首先定义一个面板模板在资源集合中,然后将其Key指定给ItemsPanel属性即可。具体的XAML实现如下所示:

  其后台代码和数据模板的后台代码一样,其实现代码为:

public partial class ItemsPanelTemplate : Window    {        ObservableCollection
persons = new ObservableCollection
() { new Student() { Name ="LearningHard", Age=25}, new Student() { Name ="HelloWorld", Age=22} }; public ItemsPanelTemplate() { InitializeComponent(); lstPerson.ItemsSource = persons; } }

  此时程序运行的效果如下图所示,从下图结果可以看出,此时ListBox中的项不再是自上而下排列了,而是从左向右排列的。

五、总结

  到这里,WPF模板的内容就介绍结束了,本文主要介绍了WPF中支持的三种模板:控件模板、数据模板和面板模板,然后各自定义并使用了自定义的模板,最后介绍了这三个模板之间的联系。使用这三个模板的方式都非常简单,都是先定义一个模板,然后在把对应的key应用到控件对应的属性中,对于控件模板,应用的是控件的Template,对于数据模板,应用的是控件的ItemTemplate属性,对于面板模板,应用的是控件的ItemsPanel属性。在下面的一篇博文将介绍如何实现一个MVVM的实例程序。

  本文所有源码下载:

 

转载地址:http://jxtqa.baihongyu.com/

你可能感兴趣的文章
适配iOS 11和iPhoneX屏幕适配遇到的一些坑
查看>>
Fetch API 简单封装
查看>>
给媳妇做一个记录心情的小程序
查看>>
iOS App无需跳转系统设置自动连接Wi-Fi
查看>>
一道柯里化面试题
查看>>
本科studying abroad 无法毕业申请硕士转学转校处理一切studying abroad 问题
查看>>
RxJava(RxAndroid)的简单学习
查看>>
Java8 函数式编程之函数接口(下)
查看>>
【本人秃顶程序员】MySQL 全表 COUNT(*) 简述
查看>>
centos7中使用febootstrap自制一个基础的centos 7.2的docker镜像
查看>>
系统优化和克隆过程
查看>>
C#开发Unity游戏教程之判断语句
查看>>
Windows自带Android模拟器启动失败
查看>>
安装 SharePoint Server 2007
查看>>
springmvc mybatis 调用sql , 转成json
查看>>
linux centos 7 网卡突然不能上网异常解决
查看>>
shell+Python实现简单的链路监控
查看>>
授之以渔-运维平台发布模块一(Jenkins篇)
查看>>
DedeCMS操作基础(一)
查看>>
FreeBSD部署dns缓存服务器
查看>>