使用WPF工做6个多月了,是时候写一些WPF的基础知识了。在这个主题上我已经写了几篇文章了。他们都是基于处理一些具体的问题而完成的。如今我抛砖引玉,并让您理解如何/为何WPF做为革命性的UI开发走向了咱们。程序员
因为这是一篇适合初学者和中级水平的程序员的文章,我将尽可能给出尽量多的基本的例子。编程
Windows Presectation Foundation浏览器
正如名字所示,WPF其实是.NET Framework3.0引入的几个framework.它其实是提出了一套新的类和程序集并容许咱们更加有效和灵活的来编程。它是使用Direct3D渲染并使用显卡来展示在显示器上。所以这种形式的绘图是很平滑的而且有好好利用您的机器中安装的硬件能力的机会。针对传统的GDI应用程序,它不能使用先进的图形处理能力,所以Windows Forms应用程序和WPF应用程序相比老是很低效。还有另一个很重要的事情我必须标注一下,GDI Windows Form应用程序是利用操做系统控件来创建它的应用程序。所以它基本上很难在你的应用程序中自定义他们。WPF控件其实是在你的屏幕上绘制的,所以若是须要的话,你能够彻底自定义控件而且修改他们的行为。缓存
WPF有不少优点,让我先介绍几个:架构
WPF为应用程序引入了与设备无关的DPI设置来构建应用程序。对于一个窗口来讲,对于计算在屏幕中可以绘制多少DPI是很是重要的。这一般是由应用程序运行的硬件设备和操做系统和设备应用的DPI设置决定的。任何用户均可以自定义这些设置,所以使得引用程序看起来很糟糕。Windows Form 应用程序使用基于像素的方法以便在DPI设置变动的时候,每一个控件都可以改变它的尺寸和外观。框架
WPF解决了这个问题,而且使它可以独立于计算机的DPI设置,让咱们看看它是怎么作到的:编程语言
比方说,如图中所示你画了一个框,它是1英寸长的96DPI屏幕,如今若是你看到120DPI设置的相同的应用程序,这个框就会变小。这是由于,咱们在屏幕中看到的东西都彻底依赖于DPI设置。ide
在WPF中,这种修改是采用基于密度的方法。这意味着当像素的密度被修改使,该元素将相应的调整他们,从而使得WPF应用程序的项目是与设备无关的像素。正如你在途中看到的,这个WPF中的控件在它须要更多的像素的状况例如120DPI下,应用程序合适的调整它,使得它大小保持不变。函数
WPF应用程序做为DirectX的环境渲染,它具备图形和动画功能的主要支撑能力。有一组单独的类来实现处理动画效果和图形。在屏幕中绘制的图形也是基于矢量的也是面向对象的。这就是说,当你在WPF应用程序中绘制一个矩形,你可以很容易的从屏幕中删除它觉得Rectangle其实是一个你一直都在掌控中的对象。在传统的基于Windows的应用程序,当你绘制了一个矩形,你不能单独的选择它。所以WPF的程序设计和传统的Windows程序设计相比是彻底不一样的,也是更加复杂的。咱们将在后面讨论图形和动画的更多细节。工具
除了图形和动画方面的能力,WPF中还附带了巨大的灵活性来定义样式和控件模板。样式基于的技术就和你也许碰到过的CSS差很少,它是一组定义。它定义控件渲染到屏幕上时,看起来是个什么样子。在传统的Windows应用程序当中,样式和每一个控件是紧耦合的,所以你须要为每一个单独的控件定义颜色,样式等等使得它看起来不一样。对WPF来讲,样式和UIElement是彻底分离的。一旦你定义了一个样式,你能够这个样式应用到这个元素上市的你能够改变它的外观和风格。
一般咱们处理的大多数的UIElemnet实际上都不只仅是一个Element在用。WPF引用了一个模板的新概念,你可使用它来从新定义整个控件自己。例如上,你有一个CheckBox,它有一个Rectangle和ContentPresenter(TextBox展示的地方).而后你能够从新定义你的CheckBox而后把一个ToggleButton放在里面,使得这个Check看起来更像是个ToggleButton而不是Rectangle。这个是很是有趣的,在后续的文章中咱们将探究更多样式和控件模板的细节。
WPF中另外一个重要的特色是可访问的资源。在传统的Windows应用程序中,从新定义一个样式是很是麻烦的。所以若是你有1000个Button,你想要给每一个Button的颜色设置为Gold,你须要建立1000个对象来给每一个单独的元素分配。所以这样使得资源看起来很庞大。
在WPF中,你可以存储样式,控件,动画,甚至吧一个对象作为资源。然而每一个资源在Form加载的时候,都要声明一下,你能够进一步把资源分配给控件。你能够在一个单独的ResourceDictionary文件中维护一个全局的样式。这里的样式能够被整个应用程序使用。所以WPF应用程序很容器被应用某个主题。
在接下来的步骤中,我必需要介绍一下WPF中的新的属性系统。每一个WPF元素都定义了大量的依赖属性。依赖属性比普通属性有更强的能力。所以使得咱们定义的新属性,能够很容易的给咱们任何想要注册的对象注册属性。浙江给每一个对象都关联了一个相同的观察者。由于每一个元素都是从DependencyObject继承的,每一个元素都包含了Dependency Observer。一旦你注册了一个变量为依赖属性,在Observer上它将建立一个区域来把Control和它的值关联起来。
根据定义,XAML是一种基于XML的生命是标记语言,用于指定和设置类的特性。换句话说,XAML是一种由WPF,Silverlight或者其余能够声明本身的类的应用程序使用的语言。所以,在你的应用程序中你能够为任何类定义变量,定义属性并直接使用。当渲染应用程序的时候XAML解析器将会自动转化和建立实际的对象。
XAML一般是用来在元素和对象中静态和可视化方面定义UI布局的。咱们不能使用XAML来定义程序流。所以即便有了XAML的强大的能力,它实际上不是编程语言。它是用来为应用程序设计UI的。所以XAML使用C#,VB.NET等其余编程语言在背后定义代码的逻辑。
对于每个新技术,对它的架构有个清晰的概念都是很是必要的。所以在开始构建你的应用程序以前,你必需要掌握几个概念。若是你不喜欢了解WPF的一些细节,请跳过这一段。正如前面提到过的,WPF其实是构架整个Framework的一组程序集。这些程序集能够这样分类:
托管层
非托管层
核心API
托管层:WPF的托管层是使用一些程序集构建的。这些程序集构建了WPF的Framework来与底层的非托管API通讯,以便渲染它的内容。构成WPF框架的一些程序集是:
PresentationFramework.dll:建立顶层的布局容器,控件,窗体,样式等等。
PresentationCore.dll:它拥有基本的类型,例如派生于PresentationFramework.dll中的全部形状和控件中的UIElement和可视化元素。
WindowsBase.dll:他们拥有更多可以在WPF环境外面使用的能力的基本的元素,例如Dispatcher, Dopendency,我将稍后逐一介绍他们。
非托管类型(milcore.dll):WPF中的非托管层称为milcore或者是媒体整合库核心。它基本上是处理WPF更高层次的对象的处理,例如布局容器,按钮,动画等等来达到Direct3D的预期。它是WPF主要的渲染引擎。
WindowsCodecs.dll:这个是WPF应用程序中用来处理图像处理支持的更低层次的API。WindowsCodecs.dll包括不少将图像转换为矢量图形的编码/解码器,这些图像而后被渲染到屏幕上。
Direct3D:这个是WPF图形渲染的低层次的API。
User32:他是每一个程序都要使用的主要的核心API。它实际是管理内存和进程分离的。
GDI&Device Drivers :GDI和设备驱动是操做系统特有的,它也被用来让应用程序访问底层的API。
从上面的途中,就如上面咱们讨论的,你能够看到不一样的Framework之间的元素进行通讯有什么不一样。
在开始构建WPF应用程序以前,有几件事情,你必需要知道。Dispatcher和Thread Affinity意味着什么?当WPF应用程序启动时,它是家上是自动建立了两个线程。一个是渲染线程,这个对程序员是隐藏的,所以在你的程序中你不能直接使用渲染线程。另一个是Dispatcher Thread,实际上它包含了全部的UI元素。换句话说,你可能会想Dispatcher是实际上就是UI Thread,在WPF应用程序中它约束了全部建立的元素。相反,WPF须要全部的UI元素都与Dispather Thread 关联起来。这就是所谓的线程关联。所以你不能在其余任何线程更改在Dispatcher线程中建立的元素。它遵循Win32 API相同的限制。然而,它容许你基于API去互操做任何WPF组件为HWND。Dispatcher是一个处理线程关联的类,实际上全部的元素都是经过渠道的优先消息循环来处理的。每个UIElement都是从定义了一个叫作dispatcher属性的DispatherObject对象继承的,这个dispather属性指向了UI Thread。所以从其余的线程,若是你想要调用或者访问UI组件,你须要经过Dispatcher Thread来调用。DispatcherObject 实际上有两个主要的职责,来检查和确认是否有访问对象的权限。
什么是可视化树和逻辑树?
每一个编码样式都包含一些不一样种类的逻辑树,从而组成了整个程序。逻辑树包含在XAML中列出来的元素,所以他们只包含您在XAML中声明的控件。
另外的可视化树, 包括构成单个控件的组成部分。你一般不须要直接处理可视化树。可是你应该知道每一个控件是怎么构成的,所以使用它将更容易构建自定义模板。
我我的在使用它以前这哦你是喜欢看看可视化树。ExpressionBuilder是一个可以生成实际控件的一个工具。
在C#语言中RoutedEvent是很新的, 可是对那些从JavaScript/Web 技术过来的人说,你可能在你的浏览器中发现了它。实际上有两种RoutedEvent,一种是经过VisualTree元素到另一个元素冒泡,另一个是在VisualTree中的元素隧道。固然也有不经过冒泡和隧道的直接路由事件。
当调用了一个注册了的路由事件,它经过冒泡/隧道可视化的元素,而后在VisualTree内逐个元素的调用被注册的全部关联的RoutedEventHandler。
二者之间的区分,WPF用这样的方法来划分,Preview**做为隧道路由事件,**做为冒泡事件。例如,IsPreviewMouseDown是当鼠标抬起时在可视的元素中的隧道事件。所以在IsPreviewMouseDown时,鼠标按下最外层的元素首先被调用;在MouseDown时,最里面的元素的MouseDown事件将被调用。
每一个WPF控件都是从DependencyObject继承来的。DependencyObject是支持依赖属性的类,属性系统是WPF中新建的。每一个对象都是从DependencyObject继承而来,所以它能够关联它本身支持WPF内置的各类特性,例如EventTriggers, PropertyBindings, Animations等等。
每一个DependyencyObject实际上有个观察者列表,它声明三个方法ClearValue, SetValue, GetValue来添加/编辑/删除这些属性。所以当你用SetValue存储一些东西的时候,DependencyProperty将仅仅建立它本身。这也是资源节约的一种方式。咱们还将在其余的文章中对DependencyProperty进行详细讨论。
另一个你应该知道的很重要的事情是,你应该知道WPF的图形是怎么渲染的。事实上WPF渲染会自动检测当前的操做系统支持多少硬件加速并据此来调整它本身。图形渲染能检测合适的层来相应的渲染输出。
针对硬件渲染来讲,影响很大的几件事情是:
在WPF控件中有几个对象。让咱们逐个进行讨论,如图所示(抽象类用椭圆标注,实体类用矩形标注)
如今是时候开始建立你第一个WPF应用程序了。要建立程序,首先让咱们打开Visual Studio 2008/2010/2012/2013,这个例子,我用了Visual Studio 2008.建立一个新的项目。你将看到一个新的窗体。XAML看起来应该像这样:
<Window x:Class="FirstWindowsApplication.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" x:Name="Window1" Title="Window1" Height="300" Width="300">
<Grid>
</Grid>
</Window>
这里生成了一个空白的窗口。Height/Width表明窗体的大小。Title肯定显示在窗口的标题栏上的文本。每一个在XAML中的控件都应该利用x:Name属性来命名,被用来在XAML中引用Window对象。x:Class属性表明和当前Window有关联的类。正如我已经告诉你的,XAML不是自给自足的,所以为了定义具体逻辑你须要用C#或VB.NET写的类。
Grid是WPF应用程序中一个主要的布局对象。Grid能够有多个孩子元素。如今让咱们把一些控件放在Grid当中来。
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition MinWidth="50" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock Text="Enter Name :" Grid.Row="0" Grid.Column="0" />
<TextBox x:Name="txtName" Grid.Row="0" Grid.Column="1" MinWidth="50"/>
<Button Content="Click Me" Grid.Row="0" Grid.Column="2" Click="Button_Click"/>
</Grid>
你能够看到,我定义了一个RowDefination和一个ColumnDefination。这将容许你将Grid划分为单元格以便你能把你的控件精确的放到你想要放置的地方。针对每一个RowDefination和ColumnDefination,你可使用它的Width和Height。你能看到我用了50,Auto和 * 做为Width的值。Auto表明单元格的尺寸将在控件被定义时定义。* 表示它将占据剩余的全部控件。所以,你能够看到button占据了这个列当中剩余的全部空间。
如今,在后置代码中,我方了一个MessageBox来显示TextBox的内容。
private void Button_Click(object sender, RoutedEventArgs e) { MessageBox.Show(string.Format("Hi {0}", this.txtName.Text)); }
所以你能够看到系统会提示你你的名字,是否是颇有趣!
若是你看了这个小小的XAML,你也许很好奇在其余的控件中我怎么才能定义这个Grid属性。就像我在每一个控件中定义Grid.Row同样。使用依赖属性使得这成为可能。这是WPF新引入的一个特性。咱们将在之后详细讨论。
这是WPF系列的第一部分,这包括WPF应用程序的初步讨论,我将深刻到WPF程序的其余方面,以及你如何利用WPF控件工做并直到您构建一个解决方案。
但愿您能喜欢阅读这篇文章