WPF的优点是:
1. 可以相对比较容易的写出完全定制化的界面。
2.特有的MVVM设计模式可以完美的分离 UI设计(View层) 和 业务逻辑(Model层)
WPF相关技术一定要弄清楚的知识点,先列个大纲,按照我个人建议的学习顺序排序。
1. 理解XAML相关窗体设计的原理。
a. 逻辑树结构非常类似HTML,但更加麻烦。
b. 可以使用XamlPad查看可视树结构。
c. 理解Style类似于CSS,并可以通过随时更换资源字典以达到更换主题或者换肤的目的。
2. 触发器(Trigger),最常用的是属性触发器和数据触发器。
a. 需要知道触发器主要是用于视觉交互的。
b. 属性触发器是控件本身的某个属性值发生改变,比如IsMouseOver=True的时候,会触发可视内容 比如背景色 发生变化。
c. 数据触发器是在数据模板(DataTemplete)中,当某个业务数据发生变化改变时,会触发可视内容发生变化。
3. 为了创建形态各异的界面,实现各种神奇的效果,需要学习WPF绘图。
a. 使用图形,包括:直线,矩形,椭圆,贝塞尔曲线,Path(最强大的路径)
b. 应用滤镜效果,Effect比较简单,但是导入和开发外部滤镜,一直没有研究。
c. 使用变形。有平移,旋转,缩放,扭曲等基本变形,以及矩阵变形。(要注意的是:每种变形既可以放在呈现变形中,也可以放在布局变形中,需要区分二者的区别。呈现变形只是看到的样子变化了,实际位置和形状都没变。布局变形是真的变化,会在变形的同时不断对其他控件重新进行布局计算。)
4. 学习使用XAML创建简单的动画
a. 尝试使用3类触发器触发动画的发生
b. 使用VisualStatusManager来应用动画
c. 如无必要,尽量避免通过写代码的方式创建动画
d.(扩展:使用Blend创建并组合出复杂的动画。)
5. 依赖属性和附加属性。
a. 要学会如何自定义我们自己扩展的依赖属性和附加属性。
b. 所谓依赖属性,从功能上讲:就是一个普通的属性,附带了可以绑定到任意对象的其他属性上的功能。所谓绑定就是:一个值变化,另外一个值跟着变化的。这样可以省去大量的界面效果相关的后台代码,并使界面和业务代码分离成为可能。
c. 依赖属性可用于继承一个现有控件或者自定义控件,并为其扩展属性。缺点是这些属性不能复用,是控件自己专属的。
d. 附加属性是一种特殊的依赖属性。有两种用法,一种继承现有类,并进行定义,实现的效果如同Canvas.Left。 一种新建类,主要用于为了不进行大量继承的前提下,给现有控件扩展额外的属性。是一种很好的 组合模式 思维。
6. 模板。主要了解:ControlTemplete,DataTemplete
a. ControlTemplete 是用来重写现有控件的可视结构的,一般和依赖属性和附加属性结合,加上绑定,控件可以获得很好的扩展。
b. DataTemplete 主要用于定义数据对象的可视化结构的。既然是数据对象,最好要有个数据类型,即在DataType中定义。
c. 模板在WPF起着巨大的作用。控件模板可以很容易写出任意形态任意效果的外观,数据模板使得View层和ViewModel层很好的分离。请一定要注意,起初我对这样的概念不屑一顾,其实就是没明白什么意思。后来我才懂:就因为数据模板的存在,使得代码中几乎再也不用出现控件对象了。
d. 推荐模式在模板中的运用,利用自动创建的模板,经常会看到Part_XXX,不明白怎么回事? 看基类的特性中,会有TemplatePart,这其实是一种推荐的设计模式,用于提示后来的开发者,告诉你控件组成的必要元素:名称和类型。也就是说,你可以重写控件模板,但是如果要实现控件自身的核心功能,一定要保留一个名为Part_XXX的某个类型的控件才行。
7. MVVM设计模式,最方便学习此模式的是MVVMLight框架,可以直接在NuGet中下载。
a. Model - View - ViewModel。不同于MVC,MVP等设计模式, MVVM最主要的特点是实现UI(View)和业务(Model)的分离。而ViewModel应该同时负责表现逻辑和业务逻辑。这在开发时尤其有用,另外可以同时快速创建设计用的ViewModel,以便设计阶段即可以模拟出完全真实的使用效果,因为View层对应的ViewModel可以很容易的切换。
b. 本人并非设计模式的过渡崇拜者,然而只要让代码生产力持续保持比较高的效率,就是好的方法。很多时候只要不影响大局,在CodeBehind中写一些代码是无可厚非的。
c. MVVMLight框架下,有很好用的EventToCommand,可以将任何事件直接转化为命令,在我看来这使得UI和业务分离的更加彻底。
d. MVVMLight下的消息机制,Messenger 也是个很好用的东东,可以实现本无关联的ViewModel间的通信,让他们继续无关联下去。另外也可以用于导航。
e. ViewModel 让 Model 更加适合于 View。注意两点:一. ViewModel 不需要知道 View 中有什么,换句话说,ViewModel 中不要引用View中的控件。 二. View 中不要直接引用 Model,而是借助ViewModel,想要用Model怎么做? 比如:PersionViewModel 里有个公有的 Persion 类型的属性 persion, View 的DataContext是 PersionViewModel,绑定时写 {Binding Path=persion.Name} 即可。
8. 可以附加在控件上的 行为 (Behavior)
a. 行为,还有上面刚说的 触发器,EventToCommand,都是一种附加属性。这样就很好理解了。通常理解的附加属性,只是一一个数据类型的值,既然扩展成了一个行为这么复杂的数据类型,目的主要是为了实现各种行为效果的重用,然而行为的封装是最完整的。
9. 要反复深刻理解装饰器相关 Adorner, Decorator, AdornerDecorator
a. 从简单开始,最好懂的是 Decorator,如果这个词感到陌生,那么Border就不陌生了,边框嘛,一个东西,外面套个边而已。
然而Decorator是Border的基类而已。可以扩展它,然而我目前还没有遇到继承Decorator的应用场景。
b. 再说Adorner,这才是真正的装饰器,本身没有可视结构,它的存在就是要你继承它,并设计它的可视样式,如何继承,网上一大堆教程,最直观的理解就是:在控件自身上面,蒙了一层额外的装饰,用于交互性的提示与操作。 比如:光标,旋转钮,调整大小,表头的排序箭头 等等。需要什么就画什么,恩恩,这个很重要。
c. 理解了前面两个,再来说最后一个,看起来好复杂好高大上的样子, 其实很简单, 按我的理解: 这就是一个双面胶,用来粘贴 Decorator 和 Adorner。微软需要在控件上面有个容器,用来放 Adorner,这个容器最好从控件的最边缘开始,控件的最边缘,自然是 Decorator了,OK,那么再包裹一个东西,就可以放 Adorner了, 于是这个东西的名字叫做 AdornerDecorator,上面有个叫做 AdornerLayer的东西可以让Adorner直接Add。
d. 事实上,只有AdornerDecorator和ScrollContentPresenter具有 AdornerLayer,所以,想要自己加装饰器的时候,不要忘记在模板里面套一个 AdornerDeocrator 哦。尤其像我这种经常写自定义窗体的,窗体的 ContentPresenter 外面套个 AdornerDecorator 是 必不能忘的。
10. 路由事件和命令
a. 如果想要写出来的WPF程序在复杂的界面中不会出现莫名其妙的问题,那么一定要弄懂路由事件和路由命令的概念。
所谓路由,有三种烂大街的方式:隧道,冒泡,直接。 顾名思义:隧道,标签从外向内响应事件。 冒泡,标签从内向外相应事件。
直接,只能从定义元素监听元素的直接事件,写别的地方没用。
b. 路由事件是定义在哪一层监听事件,而不是点击哪一层会执行事件,目的自然是 “一夫当关万夫莫开”,比如冒泡路由,N多的控件都可能响应某一鼠标事件,那么只需要在集合控件上面 监听这一事件就可以了(附加事件)。 在 响应事件处理中,再拿到实际命中的 控件就可以了。 当然,这个时候我往往是直接取命中控件的DataContext,直接转化为ViewModel,操作数据层。
c. 其实这个路由事件机制和HTML中 javascript 的事件机制非常非常类似,addEventListener的第三个参数 useCapture 用来控制 是否是 Bubbling(冒泡)方式。我也常常会用 jQuery 中的 delegate 来做类似的事情,以便监听未来产生的子元素相关的事件。
d. 可以在路由事件中标记 Handler = True。来阻止事件继续路由下去。 然而 实际上 即使这样设置,路由事件依然在继续传递。 因为用 AddHandler 显式 挂接路由事件处理程序,可以定义路由事件即使被处理依然会执行。即设置 handledEventsToo = True。 这里分析,相信 Handler || handledEventsToo 为真时,即会执行事件处理程序。
e. 自定义路由事件,类似于注册依赖属性。其中可以定义路由类型等等。
f. 说来说去,需要强调的是, Preview名称开头的事件是隧道方式,代表了事件是从外向内传递的。不带Preview的事件是冒泡方式,是从内向外传递的。 同一名称的事件Preview先执行。这是个形象的反弹,球弹进去再弹出来,这个过程球可以被人抓住。
g. 路由命令在我看来,因其天生的缺陷与限制,应用的比较少。我一般使用 MVVMLight 的 RelayCommand,当然,如果项目没必要引入MVVMLight时,自己自定义也可以。