http://www.cnblogs.com/Zhouyongh/archive/2009/11/30/1613628.html

一站式WPF--Window(一)2009-11-30 14:18        by        周永恒,        3949        visits,        收藏,        编辑

用户通过Window与 Windows Presentation Foundation (WPF) 独立应用程序进行交互。Window的主要用途是承载可视化数据并使用户可以与数据进行交互的内容。独立 WPF 应用程序使用 Window 类来提供它们自己的窗口。

  这段是MSDN上对Window的描述,虽然翻译的不是那么地道,也可以看出Window的两大功能:一,承载可视化数据。二,使用户可以与可视化数据进行交互。

  在正式研究Window的功能之前,先来看一下,什么是Window?

什么是Window

  Window是Windows操作系统的核心,从表现上来说,Windows就是由许许多多的Window组成的,那么具体什么叫Window呢?

  通常意义上讲,我们所谓的Window是最外面的Window,也就是有着关闭、最小化的主Window。在Window编程中,调用CreateWindow来创建Window,通过设置dwStyle来指定样式,比如设置LBS_OWNERDRAWFIXED可以创建ListBox,设置BS_PUSHBUTTON可以创建Button等。CreateWindow的返回值就是窗口的句柄,从这个意义上来讲,在Win32世界中,万物皆Window,只是表现形式不同,那么WPF的Window对应什么呢?

WPF的Window

  WPF中的Window继承于ContentControl,内部可以承载一个Content,当然,借助于ItemsControl或Panel,Content也可以向下添加多个对象。这些对象都是WPF中的对象,也就是要承载的可视化数据。那么用户与可视化数据间的交互是怎么完成的呢?

  无论使用GDI绘制,或者使用DirectX绘制,在操作系统来看,Window都是一块持有句柄的有效区域。所有对该区域的操作,都会通过句柄来发送到Window对应的消息处理函数。也就是说,对外来看,WPF的Window依然是传统Win32的Window,对内它又把消息转化为Routed Event或者Command等来处理。关于这层处理和消息转化,要深入WPF的Window来谈起。

深入WPF的Window

  作为外界和可视化数据之间的桥梁,Window具有对内和对外两层作用。先说对内,Window内部可能会存在Button,ListBox等等控件,这些控件组成了一个对象树。树的子节点可能很多,但顶点只有一个,这个对象树是WPF的核心,Routed Event和Routed Command等都是依附于它的。抛开具体的对象树不说,我们要关注的是它的这种“众”字型的结构。如果你把这颗可视化数据组成的对象树想象成一个人的话,那么它的顶点就是它的头,我们对手臂和腿的操作只要对头喊话就可以了。换言之,对于WPF的Window

,它对内最关心的就是找到对象树的头(RootVisual),然后通过头把操作传递下去。

  从对外来看,操作系统关注的是注册Window的风格以及Rect。比如鼠标按键被按下时,按键消息被发送到系统的消息队列中,系统通过扫描所有注册窗口的Rect判断按键发生在哪个窗口中,再在适当的时机把按键消息从系统消息队列转移到创建窗口线程的消息队列中等待窗口处理。对于WPF的Window来说,同步这个Rect很重要,Window的UI是WPF的,但内部有个隐藏的使用CreateWindow创建的Win32-Window,当用户设置win.Width=60方法时要同步内部Window的Rect,反过来接收到WM_SIZE时也需要调用RootVisual去执行WPF的Measure、Arrange流程。

  用一个草图来表示Window的消息处理过程:

  1. 系统将消息发给隐藏的Win32-Window,在Dispatcher中GetMessage并分发到对应的窗口过程处理函数WndProc。
  2. WndProc里应是一个大的Switch-Case,用以处理不同的Window消息。按照消息的类别,WPF提供了不同的Manager来管理,这里的Manger并不是直接处理Window消息,并且并不是所有消息都经过WndProc再转到Manager的。
         比如说WM_KEYDOWN,Dispatcher调用GetMessage获得消息后, 调用了ComponentDispatcher的RaiseThreadMessage方法(关于ComponentDispatcher,可以参阅Nick的文章),最终由KeyboardDevice产生Keyboard.PreviewKeyDownEvent这个路由事件(Routed Event)。
  3. 仍然以WM_KEYDOWN来说, InputManager找到这个Input发生的区域--Window,调用Window的RaiseEvent方法唤起Keyboard.PreviewKeyDownEvent这个路由事件。
  4. 路由事件沿着对象树开始向下传递,方向是一去一回,由PreviewKeyDown到KeyDown。在这个传递过程中,相应的路由事件也被唤起,比如说如果此时焦点在Button上,当传递到Button时还会唤起Button的ClickEvent事件等。

  这些Manager,其中像ContentLayoutManager,本身是Internal的,仅仅是在Measure和Arrange的内部使用,这里只是表示消息经由分类后最终由这些Manager来管理。这个过程比较有意思的是Input,简单的来谈一谈它。

Input

  路由事件是WPF处理Input的核心,简略的说就是有一去一回从PreivewKeyDown到KeyDown这个过程,PreviewKeyDown的方向是从父到子,KeyDown的方向是从子到父。这个处理的过程不是本篇文章要谈的,重点是如何把一个简单的WM_KEYDOWN消息转化为PreivewKeyDown和KeyDown这两个路由事件。

  从图中可以看出,InputManager负责处理Input,一个Input,可能来自不同的设备--Mouse,Keyboard等等。InputManager要关注的地方有二:一,这个Input会转化成什么路由事件。二,这个Input作用在哪个UIElement上。第一个转化是由InputDevice来做的,这个InputDevice,具体有MouseDevice、KeyboardDevice等等。它会根据Window消息来生成对应的路由事件,然后把这些信息报告给InputManager。InputManager再根据这些信息找到作用的UIElement,然后唤起路由事件。

  说过了Input,重点来看Presentation,所谓Windows Presentation Foundation,显示一定是它的重点。

Presentation

  在前面中,介绍到了需要被显示的可视化数据,在WPF中是以对象树(确切说是Visual Tree)来组织的。那么它又是如何被画出来的呢?从对象树到真正Render之间又发生了什么呢?

  图例是WPF的架构图,其中重要的两个是PresentationCore和MilCore。在PresentationCore中,定义了Visual类,这个是WPF显示的核心,所有可以被显示的对象都直接或间接继承自Visual。当然,这里的Visual Tree就指Visual组成的树。Milcore(MIL -- Media Integration Layer),非托管代码,负责WPF和DirectX之间的通信,它主要由两部分组成:一,Composition Engine。二,Render Engine。前者负责创建Composition Tree,后者负责把Composition Tree转换成DirectX可以识别的Triangle并通知DirectX进行Render。

  简单说一下Render的流程:

  1. Visual被添加到Visual Tree上。
  2. Visual Tree和Composite Engine通过Message Transport来进行通信,Message Transport包括Transport和Channel两部分。Transport定义了传输的细节,Channel作用在Transport上,用来建立一个双向的通信管道。这里,当Visual Tree被修改后,把被修改的Viusal数据通过Channel发送给Composition Engine。
  3. Composition Engine接收到Visual数据后,创建对应的Composition Node,并加入到Composition Tree中去。
  4. Composition Engine通知Render Engine开始绘制,Composition Tree中的节点是Rectangle,Ellipse等,DirectX不能识别这些数据,Render Engine要把这些数据转化为DirectX可以识别的三角形,这个过程叫做Tessellate。
  5. Render Engine通知DirectX开始绘制(Render),DirectX在经过驱动(WDDM或者XPDM)通知显卡开始绘制像素到屏幕。

  在第一篇文章中,介绍了WPF的线程模型,WPF中线程一分为二,有UI线程和Render线程。UI线程是托管代码,管理Visual  Tree,用于处理输入,事件等。Render线程是非托管代码,在MIL中,仅用于绘制,把从UI线程传入的Visual数据转化并添加到Composition Tree进行绘制。在这个过程中,Render线程是被动的,它等待着UI线程向它传输数据并下达命令,也会把操作的结果(绘制完成,错误)等通过Channel报告给UI线程。

  这里要说说Viusal数据,也就是如何把Visual转化为Composition Node,在Avalon世界中,UCE(Unified Composition Engine)负责处理这层转化。当然,对UCE来说,它是不能识别WPF对象的,这种不能识别,就是说直接拿一个WPF的Line,它是不知道如何转化为相应Composition Node的,必须要WPF对象进行自描述,告诉UCE它对应什么Composition类型。UCE提供了IResource接口,这个接口定义了可以通过Channel传递到UCE的一系列方法。WPF的Visual实现了这一接口,Visual子类重写了其中的AddRefOnChannel方法并注明了其对应的Composition类型,比如说LineGeometry设置了它的类型是DUCE.Resource.Type_LINEGEOMETRY。UCE通过这些信息,就可以把传递过来的Visual数据转化为相应的Composition Node了。

  这里说到了UCE,每个WPF进程都有自己的UCE,并且在Avalon(Window Vista/Window 7)中,负责绘制桌面的DWM(Desktop Window Manager)也有它的UCE(也叫DUCE)。为了提供透明效果,桌面上的显示需要进行混合,DWM也是使用Composition Tree来管理窗口的,用两幅图来描述一下UCE的处理过程:

  最终,DWM经过混合后得到了桌面最后的透明效果。

  当然,整个过程不必细究,在WPF编程中也很少需要从UCE这个角度来考虑问题,只是帮助朋友们捋清一下思路,更好的理解WPF。讲过了这些底层的处理,把思路回归到Window上来,来看看Window是如何对这些进行整合的。

Inside Window

  前面提到,Window内部有一个隐藏的Win32-Window,用于接收消息,在WPF中,使用HwndSource来封装这个隐藏Window。那么从Visual Tree到Window之间又发生了什么呢?

  从Visual Tree来看,像提线木偶一样,控制它的头(顶点)就可以随意玩弄它。WPF提供了CompositionTarget以及PresentationSource来完成这些内部的处理,关于具体的流程,那么,就下篇吧。 ^_^

[转载]一站式WPF--Window相关推荐

  1. 【转载】wpf学习笔记1

    http://blog.csdn.net/fantasiax/article/details/4575968 深入浅出WPF(7)--数据的绿色通道,Binding(上) 小序: 怎么直接从2蹦到7啦 ...

  2. 转载:WPF binding

    数据绑定细节 要使用 WPF 数据绑定功能,您必须始终要有目标和源.绑定的目标可以是从 DependencyProperty 派生而来的任何可访问属性或元素,例如 TextBox 控件的 Text 属 ...

  3. 【转载】wpf绑定全局静态变量并且实现变更通知(mvvm)

    这篇看了之后,很有启发,也很有用!,分享给大家 以下是转载内容. ---------------- 版权声明:本文为CSDN博主「returnTrue999」的原创文章,遵循CC 4.0 BY-SA版 ...

  4. 艾伟_转载:WPF/Silverlight陷阱:XAML自定义控件的嵌套内容无法通过名称访问

    为了说明这个问题,假定我们需要实现一个具有特殊功能的按钮控件.编写Xaml文件如下: <Button x:Class="TestWpf.XamlButton"     xml ...

  5. 【转载】wpf数据绑定binding与INotifyPropertyChanged

    WPF数据绑定号称是:数据变化会通过界面更新数据,这对新手而言,绝对是个误区,在我听说这句话的时候,我真是高兴,用了以后才发现其实没有那么美.要实现前面号称的特性,需要三个条件:1.进行绑定,2.绑定 ...

  6. 【转载】WPF快速入门系列(7)——深入解析WPF模板

    一.引言 模板从字面意思理解是"具有一定规格的样板".在现实生活中,砖块都是方方正正的,那是因为制作砖块的模板是方方正正的,如果我们使模板为圆形的话,则制作出来的砖块就是圆形的,此 ...

  7. 【转载】Layered Window(分层窗体,透明窗体)

    本文转载自花间醉卧<Layered Window(分层窗体,透明窗体)> // 为窗体添加 WS_EX_LAYERED 属性 , 该属性使窗体支持透明 ModifyStyleEx (0,  ...

  8. [转载]在 WPF 專案中開啟 Blend

    昨天 Bill 淑在噗浪上面問了一個問題:「我有一個傻問題..當我開了一個WPF專案, 上面放了Button, 我要怎麼開啟Expression Blend來修改這個Button ?」 艾小克當時楞了 ...

  9. WPF Window 窗口获得焦点和失去焦点事件

    Window 窗口获得焦点和失去焦点事件(窗口变为背景窗口.窗口切换等都引起窗口焦点失去) Activated获得焦点事件 和 Deactivated失去焦点的事件:     Activated: 获 ...

最新文章

  1. adb logcat read: unexpected EOF!
  2. VS2015安装简单的C#单元测试
  3. centos7 开机启动文件路径_centos7定时运行python脚本
  4. 转:NAT traversal 的概念
  5. DRL实战 : 强化学习在广告点击业务中的应用
  6. MIDI入门: 用简谱轻松自定义midi音乐 [圣诞贺卡+铃儿响叮当]
  7. MFQ(海盗派探索性测试)学习记录
  8. 《分布式服务架构》读后感
  9. 域名还能绑定动态IP?真是又涨见识了,再也不用购买固定IP了,赶快收藏
  10. SICP练习1.17
  11. su组件在什么窗口,【答疑】草图大师Sketchup组件窗口快捷键是什么呢? - 羽兔网问答...
  12. tp5的时间查询,查询时间戳是否在某一天中
  13. 数据分析:大数据时代的必备技能之EXCEL
  14. linux c控制进程并发量,浅谈Linux环境下并发编程中C语言fork()函数的使用
  15. 葵花宝典:WPF自学手册(奋斗的小鸟)_PDF 电子书
  16. 注册ArcGIS Online账号||免费使用21天(保姆级)
  17. Multimodal Discriminative Binary Embedding for Large-Scale Cross-Modal Retrieval--2016.10高新波团队
  18. 穿越火线去除屏幕两边的矩形黑边
  19. java 学习网站_Java学习必不可少的十大网站
  20. u-boot编译与烧录(二)

热门文章

  1. RxJS 6有哪些新变化?
  2. sonar:查询全部项目的bug和漏洞总数(只查询阻断/严重/主要级别)
  3. JSON学习笔记-3
  4. 内存映像分析工具Eclipse Memory Analyzer
  5. 软件工程作业No.5
  6. rhel 6.4 + udev + 11.2.0.3 + asm 单点安装
  7. ZT:成熟是一种明亮而不刺眼的光辉
  8. css 中input和select混排对齐问题
  9. 申请加入 “WebGIS” 团队
  10. Ubuntu “载入软件包列表失败”