WPF入门教程 - 图文

更新时间:2024-06-24 18:04:01 阅读量: 综合文库 文档下载

说明:文章内容仅供预览,部分内容可能不全。下载后的文档,内容与下面显示的完全一致。下载之前请确认下面内容是否您想要的,是否完整无缺。

http://tech.it168.com/zt/wpf/index.html WPF入门指南一:快速入门 【IT168技术文档】

摘要:WPF是微软推出的基于Windows Vista的用户界面框架,属于.NET Framework 3.0的一部分。本文旨在通过一些简单的示例,让你对WPF有一个概要的认识。

主要内容 1.概述 2.WPF的组成 3.WPF的编程架构 4.XAML

5.WPF中的控件集

一.概述

WPF (Windows Presentation Foundation)是微软推出的基于Windows Vista的用户界面框架,属于.NET Framework 3.0的一部分。它提供了统一的编程模型、语言和框架,真正做到了分离界面设计人员与开发人员的工作;同时它提供了全新的多媒体交互用户图形界面。

二.WPF的组成

WPF的构成组件如下所示,其中PresentationFramework、PresentationCore、milcore三部分是WPF的核心组件:

三.WPF的编程架构

WPF整体的编程架构如图2所示,可以看到,它其中包含了文档服务、用户界面服务、多媒体服务以及一些其它的基本服务。在用户界面服务中,提供了应用程序 服务、部署服务、控件集、布局以及数据绑定等;在多媒体服务中可以看到WPF几乎可以处理所有的媒体类型,包括图片、音频、视频、动画、2D、3D、文本 等。

四.XAML

在WPF中提供了两种API,一种是用于普通编程的API,比如我们可以用C#、VB.NET等 语言进行编程,另一种是基于XML的API,称为XAML(Extensible Application Markup Language),引入XAML使得UI代码和应用程序逻辑代码完全分离,它是一种标记语言,支持声明式编程,由于XAML是基于XML的,所以它拥有 XML的所有规则和定义,非常容易进行扩展。看下面两段简单的代码: XAML代码:

例子当中就使用了属性和子元素两种方式来指定属性。其中的Width是直接用属性表示,Background属性是用子元素表示。在多数时候,但不是所有,你可以自由选择这两种表示方式之一。

XAML被编译为BAML(Binary Application Markup Language)文件。通常,BAML文件比XAML更小,编译后的BAML都是Pre-tokenized的,这样在运行时能更快速的加载、分析 XAML等等。这些BAML文件被以资源的形式嵌入到Assembly当中。同时生成相应的代码(文件名称是**.g.cs或者**.g.vb),这些代 码根据XAML元素分别生成命名的 Attribute字段。以及加载BAML的构造函数。

最后,关于XAML的优点,我附上一点翻译过来的条款,可能更直观。

XAML除了有标记语言、XML的优点外,还有如下一些优点:

用XAML设计UI更简单

XAML比其他的UI设计技术所需编码更少。

XAML设计的UI方便转移、方便在其他环境提交。比如在Web或Windows Client。 用XAML设计动态UI非常容易

XAML给UI设计人员带来新的革命,现在所有的设计人员不再需要.NET开发的知识同样可以设计UI。在不远的将来,终端用户可以看到更漂亮的UI。 WPF指南之WPF的结构 【IT168技术文档】

WPF进入我们的生活已经有一段时间。个人认为在UI的实践中,用户需要的是易于操作的,更加绚丽的界面。这两个应该是最基本、也是最重要的宗旨。而对于 开发人员就是要用最简单的方法开发出尽可能漂亮的界面,并且效率也不能太差。除了在一些Web开发和特殊的应用中,很少有开发组配备单独的美工,至少目前 是这样吧!根据自己目前对WPF的了解程度,感觉WPF在其中某些方面确实有超强的震撼力。

客观上讲,Vista操作系统确实给我们带来了无可比拟的视觉效果。我自己深有体会,在近2个月的时间里每天都是在Vista下的开发,回家后看到XP系统,始终有些不爽的感觉。

WPF可以认为是MS利用原有.NET框架的一些特色,加上DirextX的产物。从下图的WPF组件中,我们可以看出最底层仍然是一些内核API。(以下两张图片都来自互联网。)

其中红色显示的组件是WPF的核心。Milcore是一个和DirectX交互的非托管组件,非托管代码能带给我们更高效的处理,能更好的和 DirextX交互。WPF的所有显示都是由Dirext完成的。milcore中一个非常重要的功能就是Composition引擎,这个引擎对效率的 要求很高,它的具体作用稍后介绍。所以milcore放弃了一些CLR的特征来换取效率。而另外两个红色的组件都是建立在CLR基础之上,利用了.NET 的优势。

至于其中的User32组件有什么作用,偶目前的知道的就是在WPF的某些应用场景中为了某些兼容需要使用User32,其中就有DWM(桌面窗口管理)。DWM的内容又可以写上一大堆,感兴趣的朋友可以看SDK文档。

我们除了关心WPF的基本结构外,更重要的 是WPF提供了什么功能,请看下图:

图中的每个黄色块都是一种媒体类型。这就表示WPF可以处理几乎所有的媒体类型:位图、3D、音频、视频和文本等等。通过WPF,它集成了现在的 GDI/GDI+、D3D/OPENGL以及多媒体的DSHOW等等。所有的东西都是等同对象,不管的3D还是2D,或者文本。

结构图中的Animate块贯串了整个的结构,因为在WPF中我们可以对所有的可视内容进行动画操作。这是非常让人期待的功能。Animate下面我们再 次看到了Composition引擎,前面提到过它是位于milcore组件中。开发过程中,我们的界面元素功能有多种,比如图片,视频等等,最后显示到 窗口的内容可以认为只是一张图片(准确说是Surface)。这个引擎的作用就是合成这些图片和视频元素最后进行提交显示。 WPF的数据处理 【IT168 技术文档】

数据绑定,这是WPF提 供的一个真正的优点。除了可以用在传统的绑定环境中,数据绑定已经被扩展应用到控件属性上。学习应用数据绑定,也能真正的体现XAML的好处。到底什么是 数据绑定呢?也许你从字面上已经理解的很不错了。通过数据绑定,我们在应用程序UI和程序逻辑之间建立了一种联系。正常建立绑定后,在数据的值发生改变 后,绑定到数据的元素将自动更新、体现出数据的变化。 同样,我们先看几个相关的知识点:

1、DataContext属性。设置DataContext属性,其实就是指定数据上下文。那么数据上下文又是什么呢?又是一个新的概念:数据上下文允 许元素从它的父元素继承数据绑定的数据源。很简单,在某个元素的DataContext中指定的值,那么在这个元素的子元素也可以使用。注意,如果我们修 改了

FrameworkElement或者FrameworkContentElement元素的DataContext属性,那么元素将不再继承 DataContext值。也就是说新设置的属性值将覆盖父元素的设置。如何设置DataContext属性,稍后说明。

2、数据源的种类。也许,WPF提供的数据绑定只是实现了一项普通的功能而已,但是,WPF中所支

持的多种数据源使得它的数据绑定功能将更加强大。现在,WPF支持如下四种绑定源:

(1)、任意的CLR对象:数据源可以是CLR对象的属性、子属性以及Indexers。对于这种类型的绑定源,WPF采用两种方式来获取属性值:A)、 反射(Reflection);B)、CustomTypeDescriptor,如果对象实现了ICustomTypeDescriptor,绑定将使 用这个接口来获取属性值。

(2)、XML结点:数据源可以是XML文件片断。也可以是XMLDataProvider提供的整个XML文件。 (3)、ADO.NET数据表。我对ADO.NET的了解不够,在此不做过多评论。

(4)、Dependency对象。绑定源可以是其它DependencyObject的DependencyProperty属性。

3、数据绑定的方式:(1)、OneWay,单一方向的绑定,只有在数据源发生变化后才会更新绑定目标。(2)、TwoWay,双向绑定,绑定的两端任何 一端发生变化,都将通知另一端。(3)、OneTime,只绑定一次。绑定完成后任何一端的变化都不会通知对方。

在上面的第二点我介绍了数据源的种类,注意这里的概念和下面要说明的指定数据源的方式的区别。目前,指定数据源有三种方式,我们可以通过任何一种方式来指定上述的任何一种数据源:

(1)、通过Source标记。我们可以在使用Binding使用Source标记显式指定数据源。

(2)、通过ElementName标记。这个ElementName指定了一个已知的对象名称,将使用它作为绑定数据源。

(3)、通过RelativeRource标记。这个标记将在后面说明ControlTemplate和Style时再进行说明。

现在我们说明了很多和数据源相关的内容。但是再绑定的时候,我们还需要指定绑定对象的属性名称。所以WPT提供了一个Path标记。它被用来指定数据源的属性。也即是数据源将在数据源对象的Path所指定的属性上寻找属性值。

在介绍WPF数据绑定源的种类时,第一种就是任意的CLR对象。这里需要注意的是WPF虽然支持任意的CLR对象,但是一个普通的CLR对象类却不行。我们还需要在CLR对象类上实现一种变化通知机制。

WPF把这种通知机制封装在了INotifyPropertyChanged接口当中。我们的CLR对象类只要实现了这个接口,它就具有了通知客户的能力,通常是在属性改变后通知绑定的目标。

下面是一个简单的例子,实现了一个支持通知功能的Camera类:

using System; using System.ComponentModel; using System.Windows.Media.Media3D; namespace LYLTEST { public class Camera : INotifyPropertyChanged { private PerspectiveCamera m_Camera; public event PropertyChangedEventHandler PropertyChanged; public Camera() {

m_Camera = new PerspectiveCamera(); } private void NotifyPropertyChanged(String info) { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(info)); } } public PerspectiveCamera CameraProp { get { return m_Camera; } set { if (value != m_Camera) { this.m_Camera = value; NotifyPropertyChanged(\} } } } } 这一段代码很简单,首先引入类中使用的INotifyPropertyChanged和PerspectiveCamera需要的名字空间。这里与普通 CLR类的区别在于首先有一个公有的PropertyChangedEventHandler事件类型。然后我们在.NET属性包装CameraProp 判断属性是否发生了变化,如果是,则用当前是属性名称字符串“CameraProp”调用另一个私有函数NotifyPropertyChanged。由 它根据属性的名称构造一个

PropertyChangedEventArgs对象,并完成对PropertyChanged的调用。它才是属性变化时真正 应该调用的一个通知事件。

最后一点,如果我们需要通知所以的属性都发生了变化,则将上面的属性字符串“CameraProp”用参数NULL替代即可。

前面讲过,通过实现INotifyPropertyChanged,我们可以改变使任意的CLR对象支持WPF的绑定源。但是, INotifyPropertyChanged通常只应用在单个的类属性上。在现实应用中,我们还会遇到另外一种情况:我们需要监视某一堆的数据是否发生 变化。也就是说我们绑定的数据源不再是一个单独数据对象。比如,绑定源是一个数据表时,我们希望在表中任何一条数据发生变化就能得到通知。(这里暂不考虑 WPF绑定对ADO.NET的支持。)

WPF提供了一个ObservableCollection类,它实现了一个暴露了INotifyPropertyChanged的数据集合。也就是说我 们不需要自己对每个单独的数据实现INotifyPropertyChanged结构。我们先看看如何实现一个简单的绑定数据集合。

namespace NSLYL { public class LYLDataObj { public LYLDataObj(string name, string description) { this.name = name; this.description = description; } public string Name { get { return name; } set { name = value; } } public string Description { get { return description; } set { description = value; } } private string name; private string description; } public class LYLDataObjCol : ObservableCollection { public LYLDataObjCol() { this.Add(new LYLDataObj(\this.Add(new LYLDataObj(\} } } 代码很简单,基本上就是这样的一个模板。然后,我们就可以把LYLDataObjCol绑定到一个需要多项数据的Element之上,比如ListBox、ComboBox等等。

绑定之后,只要我的LYLDataObjCol对象发送了变化,ListBox、ComboBox的数据也会有对应的变化。

到现在,我们已经知道在绑定的时候有两种指定数据源的方式:1、DataContext,关于它我们在这个Post有简单介绍。2、直接用Binding 类的Source属性。那么,我们在使用的时候如何区别呢?首先,Source的优先级比DataContext高,只有Source不存在,或者在当前 Source到不到需要的属性时才会

查找DataContext。除此之外,这两者没有真正的区别,只是建议使用Source,它能有助于我们调试应用程 序。因为通过它可以明确的得到Source的信息。而DataContext支持一种继承。可以在父Element指定Source源。这同时也成为了 DataContext的一个优点:如果多个Element需要绑定同一个Source源,那么我们只需要在一个地方指定DataContext,就可以 在其子Element使用。 WPF中的命令简述 【IT168技术文档】

在我们日常的应用程序操作中,经常要处理各种各样的命令和进行相关的事件处理,比如需要复制、粘贴文本框中的内容;上网查看网页时,可能需要返回上一网页 查看相应内容;而当我们播放视频和多媒体时,我们可能要调节音量,快速拖动到我们想看的片段等等。在Winform编程中,我们经常使用各种各样的控件来 解决此类问题,当然我们也必须编写一堆代码来处理各种各样的命令和事件处理。那么,Windows Presentation Foundation (WPF)作为微软新一代图形图像支援系统,它是如何处理这些命令及事件的呢?

在WPF中,许多控件都自动集成了固有的命令集。比如文本框TextBox就提供了复制(Copy),粘贴(Paste),裁切(Cut),撤消(Undo)和重做(Redo)命令等。

WPF提供常用应用程序所用的命令集,常用的命令集包括:ApplicationCommands, ComponentCommands, NavigationCommands, MediaCommands和EditingCommands。

ApplicationCommands(应用程序命令): CancelPrint:取消打印 Close:关闭

ContextMenu:上下文菜单 Copy:复制

CorrectionList: Gets the value that represents the Correction List command. Cut:剪切 Delete:删除 Find:查找 Help:帮助 New:新建

NotACommand:不是命令,被忽略 Open:打开 Paste:粘贴 Print:打印

PrintPreview:打印预览 Properties:属性 Redo:重做 Replace:取代 Save:保存 SaveAs:另存为 SelectAll:选择所有的 Stop:停止 Undo:撤消

ComponentCommands(组件命令):

ExtendSelection:后接Down/Left/Right/Up, 比如:ExtendSelectionDown(Shift+Down,Extend Selection Down),ExtendSelectionLeft等

Move:后接Down/Left/Right/Up, 如:MoveDown

MoveFocus:后接Down/Forward/Back/Up, 如:MoveFocusDown MoveFocusPage:后接Down/Up,如:MoveFocusPageUp

MoveTo:后接End/Home/PageDown/PageUp,比如:MoveToPageDown ScrollByLine

ScrollPage:后接Down/Left/Right/Up,比如:ScrollPageLeft SelectTo:End/Home/PageDown/PageUp,比如:SelectToEnd

NavigationCommands(导航命令):

Browse浏览: 后接Back/Forward/Home/Stop, 比如:BrowseBack 缩放显示:DecreaseZoom, IncreaseZoom, Zoom Favorites(收藏)

页面:FirstPage, LastPage, PreviousPage, NextPage,GoToPage NavigateJournal Refresh(刷新) Search(搜索)

MediaCommands(多媒体控制命令): Treble高音:DecreaseTreble,IncreaseTreble Bass低音:BoostBass,DecreaseBass,IncreaseBass Channel频道:ChannelDown,ChannelUp MicrophoneVolume麦克风音量调

节:DecreaseMicrophoneVolume,IncreaseMicrophoneVolume,MuteMicrophoneVolume ToggleMicrophoneOnOff:麦克风开关

Volume音量: DecreaseVolume,IncreaseVolume,MuteVolume Rewind, FastForward(回放,快进)

Track轨道:PreviousTrack,NextTrack [上一段(节)] Play,Pause,Stop,Record(播放,暂停,停止,录制) TogglePlayPause Select选择

EditingCommands(编辑/排版类命令):

Align对齐:AlignCenter,AlignJustify,AlignLeft,AlignRight(居中,撑满,左对齐,右对齐) Backspace退格

TabForward,TabBackward(Tab前缩,Tab向后)

FontSize字体大小:DecreaseFontSize,IncreaseFontSize Indentation缩排:DecreaseIndentation, IncreaseIndentation

Delete删除: Delete选中部分,DeleteNextWord:删除后一字,DeletePreviousWord:删除前一字 EnterLineBreak:换行 EnterParagraphBreak:换段

CorrectSpellingError/IgnoreSpellingError:纠正/忽略拼写错误

MoveUpByLine,MoveDownByLine: 上/下移一行, MoveUpByPage,MoveDownByPage: 上/下移一页

MoveUpByParagraph,MoveDownByParagraph: 上/下移一段 MoveLeftByCharacter/MoveRightByCharacter:左/右移一字符 MoveLeftByWord/MoveRightByWord 左/右移一词

MoveToDocumentStart/MoveToDocumentEnd:到文章开头/结尾 MoveToLineStart/MoveToLineEnd:到一行的开头/结尾 SelectUpByLine,SelectDownByLine:向上/下选一行 SelectUpByPage,SelectDownByPage:向上/下选一页

SelectUpByParagraph,SelectDownByParagraph:向上/下选一段 SelectLeftByCharacter,SelectRightByCharacter:向左/右选中一字 SelectLeftByWord,SelectRightByWord:向左/右选中一词

SelectToDocumentStart,SelectToDocumentEnd: 选中到篇头/篇尾 SelectToLineStart/SelectToLineEnd:选中到行首/行尾

ToggleBold, ToggleItalic, ToggleUnderline(加粗,斜体,下划线) ToggleBullets, ToggleNumbering(列表:加点,加数字) ToggleInsert:插入

ToggleSuperscript,ToggleSubscript(上标字,下标字) 先来举一个简单的例子:

XAML代码:

C#代码:

StackPanel mainStackPanel = new StackPanel(); TextBox pasteTextBox = new TextBox(); Menu stackPanelMenu = new Menu(); MenuItem pasteMenuItem = new MenuItem(); stackPanelMenu.Items.Add(pasteMenuItem); mainStackPanel.Children.Add(stackPanelMenu); mainStackPanel.Children.Add(pasteTextBox); pasteMenuItem.Command = ApplicationCommands.Paste;

上面代码演示了将对文本框设置为焦点时,菜单项可用,点击菜单项时,将执行粘贴命令。

下面列出关于Command的四个概念和四个小问题: 1、WPF中Command(命令)的四个概念: (1)命令command:要执行的动作。

(2)命令源command source:发出命令的对象(继承自ICommandSource)。 (3)命令目标command target:执行命令的主体 (4)命令绑定command binding:映射命令逻辑的对象

比 如在上面示例中,粘贴(Paste)就是命令(command), 菜单项(MenuItem)是命令源(command source), 文本框(TextBox)是命令目标对象(command target), 命令绑定到command binding文本框(TextBox)控件上。

提示:WPF中的命令都继承自ICommand接口。ICommand暴露两个方法:Execute方法、 CanExecute方法和一个事件:CanExecuteChanged。

继承自ICommandSource的有:ButtonBase, MenuItem, Hyperlink和InputBinding。 而Button, GridViewColumnHeader,ToggleButton,RepeatButton继承自ButtonBase。 System.Windows.Input.KeyBinding和MouseBinding继承自InputBinding。 2、四个小问题:

(1)如何指定Command Sources?

XAML:(请将“ApplicationCommands.Properties”换成对应的ApplicationCommands属性值,比如:

ApplicationCommands.Copy) 同等的C#代码:

StackPanel cmdSourcePanel = new StackPanel(); ContextMenu cmdSourceContextMenu = new ContextMenu(); MenuItem cmdSourceMenuItem = new MenuItem(); cmdSourcePanel.ContextMenu = cmdSourceContextMenu; cmdSourcePanel.ContextMenu.Items.Add(cmdSourceMenuItem); cmdSourceMenuItem.Command = ApplicationCommands.Properties;

(2)如何指定快捷键? XAML代码:

C#代码:

KeyGesture OpenKeyGesture = new KeyGesture( Key.B, ModifierKeys.Control); KeyBinding OpenCmdKeybinding = new KeyBinding(ApplicationCommands.Open,OpenKeyGesture); this.InputBindings.Add(OpenCmdKeybinding); //也可以这样(下面一句与上面两句的效果等同): //ApplicationCommands.Open.InputGestures.Add(OpenKeyGesture);

(3)如何Command Binding?

XAML代码:

C#代码:

CommandBinding OpenCmdBinding = new CommandBinding( ApplicationCommands.Open, OpenCmdExecuted, OpenCmdCanExecute); this.CommandBindings.Add(OpenCmdBinding);

具体的事件处理: C#代码:

void OpenCmdExecuted(object target, ExecutedRoutedEventArgs e) { MessageBox.Show(\ } void OpenCmdCanExecute(object sender, CanExecuteRoutedEventArgs e) { e.CanExecute = true; } (4)如何设置Command Target并进行绑定Command Binding? XAML代码:

C#代码:

StackPanel mainStackPanel = new StackPanel(); TextBox mainTextBox= new TextBox(); Menu stackPanelMenu = new Menu(); MenuItem pasteMenuItem = new MenuItem(); stackPanelMenu.Items.Add(pasteMenuItem); mainStackPanel.Children.Add(stackPanelMenu); mainStackPanel.Children.Add(mainTextBox); pasteMenuItem.Command = ApplicationCommands.Paste;

以上例子全是单条命令绑定的情形,事实上,你也可以多个按钮多条命令绑定到同一控件上,比如:

WPF中的控件模板(ControlTemplate)

【IT168技术文档】WPF包含数据模板和控件模板,其中控件模板又包括ControlTemplate和ItemsPanelTemplate,这里讨论一下ControlTemplate。

其实WPF的每一个控件都有一个默认的模板,该模板描述了控件的外观以及外观对外界刺激所做出的反应。我们可以自定义一个模板来替换掉控件的默认模板以便打造个性化的控件。

与Style不同,Style只能改变控件的已有属性值(比如颜色字体)来定制控件,但控件模板可以改变控件的内部结构(VisualTree,视觉树) 来完成更为复杂的定制,比如我们可以定制这样的按钮:在它的左办部分显示一个小图标而它的右半部分显示文本。

要替换控件的模板,我们只需要声明一个ControlTemplate对象,并对该ControlTemplate对象做相应的配置,然后将该ControlTemplate对象赋值给控件的Template属性就可以了。

ControlTemplate包含两个重要的属性:

1,VisualTree,该模板的视觉树,其实我们就是使用这个属性来描述控件的外观的

2,Triggers,触发器列表,里面包含一些触发器Trigger,我们可以定制这个触发器列表来使控件对外界的刺激发生反应,比如鼠标经过时文本变成粗体等。

参考以下代码 在上面的代码中,我们修改了Button的Template属性,我们定义了一个ControlTemplate,在 ... 之间包含的是模板的视觉树,也就是如何显示控件的外观,我们这里使用了一个Ellipse(椭圆)和一 个TextBlock(文本块)来定义控件的外观。

很容易联想到一个问题:控件(Button)的一些属性,比如高度、宽度、文本等如何在新定义的外观中表现出来呢?

我 们使用TemplateBinding 将控件的属性与新外观中的元素的属性关联起来Width=\Button.Width}\,这样我们就使得椭圆的宽度与按钮的宽度绑定在一起而保持一致,同理我们使用Text=\将TextBlock的文本与按钮的Content属性绑定在一起。 除了定义控件的默认外观外,也许我们想还定义当外界刺激我们的控件时,控件外观做出相应的变化,这是我们需要触发器。参考以下代码:

在上面的代码中注意到... 之间的部分,我们定义了触发器

你可以粘贴以下代码到XamlPad查看效果: 接下来的一个问题是:如果我要重用我的模板,应该怎么办呢? 你需要将模板定义为资源,其实大多数情况下,我们也是这样做的 参考以下代码: 上面的代码将我们原来的模板定义为窗体范围内的资源,其中TargetType=\指示我们的模板作用对象为Button,这样在整个窗体范围内的按钮都可以使用这个模板了,模板的使用方法也很简单:

Top