回顾
上一篇我们介绍了WPF基本的知识。并且介绍了WPF与winform传统的cs桌面应用编程模式上的变化,这篇,我们将会对WPF的一些基础的知识做一个简单的介绍,关于这些基础知识更深入的应用则在后续的篇幅中大幅度的应用。
本文大纲
- 什么是xaml
- 什么是路由事件
- WPF都提供了那些基础控件
- 什么是依赖属性
- 元素绑定
- WPF中的资源
- WPF的几种布局方式
什么是Xaml
Xaml(Extensible Application Markup Language) 可扩展应用程序标记语言,该语言基于xml实现的进行了相应的扩展。该语言很容易进行扩展,有点类似B/S编程中的代码后置,前端是HTML,后台是业务 逻辑和相关处理代码。
还有一点是我们反复强调的,XAML并不是HTML。尽管XAML在元素的声明、程序样式的设置和指定事件处理程序上都和HTML非常类似,但是XAML 是基于XML的,它是WPF的外在表现形式。而HTML主是一种标记语言,仅仅是用来为浏览器呈现页面内容。XAML除了用来呈现信息和请求用户输入等基 本的功能外,它还包含了一些高级的特性,例如它提供了对动画和3D众多方面的支持。
xaml语言,微软提供了可视化的设计工具blend。基于blend创建的的界面模型及效果,都可以存储或者转换为xaml格式的代码,这样就可以大大 的提高了团队中的设计人员和开发人员之间的协作,减少了设计人员原型效果,开发人员无法实现,设计人员可以自行设计后,转换为xaml代码。
关于xaml更多的内容,可以参考维基百科或百度知道。
相关的简单说明如下:
对于熟悉XML的同仁,应该没有什么难理解的。
什么是路由事件
我们先来看看MSDN给出的定义:
功能定义:路由事件是一种可以针对元素树中的多个侦听器(而不是仅针对引发该事件的对象)调用处理程序的事件。
实现定义:路由事件是一个 CLR 事件,可以由 RoutedEvent 类的实例提供支持并由 Windows Presentation Foundation (WPF) 事件系统来处理。
我们来解释下MSDN给出的定义:
1、传统方式:
对应的xaml代码和后台代码如下:
后台对于的处理如下:
当然采用如下的方式也是可行的。
后台代码修改为:
2、WPF的路由方式:
路由事件使用以下三个路由策略之一:
-
冒泡:针对事件源调用事件处理程序。路由事件随后会路由到后续的父元素,直到到达元素树的根。大多数路由事件都使用冒泡路由策略。冒泡路由事件通常用来报告来自不同控件或其他 UI 元素的输入或状态变化。
-
直接:只有源元素本身才有机会调用处理程序以进行响应。这与 Windows 窗体用于事件的“路由”相似。但是,与标准 CLR 事件不同的是,直接路由事件支持类处理(类处理将在下一节中介绍)而且可以由 EventSetter 和 EventTrigger 使用。
-
隧道:最初将在元素树的根处调用事件处理程序。随后,路由事件将朝着路由事件的源节点元素(即引发路由事件的元素)方向,沿路由线路传播到后续的子 元素。在合成控件的过程中通常会使用或处理隧道路由事件,这样,就可以有意地禁止显示复合部件中的事件,或者将其替换为特定于整个控件的事件。在 WPF 中提供的输入事件通常是以隧道/冒泡对实现的。隧道事件有时又称作 Preview 事件,这是由隧道/冒泡对所使用的命名约定决定的。
在传统的方式中,我们必须对每个事件源进行事件处理绑定,如果我们新增加新的对象或者新的事件源,那么我们还需要添加新的绑定。而这一切在WPF,都被统一处理和考虑了,下面,我们以冒泡为例来简单说明。
冒泡的顺序
修改后台代码如下:
关于逻辑树与可视化树,我们这里解释下:
逻辑树:简单来说,就是我们从界面上来看 界面的可视化基本组成部分,这就构成了窗口的一个逻辑树。逻辑树始终存在于WPF的UI中,不管UI是用XAML编写还是用代码编写。WPF的每个方面(属性、事件、资源等等)都是依赖于逻辑树的
可视树:可视树基本上是逻辑树的一种扩展。逻辑树的每个结点都被分解为它们的核心视觉组件。逻辑树的结点对我们而言基本是一个黑盒。而可视树不同,它暴露 了视觉的实现细节。下面是Visual Tree结构就表示了上面四行XAML代码的视觉树结构。
可视树,将界面组成的所有对象本身的内容进行了解析,例如button 按钮,他的内容,是有contentpresenter和textblock构成。
关于逻辑树和可视树的更多介绍,可参考MSDN的介绍。
我这里给出简单的获取逻辑树和可视树的代码:
都上WPF本身提供了一些方法。采用递归的方式。
我们接着上面的关于查看事件源的地方:
经过上面我们看到了,默认情况下,WPF的事件是采用冒泡的方式进行事件的传递的,那么我们如何采用其他的二种策略呢?
我们再来看下隧道事件,隧道事件的顺序如下:引用MSDN中的解释图:
输入事件的冒泡和隧道
事件的处理顺序如下所示:
-
针对根元素处理 PreviewMouseDown(隧道)。
-
针对中间元素 1 处理 PreviewMouseDown(隧道)。
-
针对源元素 2 处理 PreviewMouseDown(隧道)。
-
针对源元素 2 处理 MouseDown(冒泡)。
-
针对中间元素 1 处理 MouseDown(冒泡)。
-
针对根元素处理 MouseDown(冒泡)。
我们来验证下,我们以此处理所有的preview事件,看看是不是按照上面介绍的方式去处理的。
对应的后台代码如下:
运行,后查看输出结果:
与我们预期的一致。关于更多的路由事件,我们后面通过单独的章节来说明。
WPF基础控件
系统默认提供的基础控件:
有些由于自己也没有进行深入的使用,可能就不会介绍太详细。
关于控件的基本使用,会在后续进行深入的讨论和演示。
什么是依赖属性
DependencyProperty(依赖属性):Windows Presentation Foundation (WPF) 提供了一组服务,这些服务可用于扩展公共语言运行时 (CLR) 属性的功能,这些服务通常统称为 WPF 属性系统。由 WPF 属性系统支持的属性称为依赖项属性。
WPF界面控件中的属性,都可以称作是依赖属性,通过依赖属性,我们能够实现viewModel与界面元素属性之间进行绑定。
依赖属性与我们平时说的属性有区别的。
1、传统的属性
这没啥好说的,就是我们针对对象进行了封装。
假设,我们有一个类,有很多的属性,而且,又被多个对象继承,我们知道继承的特性,那么肯定会有很多的属性,本身我们可能基本不适用,那么该属性,还是会 被创建的时候实例化,那么有没有什么办法,我们减少这样的开销呢?WPF针对这样的情况提出了依赖属性的概念。
2、依赖属性:
查看其基本的声明和定义:在注释中描述:通过方法设置的属性,数据绑定,动画,样式等。
依赖属性就是自己自己没有值,通过Binding从数据源获得值,就是依赖在别人身上,拥有依赖属性的对象称为依赖对象。
我们来看看WPF控件的基本继承链
- Object类:在.Net中所有类型的根类型
- DispatcherObject类:WPF 中的大多数对象是从 DispatcherObject 派生的,这提供了用于处理并发和线程的基本构造。WPF 基于调度程序实现的消息系统。
- DependencyObject类:表示一个参与依赖项属性系统的对象。
- Visual类:为 WPF 中的呈现提供支持,其中包括命中测试、坐标转换和边界框计算。
- UIElement 类: WPF 核心级实现的基类,该类建立在 Windows Presentation Foundation (WPF) 元素和基本表示特征基础上。
- FrameworkElement 类:为 Windows Presentation Foundation (WPF) 元素提供 WPF 框架级属性集、事件集和方法集。此类表示附带的 WPF 框架级实现,它是基于由UIElement定义的 WPF 核心级 API 构建的。
- Control类:表示 用户界面 (UI) 元素的基类,这些元素使用 ControlTemplate 来定义其外观。
- ContentControl类:表示包含单项内容的控件。
- ItemsControl类:表示一个可用于呈现项的集合的控件。
- Decorator类:提供在单个子元素(如 Border 或 Viewbox)上或周围应用效果的元素的基类。PPT中鼠标滑过文本框后,出现边框
- Image类:表示显示图像的控件。
- MediaElement类:表示包含音频和/或视频的控件。自己封装一个视频播放器
- Panel类:为所有 Panel 元素提供基类。使用 Panel 元素在 Windows Presentation Foundation (WPF) 应用程序中放置和排列子对象。
- Sharp类:为 Ellipse、Polygon 和 Rectangle 之类的形状元素提供基类。
关于依赖属性的声明和更详细的内容,我们在后续的单独的章节中有更详尽的介绍。
几种应用依赖属性的场景:
- 希望可在样式中设置属性。
- 希望属性支持数据绑定。
- 希望可使用动态资源引用设置属性。
- 希望从元素树中的父元素自动继承属性值。
- 希望属性可进行动画处理。
- 希望属性系统在属性系统、环境或用户执行的操作或者读取并使用样式更改了属性以前的值时报告。
- 希望使用已建立的、WPF 进程也使用的元数据约定,例如报告更改属性值时是否要求布局系统重新编写元素的可视化对象。依赖对象创建时并不包含存储数据空间。WPF中必须使用依赖对象作为依赖属性的宿主。
后面的章节,我们会结合具体的案例,来说明如何使用依赖属性。
元素绑定
有了依赖属性后,我们就可以实现界面的绑定机制了,就会比较容易处理了,既然实现界面的绑定的话,就需要实现接口:INotifyPropertyChanged 实现了该接口的属性,就可以通过绑定的方式,将属性绑定到控件的属性上
下面给出一个简单的绑定的例子
我们来查看下,xaml代码和后台的代码,我们会发现,我们没有对文本框设置过任何的值,而是通过绑定来实现的赋值操作.
1、xaml代码如下:
2、后台代码中,我们并没有设置任何的关于Textbox的赋值或者设置值的代码和操作:
3、我们接着看看TestViewModel的实现和要注意的事项:
注意这里的绑定属性,必须是public,并且 事件通知的名称必须与属性名称一致。
BaseViewModel的代码:
后面关于我们进行MVVM编程时,还会使用这部分的内容。
WPF中的资源
WPF中的资源,包含的内容比较多,样式,图片,视频,动画等,都可以称作资源。资源,我们一般可以再一个应用程序范围内容定义公共资源,也可以在一个页面内定义资源。
我们平时看到有很多的软件,有很多的主题,在WPF中就可以通过定义样式来实现,这点有点类似web上的样式表。
我们可以再运行期动态的修改样式表,达到修改界面的目标。
下面,我们样式下,如何使用资源,简单的介绍资源的几种使用方式,关于更多的资源的介绍,在后续单独的章节中介绍。
1、添加样式文件
添加样式内容如下:
将样式文件添加到App中
运行查看效果:
如果,我们不想使用默认的样式应该怎么办
我们只要自己设置了属性值后,他就不会使用样式表中定义的属性值:
例如,我们只要设置 宽160,高30,字体大小10 然后再次运行看看效果
同样,如果我们只需要在当前的页面,应用当前的样式,那么我们就不要再App中加入样式文件,二是把代码移动到当前的页面中:
上面就是对资源文件的简单介绍,后续会对样式的设置和应用等结合具体的例子进行更深入的介绍。
WPF的几种基本布局
1、Grid的布局
这个就没啥特别好说的,其实,基本上复杂的布局,都需要用到Grid。
主要就是对行和列进行进行设置和定义。
1、行表格
列表格:
包含行和列的表格
2、StackPanel
垂直方向:
3、DockPanel
4、WarpPanel
本文涉及到的内容比较多,但是讲述的内容,都不深入,大家对基本的用法有一定的了解即可,后续的章节,将会更深入的介绍,并且结合实际项目中的心得体会进行总结和分享。