《C#程序设计教程(第3版)[精品]》-笔记

2016-06-28
WPF应用程序和Silverlight应用程序是微软公司推出的基于.NET和DirectX的应用程序编程模型。开发在Windows 7操作系统上运行的客户端应用程序时,建议用 WPF 应用程序来实现,这样可以充分发挥GPU硬件加速的性能优势。


2016-06-28
.NET框架包括两个主要组件,一个是公共语言运行库(Common Language Runtime,CLR),另一个是类库。


2016-06-28
公共语言运行库提供.NET应用程序所需要的核心服务;类库是与公共语言运行库紧密集成的可重用的类的集合,旨在为开发和运行.NET应用程序提供各种支持。 .NET 框架 4.5 版的类库由近 5 000 个类组成,这些类提供了 Internet 和企业级开发所需要的各种功能,为开发各种.NET应用程序提供了很大的方便。 类库中的每个类均按照功能划分到不同的命名空间下。


2016-06-28
Blend for Visual Studio 2012是微软公司研制的、针对VS2012的界面和原型设计工具。在设计XAML动画和生成XAML格式的三维模型时,我们需要使用这个工具。


2016-06-28
Console.WriteLine("Hello World"); (2)作为别名指令,用于简化命名空间的表达形式。 除了使用using关键字指定引用的命名空间外,还可以使用using简化命名空间的层次表达形式,例如: using System.Windows.Forms; 可以表示为 using WinForm=System.Windows.Form; 这样一来,语句 System.Windows.Form.MessageBox.Show("hello"); 就可以简写为 WinForm.MessageBox.Show("hello"); (3)作为语句,用于定义一个范围。 在C#语言中,using关键字还可用来创建using语言,该语句的作用是定义一个用大括号包围的范围,程序执行到此范围的末尾,就会立即释放在 using 的小括号内创建的对


2016-06-28
象。例如: static void Main() { using (TextWriter w = File.CreateText("test.txt")) { w.WriteLine("Line one"); w.WriteLine("Line two"); w.WriteLine("Line three"); } } 这段代码中的 using 语句表示程序执行到它所包含的语句块的末尾“}”时,会立即释放TextWriter对象占用的内存资源。 如果某个范围内有多个需要立即释放的对象,可以用嵌套的using语句来实现。


2016-06-28
Main方法只能声明为public static int或者public static void,这是C#程序的规定。另外,每一个方法都要有一个返回值,对于没有返回值的方法,必须声明返回值类型为void。 Mian方法的返回值只能有两种类型,一种是int,另一种是void。int类型的返回值用于表示应用程序终止时的状态码,其用途是退出应用程序时返回程序运行的状态(0 表示成功返回,非零值一般表示某个错误编号,错误编号所代表的含义也可以由程序员自己规定)。


2016-06-28
Main 方法可以放在任何一个类中,但为了让开发人员容易找到入口点,控制台应用程序和Windows窗体应用程序默认将其放在Program.cs文件的Program类中。


2016-06-28
XML注释方式 “///”符号是一种XML注释方式,只要在用户自定义的类型(如类、接口、枚举等)或者在其成员上方,或者命名空间的声明上方连续键入3个斜杠字符“/”,系统就会自动生成对应的XML注释标记。添加XML注释的步骤举例如下: (1)首先定义一个类、方法、属性、字段或者其他类型。例如,在Studentinfo.cs中定义一个PrintInfo方法。 (2)在类、方法、属性、字段或者其他类型声明的上面


2016-06-28
键入 3 个斜杠符号(“/”),此时开发环境就会自动添加对应的XML注释标记。例如,先编写一个PrintInfo方法,然后在该方法的上面键入3个斜杠符号后,就会得到下面的XML注释代码: /// <summary> /// /// </summary> public void PrintInfo ( ) { Console.WriteLine("姓名:{0},年龄:{1}", studentName, age); } (3)在 XML 注释标记内添加注释内容。例如,在<summary>和</summary>之间添加方法的功能描述。 以后调用该方法时,就可以在键入方法名和参数的过程中直接看到用 XML 注释的智能提示。


2016-06-28
Main方法结束前加上Console.ReadKey();语句,意思是读取键盘输入的任意一个字符,按一下键盘上的空格键、回车键或者其他任何一个字符键,就又返回到开发环境下了。


2016-06-28
Write方法与WriteLine方法的区别是后者在输出数据后,还自动输出一个回车换行符,即将光标自动转到下一行。


2016-06-28
System命名空间下的Console类提供了一个ReadLine方法,该方法从标准输入流依次读取从键盘输入的字符,并将被按下的字符立即显示在控制台窗口中,而且在用户按下回车键之前会一直等待输入,直到用户按下回车键为止。 下面的代码演示了ReadLine方法的简单用法。 string s = Console.ReadLine(); if (s == "abc") {


2016-06-28
Console.WriteLine("OK"); } 除了ReadLine方法之外,还可以用ReadKey方法读取用户按下的某一个字符或功能键,并将被按下的键值显示在控制台窗口中。ReadKey方法返回的是一个ConsoleKeyInfo类型的对象,该对象描述用户按下的是哪个键,例如: ConsoleKeyInfo c; do { c = Console.ReadKey( ); } while (c.Key != ConsoleKey.Escape); 上面这段代码的功能是一直接受用户按下的键,直到用户按下<Esc>键为止。


2016-06-28
编写C#代码时,系统提供了很多可直接插入的代码段,利用这些代码段可加快C#代码输入的速度。例如输入“for”这3个字母后,连续按两次<Tab>键,系统就会自动插入如下的代码段: for (int i = 0; i < length; i++) { } 此时可继续按<Tab>键跳转到代码段的某个修改位置,按回车键完成修改。 也可以在要插入代码段的位置处,用鼠标右键单击选择“外侧代码”的办法插入代码段。


2016-06-28
常用形式为 Console.WriteLine("格式化表示", 参数序列);


2016-06-28
或者 Console.Write("格式化表示", 参数序列); 带下画线的斜体字表示需要用具体内容替换,例如: int x=10, y=20, z=30; Console.WriteLine("{0}+{1}+{2}={3}", x, y, z, x+y+z); //输出10+20+30=60 Console.WriteLine("{3}={1}+{2}+{0}", x, y, z, x+y+z); //输出60=20+30+10 1.格式化表示的一般形式 使用格式化表示时,用“{”和“}”将格式与其他输出字符区分开。一般形式为: {N [, M][: 格式码]} 注意 格式中的中括号表示其内容为可选项。 假如参数序列为x、y、z,格式中的含义如下。


2016-06-28
• N:指定参数序列中的输出序号。例如{0}表示x,{1}表示y,{2}表示z。 • M:指定参数输出的最小长度,如果参数的长度小于M,就用空格填充;如果大于等于M,则按实际长度输出;如果M为负,则左对齐;如果M为正,则右对齐;如果未指定M,默认为0。例如{1,5}表示将参数y的值转换为字符串后按5位右对齐输出。 • 格式码:为可选的格式化代码字符串。例如{1:00000}的输出结果为00020,含义是将参数y按5位数字输出,不够5位左边补0,超过5位按实际位数输出。


2016-06-28
• 如果恰好在格式中也要使用大括号,可以用连续的两个大括号表示一个大括号,例如“{{”、“}}”。


2016-06-28
• 如果希望格式中的字符或字符串包含与格式符相同的字符,但是又希望让其原样显示时,可以用单引号将其括起来。


2016-06-28
也可以利用string.Format方法将某种类型的数据按照希望的格式转换为对应的字符串,该方法既可以在控制台应用程序中使用,也可以在其他应用程序中使用。例如: int i = 123; string s = string.Format("{0:d6}", i); //d6表示不够6位左边补0


2016-06-28
如果是一个变量,使用ToString方法更简单。例如: int n1 = 12; string s1 = n1.ToString("X4"); //X格式表示用十六进制输出。结果为:000C string s2 = n1.ToString("d5"); //结果:00012


2016-06-28
http://www.ptpedu.com.cn


2016-06-28
GDI(Graphics Device Interface,图形设备接口)是Windows 2000操作系统内核提供的技术,它提供了二维图形和文本处理功能以及功能有限的图像处理功能,但GDI没有三维图形、音频、视频等多媒体处理功能。随着Windows 2000操作系统逐渐退出历史舞台,使用GDI技术的开发人员也越来越少。


2016-06-28
GDI+引入了2D图形的反走样、浮点数坐标、渐变以及单个像素的Alpha支持,同时还支持多种


2016-06-28
图像格式。但是,GDI+没有GPU硬件加速功能,所有图形图像处理功能全部都是靠软件来实现。 从应用开发的角度来看,WinForm 已有多年的历史,其技术高度成熟,如果开发的程序不是包含动画、多媒体以及三维图形等对性能要求比较高的程序,使用WinForm编程模型可以获得比较高的开发效率和运行性能


2016-06-28
在WinForm编程模型的Main方法中,使用Application类提供的静态方法来启动、退出应用程序。Application类提供的常用方法如下。 • Run方法:用于在当前线程上启动应用程序消息循环,并显示窗体。


2016-06-28
• Exit方法:用于停止应用程序消息循环,即退出应用程序。


2016-06-28
.创建并显示窗体 要显示一个窗体,必须先创建该窗体类的实例,再调用该实例提供的方法将其显示出来。 例如: MyForm myForm = new MyForm(); myForm.Show(); 或者 MyForm myForm = new MyForm(); myForm.ShowDialog(); Show方法将窗体显示出来后就立即返回,接着程序会


2016-06-28
执行Show方法后面的语句代码,而不会等待该窗口关闭,因此,打开的窗口不会阻止用户与应用程序中的其他窗口交互。这种类型的窗口称为“无模式”窗口。 如果使用ShowDialog方法来打开窗口,该方法将窗体显示出来以后,在该窗体关闭之前,应用程序中的所有其他窗口都会被禁用,并且仅在该窗体关闭后,程序才继续执行ShowDialog方法后面的代码。这种类型的窗口称为“模式”窗口。


2016-06-28
.隐藏打开的窗体 对于“无模式”窗口,调用Hide方法即可将其隐藏起来。例如,隐藏当前打开的窗体可以用下面的语句: this.Hide(); 隐藏其他窗体可以调用实例名的Hide方法,例如: myForm.Hide(); 窗体隐藏后,其实例仍然存在,因此,可以重新调用Show方法再次将其显示出来。


2016-06-28
代码中可以直接调用Close方法关闭窗体。例如,下面的语句将关闭当前打开的窗体: this.Close(); 如果要关闭其他窗体,如关闭用语句Form2 fm = new Form2( );创建的窗体,则使用下面的语句即可。 fm.Close(); 还有一点要注意,不论程序打开了多少个窗体,也不论当前窗体是哪个窗体,只要调用了Application的Exit方法,整个应用程序就会立即退出,用法为: Application.Exit();


2016-06-28
4.注册事件 事件是响应用户操作的一种技术。在Windows窗体应用程序中,有3种注册事件的办法。 (1)在窗体的设计界面中双击控件,此时默认会自动注册最常用的事件,例如,按钮的最常用事件是Click事件,所以双击按钮注册的是Click事件。


2016-06-28
(2)选择某个控件,然后单击【属性】窗口中的“雷电”符号,就会看到该控件对应的各种事件,双击指定的事件,即可注册对应的事件。 (3)在代码中通过+=注册指定的事件,通过-=注销指定的事件。当熟悉代码后,这种办法是最灵活的,也是方便的。


2016-06-28
鼠标右击设计界面,选择【查看代码】 ,添加下面的命名空间引用: using WinFormExamples.Forms; (11)在构造函数的InitializeComponent();语句的下面,添加语句设置主窗体的启动位置。


2016-06-28
this.StartPosition = FormStartPosition.CenterScreen; 输入枚举类型的值时有一个小技巧:先输入this.StartPosition =,然后按空格键,它就会自动弹出快捷菜单可选项,选择合适的选项,按回车键即可。


2016-06-28
(12)注册Button按钮的Click事件。注册办法为:先输入ButtonHelloWorld.Click +=,然后按两次<Tab>键,就会自动得到下面的代码: public MainForm() { InitializeComponent(); this.StartPosition = FormStartPosition.CenterScreen; ButtonHelloWorld.Click += ButtonHelloWorld_Click; } private void buttonHelloWorld_Click(object sender, EventArgs e) {


2016-06-28
throw new NotImplementedException(); } ( 13 )鼠标右键单击 buttonHelloWorld_Click ,选择【重构】→【重命名】,将其换名为button_Click。


2016-06-28
(14)修改button_Click的事件处理程序,将代码改为下面的内容。 void button_Click(object sender, EventArgs e) { Button btn = sender as Button; Form fm = null; switch (btn.Text) { case "显示新窗体": fm = new HelloWorldForm(); break; } if (fm != null)


2016-06-28
{ fm.Owner = this; fm.StartPosition = FormStartPosition.CenterParent; fm.ShowDialog(this); } }


2016-06-29
在Windows窗体应用程序中,MessageBox是一个预定义的模式对话框,程序员可以通过调用MessageBox类的静态Show方法来显示消息对话框,并可以通过检查Show方法返回的值来确定用户单击了哪个按钮。注意,这里虽然


2016-06-29
使用Show方法显示消息框,但由于它本身是模式窗口,所以在消息框关闭前,不会继续执行该方法后面的代码。


2016-06-29
MessageBox的Show方法提供了多种重载形式,常用的重载形式有: public static DialogResult Show(string text) public static DialogResult Show(string text, string caption) public static DialogResult Show( string text, string caption, MessageBoxButtons buttons, MessageBoxIcon icon) 方法中各参数的含义如下。 • text:在消息框中显示的文本。 • caption:在消息框的标题栏中显示的文本。 • buttons:MessageBoxButtons枚举值之一,指定在消息框中显示哪些按钮。枚举值有OK、OKCancel、YesNoCancel和YesNo。


2016-06-29
• icon:MessageBoxIcon 枚举值之一,指定在消息框中显示哪个图标。枚举值有 None(不显示图标)、Hand(手形)、Question(问号)、Exclamation(感叹号)、Asterisk(星号)、Stop(停止)、Error(错误)、Warning(警告)和Information(信息)。


2016-06-29
1.分组(Panel、GroupBox) Panel控件和GroupBox控件均用于对控件进行分组,不同组的控件放置在不同的Panel控件和GroupBox控件内即可。Panel控件与GroupBox控件的不同之处是,Panel控件不能显示标题但可以有滚动条,而GroupBox控件可显示标题但不能显示滚动条。


2016-06-29
2.标签(Label)和文本框(TextBox) Label控件用于提供控件或窗体的描述性文字,以便为用户提供相应的信息。Label控件常用的属性是Text属性,该属性表示显示的文本内容。 TextBox 控件的主要作用是允许用户在应用程序中输入或编辑文本,也可以将控件的ReadOnly属性设为true,用来只显示文本,而不允许用户编辑文本框中所显示的内容。 在TextBox中编辑的文本可以是单行的,也可以是多行的,还可以设置为密码字符屏蔽状态作为密码输入框。 TextBox控件的常用属性如下。 • Name:指定控件的名称,以便C#代码可通过它访问控件。 • Text:获取或设置文本框中的内容。 • PasswordChar:指定作为密码输入时文本框中显示的字符,一般用“*”来显示。


2016-06-29
3.复选框(CheckBox)和单选按钮(RadioButton) CheckBox用于选择一个或者多个选项,每个选项一般用选中和非选中两种状态表示。 RadioButton 以单项选择的形式出现,即一组RadioButton按钮中只能有一个处于选中状态。一旦某一项被选中,则同组中其他 RadioButton 按钮的选中状态自动清除。


2016-06-29
ListBox(列表框)控件和ComboBox(下拉框)控件均用于显示一组条目,以便操作者从中选择一条或者多条信息,并对其进行相应的处理。两个控件的用法基本上完全一样,不同之处仅仅在于控件的外观不一样。


2016-06-29
这两个控件的常用属性和方法如下。 • SelectedIndex属性:获取或设置当前选择项的索引序号,-1表示没有选择项。 • SelectedItem属性:获取或设置当前选择项的值。 • Count属性:获取项的个数。 • Items.Add方法:添加项。 • Items.Clear方法:清除所有项。 • Items.RemoveAt方法:删除指定的项。


2016-06-29
ErrorProvider 组件一般用于提示用户输入的信息有错误,利用该组件可在指定的控件(如文本框)旁显示一个闪烁的错误图标,当用户将鼠标指针放在闪烁的图标上时,会自动显示错误信息。


2016-06-29
WPF是Windows 7、Windows 8操作系统内核默认提供的技术


2016-06-29
WPF的界面技术是通过操作系统底层支持的DirectX和GPU硬件加速来实现,而不是通过GDI+来实现。WPF应用程序用于生成能带给用户震撼视觉体验的Windows客户端应用程序。它除了具有传统WinForm应用程序的一般功能外,还具有音频、视频、文档、样式、动画以及二维和三维图形等一体化设计功能。


2016-06-29
WPF提供的图形界面呈现技术和布局控件都是矢量形式的,因此可以对窗口进行任意级别的缩放,而且画面的质量不会有任何的损耗。这是基于GDI+的WinForm应用程序无论如何优化都无法做到的。 在Windows 7、Windows 8操作系统上,用WPF应用程序模板来开发客户端应用程序能获得非常高的性能。而在Windows XP操作系统上,虽然也能运行WPF应用程序,但由于它没有操作系统的底层支持,运行性能会大打折扣,显得有些力不能及。 当然,Windows 7、Windows 8操作系统仍然继续支持GDI+,即仍然继续提供Windows窗体应用程序的实现,但


2016-06-29
是由于GDI+和DirectX是两套完全不同的实现技术,因此WinForm和WPF本质上也是两种完全不同的应用程序开发技术。


2016-06-29
Windows窗体应用程序及WPF应用程序主要用于开发基于C/S的桌面客户端应用程序,而Silverlight是一种插件式的富客户端浏览器应用程序,其功能类似于Flash。 Silverlight主要是靠客户端浏览器(IE、Firefox、Chrome)来承载运行,但也可以独立运行。 对于桌面计算机来说,Silverlight应用程序不需要在客户端安装.NET框架,因为它靠安装在客户端的Silverlight插件来运行,就像运行Flash程序时要求必须在客户端安装Flash插件一样。 对于开发人员来说,从API层面上来看,Silverlight应用程序和WPF应用程序出奇地相似,全部都是用XAML来编写页面,后台代码用C#语言来编写。但从内部实现来看,由于Silverlight应用程序需要解决跨浏览器、安装Win


2016-06-29
dows Phone操作系统的手机,以及Xbox 360游戏机3种平台的问题,因此它实际上是另一种解决方案和实现技术。 微软公司对Silverlight的定位是主要用来开发基于Windows Phone 7操作系统(简称WP7)或者Windows Phone 8操作系统(简称WP8)的程序,WP 8可安装在平板电脑、移动手机、游戏机、电视机和车载娱乐系统上。 在桌面计算机上,如果客户端使用Windows 7、Windows 8操作系统,而且希望通过IE(建议使用IE 10.0)或者Firefox 浏览器来访问基于Web 的企业级应用程序,用Silverlight 来实现开发效率也非常高,但由于 Silverlight 应用程序受浏览器的沙盒限制(即使提升权限也仍然有一定限制),所以它提供的功能没有基于C/S的WPF应用程序强大。


2016-06-29
Silverlight和WPF的语法极其相似,实际上,读者只要学会WPF应用程序和XAML语法,再学习如何用 Silverlight 来编写富客户端浏览器应用程序,可以说是轻而易举的,甚至连实现的XAML和C#代码大部分也都是完全相同的。另外,在浏览器中运行时,由于HTML5支持能在各种操作系统上运行的各种浏览器,比Silverlight更通用


2016-06-29
VS2012 还提供了很多其他类型的应用程序编程模型,比如,WCF应用程序、ASP.NET Web应用程序、ASP.NET MVC应用程序,以及只能在Windows 8操作系统上运行的Metro样式的应用程序等。


2016-06-29
C#语言的类型系统除了通过.NET框架提供了一些内置的数据类型外,还允许开发人员用类型声明(type declara


2016-06-29
tion)来创建自定义的类型,包括类类型(简称类)、结构类型(简称结构)、接口类型(简称接口)、枚举类型(简称枚举)、数组类型(简称数组)和委托类型(简称委托)等。


2016-06-29
一种是值类型,另一种是引用类型


2016-06-29
匿名类型的局部变量 如果是局部变量,在C#中还可以用var来声明其类型,当开发人员记不清楚它到底是哪种数据类型时,使用 var 声明非常方便。实际上采用这种方式声明的仍然是一种强类型的数据,只是具体的类型由编译器负责推断而已。例如: var key = Console.ReadKey(); 这条语句和下面的语句是等价的: ConsoleKeyInfo key = Console.ReadKey(); 显然,用var声明要方便得多。


2016-06-29
给变量赋值时,可采用十进制或十六进制的常数。如果是十六进制常数,在程序中必须加前缀“0x”。例如: long x = 0x12ab; //声明一个长整型变量x,并为其赋值为十六进制的数据12AB


2016-06-29
C#语言中还可以使用十六进制的转义符前缀(“\x”)或 Unicode 表示法前缀(“\u”)表示字符型常量,例如: char c2 = '\x0041'; //字母“A”的十六进制表示 char c3 = '\u0041'; //字母“A”的Unicode编码表示 对于一些特殊字符,包括控制字符和图形符号,一样可以用转义符来表示


2016-06-29
public enum MyColor{ Red, Green, Blue} 这行代码的含义是:定义一个类型名为MyColor、基础


2016-06-29
类型是int的枚举类型。它包含3个常量名:Red、Green、Blue。3个常量都是int类型,常量值分别为:0、1、2。 上面这行代码也可以写为 public enum MyColor{ Red=0, Green=1, Blue=2}


2016-06-29
定义枚举类型时,枚举中的所有常量值必须是同一种基础类型。基础类型只能是8种整型类型之一,如果不指定基础类型,默认为int类型。 如果希望基础类型不是int类型,定义时必须用冒号指定是哪种基础类型。例如: public enum Number:byte{x1=3, x2=5}; 该枚举类型定义了两个常量x1、x2,两


2016-06-29
使用枚举的好处是可以利用.NET框架提供的Enum类的一些静态方法对枚举类型进行各种操作。例如,当我们希望将枚举定义中的所有成员的名称全部显示出来,供用户选择时,可以用Enum类提供的静态GetNames方法来实现。


2016-06-29
添加 XML 注释时,一定要先定义,然后再在定义的上面按 3 个斜杠(///)添加注释,而不应先键入XML注释后再定义。 另外,如果键入源代码时格式比较混乱,希望快速调整


2016-06-29
源程序的格式,可将源程序文件中的最后一个右大括号删除,然后再键入刚删除的右大括号,此时该文件内整个源程序就会按格式自动重新排列。


2016-06-29
创建字符串的方法有多种,最常用的一种是直接将字符串常量赋给字符串变量,例如: string s1 = "this is a string."; 另一种常用的操作是通过构造函数创建字符串类型的对象。下面的语句通过将字符‘a’重复4次来创建一个新字符串: string s2 = new string('a',4); //结果为aaaa 也可以直接利用格式化输出得到希望的字符串格式。例如: string s = string.Format("{0, 30}", ' ');  //s为30个空格的字符串 string s1 = string.Format("{0, -20}", "abc"); //s1为左对齐长度为20的字符串


2016-06-29
string filePath = "C:\\CSharp\\MyFile.cs"; 为了使表达更清晰,C#规定:如果在字符串常量的前面加上@符号,则字符串内的所有内容均不再进行转义,例如: string filePath = @"C:\CSharp\MyFile.cs"; 这种表示方法和带有转义的表示方法效果相同。


2016-06-29
1.字符串比较 如果仅仅比较两个字符串是否相等,直接使用两个等号来比较即可。例如: Console.WriteLine(s1 == s2);    //结果为True Console.WriteLine(s1.Equals(s2));   //结果为True 注意 对于引用类型的对象来说,“==”是指比较两个变量是否引用同一个对象,要比较两个对象的值是否完全相同,应该使用 Equals 方法。但是对于字符串来说,“==”和Equals都是指比较两个对象的值是否完全相同。之所以这样规定,主要是因为字符串的使用场合比较多,用“==”书写起来比较方便。


2016-06-29
Contains方法用于查找一个字符串中是否包含指定的子字符串,语法为 public bool Contains( string value ) 例如: if(s1.Contains("abc")) Console.WriteLine("s1中包含abc");


2016-06-29
StartsWith方法和EndsWith方法用于从字符串的首或尾开始查找指定的子字符串,并返回布尔值(true或false)


2016-06-29
IndexOf 方法用于求某个字符或者子串在字符串中出现的位置


2016-06-29
如果希望得到一个字符串中从某个位置开始的子字符串,可以用Substring方法


2016-06-29
• 从startIndex开始插入子字符串value。 public string Insert( int startIndex, string value) • 删除从startIndex到字符串结尾的子字符串。 public string Remove(int startIndex) • 删除从startIndex开始的count个字符。 public string Remove(int startIndex, int count)


2016-06-29
• 将oldChar 的所有匹配项均替换为newChar。 public string Replace(char oldChar, char newChar) • 将oldValue 的所有匹配项均替换为newValue。 public string Replace(string oldValue, string newValue)


2016-06-29
利用 TrimStart 方法可以移除字符串首部的一个或多个字符,从而得到一个新字符串;利用TrimEnd方法可以移除字符串尾部的一个或多个字符;利用Trim方法可以同时移除字符串首部和尾部的一个或多个字符。


2016-06-29
将字符串的所有英文字母转换为大写可以用ToUpper方法,将字符串的所有英文字母转换为小写可以用ToLower方法


2016-06-29
String类表示的是一系列不可变的字符。例如,在myString的后面连接上另一个字符串: string myString = "abcd"; myString += " and 1234 "; //结果为"abcd and 1234" 对于用“+”号连接的字符串来说,其实际操作并不是在原来的 myString 后面直接附加上第二个字符串,而是返回一个新String实例,即重新为新字符串分配内存空间。显然,如果这种操作非常多,对内存的消耗是非常大的。因此,字符串连接要考虑如下两种情况:如果字符串连接次数不多(如 10 次以内),使用“+”号直接连接比较方便;如果有大量的字符串连接操作,应该使用System.Text命名空间下的StringBuilder类,这样可以大大提高系统的性能


2016-06-29
StringBuilder sb = new StringBuilder( ); sb.Append("string1"); sb.AppendLine("string2"); sb.Append("string3"); string s = sb.ToString( ); Console.WriteLine(s); 这段代码的输出结果如下: string1string2 string3


2016-06-29
数组长度是指数组中所有元素的个数。例如: int[] a = new int[10]; //定义一个有10个元素的数组,数组元素分别为a[0]、a[1]……a[9] int[,] b = new int[3, 5]; //数组长度为3*5=15,其中第0维长度为3,第1维长度为5


2016-06-29
在C#中,数组的最大容量默认为20GB。换言之,只要内存足够大,绝大部分情况都可以利用数组在内存中对数据直接进行处理。


2016-06-29
还可以一次性加载大于20GB 的数组。例如在WPF应用程序中,通过在App.xaml中配置<gcAllowVeryLargeObjects>元素即可达到这个目的。


2016-06-29
交错数组相当于一维数组的每一个元素又是一个数组,也可以把交错数组称为“数组的数组”。下面是交错数组的一种定义形式: int[][] n1 = new int[2][] { new int[] {2,4,6}, new int[] {1,3,5,7,9}


2016-06-29
}; 注意 初始化交错数组时,每个元素的new运算符都不能省略。 上面定义的数组也可以写为: int[][] n1 = new int[][] { new int[] {2,4,6}, new int[] {1,3,5,7,9} }; 或者写为: int[][] n1 = { new int[] {2,4,6}, new int[] {1,3,5,7,9} }; 还有一点要说明,交错数组的每一个元素既可以是一维数组,也可以是多维数组。例如,下面的语句中每个元素又是一个二维数组: int[][,] n4 = new int[3][,] { new int[,] { {1,3}, {5,7} }, new int[,] { {0,2}, {4,6}, {8,10} },


2016-06-29
new int[,] { {11,22}, {99,88}, {0,9} } };


2016-06-30
在实际应用中,我们可能需要计算数组中所有元素的平均值、求和、求最大数以及求最小数等,这些可以利用数组的Average方法、Sum方法、Max方法和Min方法来实现。


2016-06-30
对于字符串数组,可以直接利用string的静态Join方法和静态Split方法实现字符串和字符串数组之间的转换。 Join方法用于在数组的每个元素之间串联指定的分隔符,从而产生单个串联的字符串。它相当于将多个字符串插入分隔符后合并在一起。语法为:


2016-06-30
public static string Join( string separator, string[] value ) Split方法用于将字符串按照指定的一个或多个字符进行分离,从而得到一个字符串数组。常用语法为: public string[] Split( params char[] separator )


2016-06-30
Array是所有数组类型的抽象基类。对数组进行处理时,可以使用Array类提供的静态方法,如对元素进行排序、反转、查找等。常用有以下方法。 • Copy方法:将一个数组中的全部或部分元素复制到另一个数组中。 • Sort方法:使用快速排序算法,将一维数组中的元素按照升序排列。


2016-06-30
• Reverse 方法:反转一维数组中的元素。 另外,还可以使用该类提供的Contains方法和IndexOf方法查找指定的元素。


2016-06-30
句逐个提取集合中的元素,并对集合中每个元素执行语句序列中的操作。一般形式为: foreach ( 类型 标识符 in 表达式) { 语句序列 } “类型”和“标识符”用于声明循环变量,“表达式”为操作对象的集合。注意在循环体内不能改变循环变量的值。另外,类型也可以使用var来表示,此时其实际类型由编译器自行推断。


2016-06-30
goto语句的功能是将控制转到由标识符指定的语句。 格式: goto 标识符; 例如: static void Main(string[] args) {


2016-06-30
int i = 0; goto check; loop: Console.WriteLine(args[i++]); check: if (i < args.Length) goto loop; } 需要注意的是,虽然goto语句使用比较方便,但是容易引起逻辑上的混乱,因此除了以下两种情况外,其他情况下不要使用goto语句。 • 在switch语句中从一个case 标记跳转到另一个case 标记时。 • 从多重循环体的内部直接跳转到最外层的循环体外时。


2016-06-30
在catch块内,还可以使用throw语句将异常抛给调用它的程序。 1.try-catch语句 C#语言提供了利用 try-catch 捕捉异常的方法。在 try 块中的任何语句产生异常,都会执行catch块中的语句来处理异常。常用形式为: try


2016-06-30
{ 语句序列 } catch { 异常处理语句序列 } 或者 try { } 语句序列 catch(异常类型 标识符) {


2016-06-30
异常处理语句序列 } 在程序运行正常的时候,执行 try 块内的程序。如果 try 块中出现了异常,程序就立即转到catch块中执行。 在 catch 块中可以通过指定异常类型和标识符来捕获特定类型的异常。也可以不指定异常类型和标识符,此时将捕获所有异常。 一个try语句中也可以包含多个catch块。如果有多个catch块,则每个catch块处理一个特定类型的异常。但是要注意,由于Exception是所有异常的基类,因此如果一个try语句中包含多个catch块,应该把处理其他异常的catch块放在上面,最后才是处理Exception异常的catch块,否则的话,处理其他异常的catch块就根本无法有执行的机会。


2016-06-30
y-catch-finally语句 如果try后有finally块,不论是否出现异常,也不论是否有catch块,finally块总是会执行的,即使在try内使用


2016-06-30
跳转语句或return语句也不能避免finally块的执行。 一般在finally块中做释放资源的操作,如关闭打开的文件、关闭与数据库的连接等。


2016-06-30
有时候在方法中出现了异常,不一定要立即把它显示出来,而是想把这个异常抛出并让调用这个方法的程序进行捕捉和处理,这时可以使用throw语句。它的格式为: throw [表达式]; 可以使用 throw 语句抛出表达式的值。注意:表达式类型必须是 System.Exception 类型或从System.Exception继承的类型。 throw也可以不带表达式,不带表达式的throw语句只能用在catch块中,在这种情况下,它重新抛出当前正在由catch块处理的异常。 除了这些常用的基本语句外,C#还提供了其他一些语句,如 yield 语句、fixed 语句、unsafe语句、lock语句,以及checked和unchecked语句等。


2016-06-30
类是封装数据的基本单位,用来定义对象具有的特征(字段、属性等)和可执行的操作(方法、事件等)。自定义类的常用形式为: [访问修饰符] [static] class 类名 [: 基类 [,接口序列]] { [类成员] } 其中带中括号的表示可省略,带下画线的表示需要用实际内容替换。 一个类可以继承多个接口,当接口序列多于一项时,各项之间用逗号分隔。如果既有基类又有接口,则需要把基类放在冒号后面的第一项,基类和接口之间也是用逗号分隔。 类成员包括字段、属性、构造函数、方法、事件、运算符、索引器、析构函数等。 如果省略类的访问修饰符,默认为Internal;如果省略类成员的访问修饰符,默认为private


2016-06-30
访问修饰符 类和类的成员都可以使用下面的访问修饰符。 • public:类的内部和外部代码都可以访问。 • private:类的内部可访问,类的外部无法访问。如果省略类成员的访问修饰符,默认为private。 • internal:同一个程序集中的代码都可以访问,程序集外的其他代码无法访问。如果省略类的访问修饰符,默认为internal。


2016-06-30
• protected:类的内部或者从该类继承的子类可以访问。 • protected internal:从该类继承的子类或者从另一个程序集中继承的类都可以访问。 类的访问修饰符用于控制类的访问权限,成员的访问修饰符用于控制类中成员的访问权限。类的成员可以是常量、字段、属性、索引、方法、事件、运算符、构造函数、析构函数、嵌套类。 除了上面的访问修饰符以外,还可以使用partial修饰符,包含partial修饰符的类称为分部类。


2016-06-30
在C#语言中,通过指定类名来调用静态成员,通过指定实例名来调用实例成员。 如果有些成员是所有对象共用的,此时可将成员定义为静态(static)的,当该类被装入内存时,系统就会在内存中专门开辟一部分区域保存这些静态成员。 static关键字表示类或成员加载到内存中只有一份,而不是有多个实例。当垃圾回收器检测到不再使用该静态成员时,会自动释放其占用的内存。


2016-06-30
静态字段有两个常见的用法:一是记录已实例化对象的个数;二是存储必须在所有实例之间共享的值。 静态方法可以被重载但不能被重写,因为它们属于类,而不是属于类的实例。


2016-06-30
构造函数一般使用public修饰符,但也可以使用private创建私有构造函数。私有构造函数是一种特殊的构造函数,通常用在只包含静态成员的类中,用来阻止该类被实例化。 如果不指定构造函数的访问修饰符,默认是private。但是,一般都显式地使用private修饰符来清楚地表明该类不能被实例化。


2016-06-30
readonly关键字用于声明在程序运行期间只能初始化“一次”的字段。初始化的方法有两种,一种是在声明语句中初始化该字段,另一种是在构造函数中初始化该字段。初始化以后,该字段的值就不能再更改


2016-06-30
则其作用就和用 const声明定义一个常量相似,区别是readonly常量在运行的时候才初始化,而const 常量在编译的时候就将其替换为实际的值。另外,const 常量只能在声明中赋值,readonly常量既可以在声明中赋值,也可以在构造函数中赋值。


2016-06-30
自定义结构的常用形式为: [访问修饰符] [static] struct 结构名 [: 接口序列] { [结构成员] }


2016-06-30
结构和结构成员的访问修饰符只能是以下之一:public、private、internal。由于自定义的结构不能从其他结构继承,所以不能使用protected和protected internal。 如果省略结构的访问修饰符,默认为 internal;如果省略结构成员的访问修饰符,默认为private。


2016-06-30
使用自动实现的属性可使属性声明变得更简单,因为这种方式不再需要声明对应的私有字段。例如: class Student


2016-06-30
{ public int Age{ get; private set; } public string Name { get; set; } } 上面这段代码中,Age是只读属性,而Name则是读写属性。


2016-06-30
更多的用法(如扩展方法、分部方法、匿名方法、迭代器、动态查询、具名参数和可选参数等)


2016-06-30
C#程序中定义的方法都必须放在某个类中。定义方法的一般形式为: [访问修饰符] 返回值类型 方法名([参数序列]) { [语句序列] } 如果方法没有返回值,可将返回值类型声明为void。


2016-06-30
方法声明中的参数用于向方法传递值或变量引用。方法的参数从调用该方法时指定的实参获取实际值。有四类参数:值参数、引用参数、输出参数和参数数组。


2016-06-30
引用参数(reference parameter)用于传递输入和输出参数。为引用参数传递的实参必须是变量,并且在方法执行期间,引用参数与实参变量表示同一存储位置。


2016-06-30
引用参数使用ref修饰符声明。 与值参数不同,引用参数并没有再分配内存空间,实际上传递的是指向原变量的引用,即引用参数和原变量保存的是同一个地址。运行程序时,在方法中修改引用参数的值实际上也就是修改被引用的变量的值。 当将值类型作为引用参数传递时,必须使用ref关键字。对于引用类型来说,可省略ref关键字。


2016-07-01
抽象类使用 abstract 修饰符来描述,用于表示该类中的成员(如方法)不一定实现,即可以只有声明部分而没有实现部分。 抽象类只能用做其他类的基类,而且无法直接实例化抽象类。 带有abstract修饰符的成员称为抽象成员。 当从抽象类派生非抽象类时,非抽象类必须实现抽象类的所有抽象成员,否则会引起编译错误。 在非抽象类中实现抽象类时,必须实现抽象类中的每一个抽象方法,而且每个实现的方法必须和抽象类中指定的方


2016-07-01
法一样,即接收相同数目和类型的参数,具有同样的返回类型。


2016-07-01
还可以将类声明为密封类,以禁止其他类从该类继承。 密封类是指不能被其他类继承的类。在C#语言中,用sealed修饰符声明密封类。由于密封类不能被其他类继承,因此,系统就可以在运行时对密封类中的内容进行优化,从而提高系统的性能。 同样道理,sealed关键字也可用于防止基类中的方法被扩充类重写,带有sealed修饰符的方法称为密封方法。密封方法同样不能被扩充类中的方法继承,也不能被隐藏。


2016-07-01
继承用于简化类的设计工作量,同时还能避免设计的不一致性。一般将公共的、相同的部分放在被继承的类中,非公共的、不相同的部分放在继承的类中。 1.类继承的一般形式 在C#中,用冒号(“:”)表示继承。被继承的类叫基类


2016-07-01
或者父类,从基类继承的类叫扩充类,又叫派生类或者子类


2016-07-01
继承意味着一个类隐式地将它的基类的所有成员当作自已的成员,而且派生类还能够在继承基类的基础上继续添加新的成员。但是,基类的实例构造函数、静态构造函数和析构函数除外。


2016-07-01
C#在内部按照下列顺序处理构造函数:从扩充类依次向上寻找其基类,直到找到最初的基类,然后开始执行最初的基类的构造函数,再依次向下执行扩充类的构造函数,直至执行完最终的扩充类的构造函数为止。


2016-07-01
有以下几种实现多态性的方式。 第一种方式是通过继承实现多态性。多个类可以继承自同一个类,每个扩充类又可根据需要重写基类成员以提供不同的功能。 第二种方式是通过抽象类实现多态性。抽象类本身不能被实例化,只能在扩充类中通过继承使用。抽象类的部分或全部成员不一定都要实现,但是要在继承类中全部实现。抽象类中已实现的成员仍可以被重写,并且继承类仍可以实现其他功能。


2016-07-01
解决这个问题的办法其实很简单,只需要将 public B(int age) 改为 public B(int age):base(age) 其含义为:将 B 类的构造函数的参数 age 传递给 A 类的构造函数。程序执行时,将首先调用System.Object的构造


2016-07-01
函数,然后调用A类中带参数的构造函数,由于B的构造函数中已经将age传递给A,所以A的构造函数就可以利用这个传递的参数进行初始化。


2016-07-01
多态(new、virtual、override)


2016-07-01
在C#中,多态性的定义是:同一操作可分别作用于不同的类的实例,此时不同的类将进行不同的解释,最后产生不同的执行结果。


2016-07-01
简单地说,当建立一个类型名为A的父类的对象时,它的内容可以是A这个父类的,也可以是它的子类B的,如果子类B和父类A都定义有同样的方法,当使用B对象调用这个方法的时候,定义这个对象的类,也就是父类A中的同名方法将被调用。如果在父类A中的这个方法前加virtual关键字,并且子类B中的同名方法前面有override关键字,那么子类B中的同名方法将被调用。


2016-07-01
第三种方式是通过接口实现多态性。多个类可实现相同的“接口”,而单个类可以实现一个或多个接口。接口本质上是类需要如何响应的定义。接口仅声明类需要实现的方法、属性和事件,以及每个成员需要接收和返回的参数类型,而这些成员的特定实现留给实现类去完成。


2016-07-01
在基类中,用修饰符virtual表示某个属性或者方法可以被扩充类中同名的属性或方法重写。例如: public virtual string MyProperty{get; set;} //扩充类可以重写此属性 public virtual void myMethod( ) //扩充类可以重写此方法 { //实现代码 } 这样一来,在扩充类中就可以使用修饰符override重写基类的属性和方法了。


2016-07-01
如果重写基类的虚拟方法,必须在扩充类中用override关键字声明。


2016-07-01
在类Triangle中使用base.ShowShape( )是指调用在类Shape中声明的ShowShape方法。对基类的访问禁用了虚拟调用机制,它只是简单地将那个重写了的基类的方法视为非虚拟方法。


2016-07-01
只有在扩充类中使用override修饰符时,才能重写基类声明为virtual的方法;否则,在继承的类中声明一个与基类方法同名的方法会隐藏基类的方法。


2016-07-01
如果类B继承自类A,类C继承自类B,A中用virtual声明了一个方法M1,而B中用override声明的方法M1再次被其扩充类C重写,则B中重写的方法M1仍然使用override修饰符(而不是virtual),C中的方法M1仍然用override重写B中的M1方法。


2016-07-01
隐藏(new) 编写方法时,如果希望扩充类重写基类的方法,需要在扩充类中用override声明;如果希望隐藏基类的方法,在扩充类中需要用new声明,这就是C#语言进行版本控制的依据。 除了重写基类的方法外,还可以在扩充类中使用new修饰符来隐藏基类中同名的方法。 与方法重写不同的是,使用new关键字时并不要求基类中的方法声明为virtual,只要在扩充类的方法前声明为new,就可以隐藏基类的方法。什么情况下需要这样做呢?例如,要求开发人员需要重新设计基类中的某个方法,该基类是一年前由另一组开发人员设计的,并且已经交给用户使用,可是原来的开发人员在该方法前并没有加virtual关键字,但又不无法修改原来的程序。这种情况下显然不能使用override重写基类的方法,这时就需要隐藏基类的方法。


2016-07-01
也可以在扩充类中通过base关键字直接调用基类的方法


2016-07-01
Math类 Math类定义了各种常用的数学运算,该类位于System命名空间下,其作用有两个,一个是为三角函数、对数函数和其他通用数学函数提供常数,如PI值等;二是通过静态方法提供各种数学运算功能。


2016-07-01
Round方法的转换原则,即该方法转换为整数时采用“四舍六入五成双”的原则,就是说,以.5结尾的数字转换为整数时将舍入为最近的“偶数”位。例如,2.5舍入为2,3.5舍入为4。在大型数据事务处理中,此转换原则有助于避免趋向较高值的系统偏差。如果转换为非整数,则采用“四舍五入”的原则。


2016-07-01
ateTime表示范围在0001年1月1日午夜12:00:00到9999年12月31日晚上11:59:59之间的日期和时间,最小时间单位等于100ns。 TimeSpan表示一个时间间隔,其范围在Int63.MinVal


2016-07-01
ue到Int63.MaxValue之间。


2016-07-01
对于中文操作系统来说,默认情况下星期几和月份均显示类似“星期三”、“三月”的中文字符串形式。如果希望在中文操作系统下显示英文形式的月份和星期,还需要使用System.Globalization命名空间下的DateTimeFormatInfo类,例如: DateTime dt = new DateTime(2009, 3, 25, 12, 30, 40); System.Globalization.DateTimeFormatInfo dtInfo = new System.Globalization.CultureInfo("en-US", false).DateTimeFormat; string s = string.Format(dtInfo, "{0:yyyy-MM-dd HH:mm:ss ddd(dddd),MMM(MMMM)}", dt); Console.WriteLine(s);


2016-07-01
也可以使用ToString方法指定输出格式。例如: DateTime date1 = new DateTime(2012, 11, 18); Console.WriteLine(date1.ToString("yyyy-MM-dd ddd MMMM", new System.Globalization.CultureInfo("en-US"))); Console.WriteLine(date1.ToString("yyyy-MM-dd ddd MMMM", new System.Globalization.CultureInfo("zh-CN")));


2016-07-01
秒表、计时和随机数(Stopwatch、Timer、Random)


2016-07-01
秒表(System.Diagnostics.Stopwatch类)提供了一组方法和属性,利用Stopwatch类的实例可以测量一段时间间隔的运行时间,也可以测量多段时间间隔的总运行时间。 Stopwatch的常用属性和方法如下。 • Start方法:开始或继续运行。 • Stop方法:停止运行。 • Elapsed属性、ElapsedMilliseconds属性、ElapsedTicks属性:检查运行时间。 默认情况下,Stopwatch实例的运行时间值相当于所有测量的时间间隔的总和。每次调用Start时开始累计运行时间计数,每次调用Stop时结束当前时间间隔测量,并冻结累计运行时间值。使用Reset方法可以清除现有Stopwatch实例中的累计运行时间。


2016-07-01
System.Windows.Forms命名空间下的Timer类是一个基于Windows窗体的计时组件,利用它可在WinForm中按固定的间隔周期性地引发Tick事件,然后通过处理这个事件


2016-07-01
来提供定时处理,如每隔30ms更新一次窗体内容等。该类常用属性、方法和事件如下。 • Interval属性:获取或设置时间间隔。 • Tick事件:每当到达指定的时间间隔后引发的事件。 • Start方法:启动计时器。它和将Enabled属性设置为true的功能相同。 • Stop方法:停止定时器。它和将Enabled属性设置为false的功能相同。


2016-07-01
在 WPF 应用程序中,不能使用 WinForm 的 System.Windows.Forms.Timer 类,此时应该用System.Windows.Threading.DispatcherTimer类来实现定时


2016-07-01
System.Random 类用于生成随机数。


2016-07-01
Random 类的无参数构造函数使用系统时钟生成其种子值,但由于时钟的分辨率有限,频繁地创建不同的Random对象有可能创建出相同的随机数序列。为了避免这个问题,一般创建单个Random对象,然后利用对象提供的方法来生成随机数。


2016-07-02
在C#中,有一个特殊的关键字称为“using”。该关键字有3种用途: (1)作为引用指令,用于为命名空间导入其他命名空间中定义的类型。 为了快速引用需要的功能,一般在程序的开头引用命名空间来简化代码表示形式。如果在程序的开头加上: using System; 则: System.Console.WriteLine("Hello World"); 就可以直接写为


2016-07-04
接口可以包含方法、属性、事件和索引器。接口只包含成员的声明部分,而没有实现部分,即接口本身并不提供成员的实现,而是在继承接口的类中去实现接口的成员。 在C#语言中,使用interface关键字声明一个接口。语法为: [访问修饰符] interface 接口名称 {


2016-07-04
接口体 }


2016-07-04
接口中只能包含方法、属性、索引器和事件的声明,不能包含构造函数(因为无法构建不能实例化的对象),也不能包含字段(因为字段隐含了某些内部的执行方式)。另外,定义在接口中的属性、方法等接口体要求必须都是 public的,因此不能再用 public修饰符声明。 接口是用类或结构来实现的,实现接口的类或结构必须严格按照接口的声明来实现接口提供的功能。有了接口,就可以在不影响现有接口声明的情况下,修改接口的内部实现,从而使兼容性问题最小化。 若要实现接口成员,类中的对应成员必须是公共的、非


2016-07-04
静态的,并且必须与接口成员具有相同的名称和签名。类的属性和索引器可以为接口中定义的属性或索引器定义额外的访问器。例如,接口可以声明一个带有 get 访问器的属性,而实现该接口的类可以声明同时带有get和set访问器的同一属性。但是,如果显式实现该属性,则其访问器必须和接口中的声明完全匹配。


2016-07-04
C#语言提供了两种实现继承的方式:类继承和接口继承。类继承只允许单一继承,如果必须使用多重继承,可以通过接口继承来实现。


2016-07-04
委托类型(delegate type)用于定义一个从System.Delegate类派生的类型,其功能与C++语言中指向函数的指针功能类似,不同的是C++语言的函数指针只能够指向静态的方法,而委托除了可以指向静态的方法之外,还可以指向实例的方法。另外,委托是完全面向对象的技术,不会


2016-07-04
像C++程序一样,一不小心就会出现内存泄露的情况。 委托的最大特点是,任何类或对象中的方法都可以通过委托来调用,唯一的要求是方法的参数类型和返回类型必须与委托的参数类型和返回类型完全匹配。


2016-07-04
定义委托的一般语法为: [访问修饰符] delegate 返回类型 委托名([参数序列]); 例如: public delegate double MyFunction(double x); 这行代码定义了一个名为MyFunction 的委托。编译器编译这行代码时,会自动为其生成一个继承自System.Delegate的类型,类型的名称为MyFunction。


2016-07-04
由于事件是利用委托来实现的,因此声明事件前,需要先定义一个委托。例如: public delegate void MyEventHandler() 定义了委托以后,就可以用event关键字声明事件,例如: public event MyEventHandler Handler; 若要引发该事件,可以定义引发该事件时要调用的方法,如下例所示: public void OnHandler()


2016-07-04
{ Handler(); } 在程序中,可以通过“+ =”和“− =”运算符向事件添加委托,来注册或取消对应的事件。例如: myEvent.Handler += new MyEventHandler(myEvent.MyMethod); myEvent.Handler −= new MyEventHandler(myEvent.MyMethod);


2016-07-04
在实际的应用开发中,绝大部分情况下,实际上使用的都是具有标准签名的事件。在具有标准签名的事件中,事件处理程序包含两个参数,第1个参数是Object类型,表示引发事件的对象;第2个参数是从EventArgs类型派生的类型,用于保存事件数据。 为了简化具有标准签名的事件的用法,.NET框架为开发人员提供了以下委托: public delegate void EventHandler(object sender, EventArgs e)


2016-07-04
public delegate void EventHandler<TEventArgs>(Object sender, TEventArgs e) EventHandler委托用于不包含事件数据的事件,EventHandler<TEventArgs>委托用于包含事件数据的事件。如果没有事件数据,可将第2个参数设置为EventArgs.Empty;否则,第2个参数是从EventArgs派生的类型,在该类型中,提供事件处理程序需要的数据。


2016-07-04
匿名类型提供了一种方便的方法,利用它可将一组只读属性封装到单个对象中,而无需显式定义一个类型。例如: var v = new { ID = "0001 ", Age = 18 }; Console.WriteLine("ID:{0},年龄:{1}", v.ID, v.Age);


2016-07-05
在XAML中映射自定义命名空间 除了默认的命名空间之外,在实际开发中,我们还会经常使用自定义的对象(扩展名为.cs的文件或者扩展名为.dll的文件)。如果在XAML中引用这些对象,就必须在XAML中映射自定义命名空间。 如果在项目中添加一个类, 比如在ch07解决方案的cs文件夹下添加一个文件名为MyClass.cs的类,该文件中定义一个MyProperty属性: public class MyClass { public string MyProperty { get; set; } public MyClass() { MyProperty = "Hello"; } } 要在TestWindow.xaml文件中引用MyClass中的属


2016-07-05
性,须满足两个条件:一是MyClass类的访问修饰符必须是public;二是必须在根元素中为其指定一个XAML命名空间。例如: <Window x:Class="ch07.cs.TestWindow" …… xmlns:c="clr-namespace:ch07.cs" ……> 在这段代码中,xmlns:c中的c是自定义前缀,也可以将其换成其他字符串,比如: xmlns:myCustom="clr-namespace:ch07.cs"


2016-07-05
XAML的语法与开发Web应用程序时使用的 HTML(超文本标记语言)的语法非常相似,即都是利用元素、特性(Attribute)和属性(Property)来描述元素对象(类的实例)的各种要素。


2016-07-05
以下标记创建一个具有红色文本和蓝色背景的按钮,Content 特性用于指定在按钮上显示的文本: <Button Background="Blue" Foreground="Red" Content="按钮1"/>


2016-07-05
在XAML中,特性语法是设置对象属性的最简单有效的形式。使用时应该尽量使用特性语法来描述,只有无法用特性语法描述时才使用属性语法。


2016-07-05
如果某个属性采用集合类型,可以使用集合语法,此时在XAML标记中,声明该属性的值的所有子元素项都将自动成为集合的一部分。下面的XAML代码用集合语法为GradientStops属性设置线性渐变的值: <Window.Background> <LinearGradientBrush> <LinearGradientBrush.GradientStops> <GradientStop Offset="0.0" Color="Red" /> <GradientStop Offset="1.0" Color="Blue" /> </LinearGradientBrush.GradientStops> </LinearGradientBrush> </Window.Background>


2016-07-05
根据 XAML 规则的规定,XAML 内容属性的值必须完全在该对象元素的其他任何属性之前或之后指定


2016-07-05
XAML中的空白字符包括空格、换行符和制表符。默认情况下,XAML处理器会将所有空白字符(空格、换行符和制表符)自动转换为空格。另外,处理XAML时连续的空格将被替换为一个空格。如果希望保留文本字符串中的空格,可以在该元素的开始标记内添加xml:space="preserve"特性。但是,要避免在根级别指定该特性,否则会影响XAML处理的性能。


2016-07-05
在WPF应用程序中,有两种类型的窗口,一种是WPF窗口(简称窗口),用于直接显示WPF元素;另一种是


2016-07-05
WPF导航窗口,用于显示WPF页。


2016-07-05
按照窗口的形式来划分,可将WPF窗口分为标准窗口、无边框窗口、浮动窗口和工具窗口。标准窗口是指包含工作区和非工作区的窗口,这是 WPF 默认的窗口;无边框窗口只有工作区部分,没有非工作区部分;浮动窗口和标准窗口类似,但非工作区的右上角只有关闭按钮,不包括最小


2016-07-05
化、最大化和还原按钮;工具窗口和浮动窗口相似,但它比浮动窗口多了一个“铆钉”按钮。


2016-07-05
为了达到窗口关联这个目的,可以通过设置附属窗口的Owner属性让一个窗口拥有另一个窗口。例如: Window ownedWindow = new Window(); ownedWindow.Owner = this;


2016-07-05
ownedWindow.Show(); 通过这种方式建立关联之后,附属窗口就可以通过Owner属性的值来引用它的所有者窗口,所有者窗口也可以通过OwnedWindows属性的值来发现它拥有的全部窗口。


2016-07-05
在 WPF 应用程序中,我们可能希望在主窗口显示前先显示另一个窗口,比如登录窗口或者欢迎窗口,当用户关闭登录窗口或者欢迎窗口后再显示主窗口,要达到这个目的,可以通过主窗口的SourceInitialized事件来实现。 由于引发主窗口的SourceInitialized事件时,窗口还没有显示出来(内部正在进行初始化),所以开发人员可以在该事件处理程序中同时做一些其他的工作。


2016-07-05
{ Button btn = sender as Button; switch (btn.Content.ToString()) { case "确定": this.UserName = userNameTextBox.Text; this.Close(); break; case "取消": App.Current.Shutdown(); break; }


2016-07-05
App.Current.Shutdown();


2016-07-05
.消息框 WPF应用程序的消息框和WinForm的消息框用法类似,区别是其返回值是MessageBoxResult枚举类型,通过枚举可检查用户单击了哪个按钮。 下面的代码演示了MessageBox的典型用法。 常用形式1: MessageBox.Show("输入的内容不正确"); 常用形式2: MessageBox.Show("输入的内容不正确", "警告"); 常用形式3: MessageBoxResult result= MessageBox.Show("是否退出应用程序?", "提示",


2016-07-05
MessageBoxButton.YesNo, MessageBoxImage.Question); if (result == MessageBoxResult.Yes) { App.Current.ShutDown( ); }


2016-07-05
2.通用对话框 WPF公开的通用对话框有3个:OpenFileDialog、SaveFileDialog和PrintDialog。由于这些对话框是操作系统级别的实现,所以在各种应用程序中都可以使用。


2016-07-05
WPF页和页面导航 在WPF应用程序中,既可以用WPF窗口设计界面,也可以用WPF页(Page)设计界面,并通过Window、Frame或者NavigationWindow来承载WPF页。 如果通过Frame或者NavigationWindow来承载WPF


2016-07-05
页,还可以实现导航功能。另外,在具有导航功能的窗口中,还可以在 TextBlock 中使用超链接(HyperLink)标记链接到另一个WPF页。


2016-07-05



2016-07-05
1.在NavigationWindow中承载Page 利用C#代码将NavigationWindow窗口的Content属性设置为页的实例来承载WPF页,即将NavigationWindow作为页的宿主窗口。例如: Window w = new System.Windows.Navigation.NavigationWindow(); w.Content = new PageExamples.Page1(); w.Show(); 采用这种方式时,可以在页中设置导航窗口(NavigationWindow)的标题以及窗口大小。也可以在C#代码中使用NavigationService类提供的静态方法实现导航功能。 Page的常用属性如下。 • WindowTitle:设置导航窗口的标题。


2016-07-05
• WindowWidth和WindowHeight:设置导航窗口的宽度和高度。 • ShowsNavigationUI:false表示不显示导航条,true表示显示导航条。 • NavigationService属性:获取该页的宿主窗口中管理导航服务的对象,利用该对象可实现前进、后退、清除导航记录等操作。


2016-07-05
2.在Frame中承载Page 第二种方式是在Frame元素中将Source属性设置为要导航到的页,这是使用最方便的页导航方式,也是项目中最常用的导航方式。 在这种方式下,既可以用XAML加载页并实现导航,也可以用C#代码来实现。其宿主窗口既可以是Window,也可以是NavigationWindow。或者说,在WPF窗口中以及WPF页中,都可以使用Frame元素。例如: XAML: <Frame Name="frame1" NavigationUIVisibility="Visi


2016-07-05
ble" Source="Page1.xaml" Background="#FFF9F4D4" /> C#: frame1.Source = new Uri("Page1.xaml", UriKind.Relative);


2016-07-05
在System.Windows.dll中的System.Windows.Media命名空间下的Brushes类和Colors类都利用静态属性提供了预定义的颜色,这些颜色在各种应用程序中都可以使用。比如设置控件的前景色、背景色、边框色等。


2016-07-05
Brushes类的C#语法为 public sealed class Brushes Colors类的C#语法为 public sealed class Colors 可以看出,这两个类都是隐藏类,即只能通过它们提供的静态属性获取或设置颜色。 下面的XAML用Background设置按钮的背景色: <Button Name="btn" Background="AliceBlue" Width="60" Height="30" Content="取消"/> 下面的C#代码用Brushes类提供的静态属性实现相同的功能: btn.Background = Brushes.AliceBlue; 也可以用下面的C#代码实现相同的功能: SolidColorBrush sb = new SolidColorBrush(Colors.AliceBlue);


2016-07-05
btn.Background = sb;


2016-07-05
Color结构 在System.Windows.dll中的System.Windows.Media命名空间下,WPF还提供了一个Color结构,该结构通过A(透明度)、R(红色通道)、G(绿色通道)和B(蓝色通道)的组合来创建各种自定义的颜色。 用XAML表示颜色时,可直接用字符串表示。一般形式为“#rrggbb”或者“#aarrggbb”,其中#表示十六进制,aa表示透明度,rr表示红色通道,gg表示绿色通道,bb表示蓝色通道。也可以使用“#rgb”或者“#argb”的简写形式,例如“#00F”或者“#F00F”。 下面的XAML设置按钮的前景色、背景色、边框颜色及宽度: <Button Name="btn1" Content="确定" Background="#FFC6ECA7" Foreground="#FFE00B0B" BorderBrush="#FFFFC154" BorderThickness="5" Height="90" />


2016-07-05
在C#代码中,可分别使用Color结构的A、R、G、B属性获取或设置颜色的某个成分,还可以使用FromArgb方法来创建自定义颜色。例如: Button myButton = new Button(); myButton.Content = "A Button"; SolidColorBrush mySolidColorBrush = new SolidColorBrush(); mySolidColorBrush.Color = Color.FromArgb(255,255,0,0); myButton.Background = mySolidColorBrush;


2016-07-05
形状(Shape)是具有界面交互功能的几何图形的封装形式。System.Windows.Shapes 命名空间定义了呈现2D几何图形对象的类,这些类都继承自同一个Shape类。这些类可作为普通控件使用,就像使用【工具箱】的其他控件一样,既可以用XAML来描述,也可以用C#访问其对应的属性、方法和事件。


2016-07-05
从Shape类继承的类也称为形状控件,包括Rectangle(矩形)、Ellipse(椭圆)、Line(直线)、Polyline(折线)、Polygon(封闭的多边形)和Path(路径)。由于Rectangle和Ellipse这两个控件比较常用,所以VS2012将其放在了工具箱中,其他的形状控件没有放在工具箱内。


2016-07-06
在XAML中,可直接用特性语法声明这些属性。 1.矩形 Rectangle类用于绘制矩形。例如:


2016-07-06
<Canvas> <Rectangle Width="100" Height="100" Fill="Blue" Stroke="Red" Canvas.Top="20" Canvas.Left="20" StrokeThickness="3" /> </Canvas>


2016-07-06
在WPF应用程序中,画笔(Brush,也叫画刷)是所有控件都具有的基本功能。最常见的是利用画笔设置控件的前景色、背景色,填充渐变色、图像和图案。


2016-07-06
SolidColorBrush类提供了Color属性来创建一个纯色画笔。 在C#代码中,创建SolidColorBrush实例后,可通过Color类提供的方法设置Color属性。例如: SolidColorBrush scb = new SolidColorBrush(); scb.Color = Color.FromArgb(0xFF, 0xFF, 0x0, 0x0); button1.Background = scb;


2016-07-06
渐变画笔使用沿一条轴彼此混合的多种颜色绘制区域。可以使用渐变画笔来形成各种光和影的效果。还可以使用渐变画笔来模拟玻璃、镶边、水和其他光滑表面。 LinearGradientBrush 使用沿一条直线(即渐变轴)定义的渐变来绘制区域。可以使用GradientStop 对象指定渐变的颜色及其在渐变轴上的位置,还可以修改渐变轴创建水平和垂直渐变并反转渐变方向。 如果不指定渐变方向,LinearGradientBrush默认创建对角线渐变。下面的XAML代码使用4种颜色创建线性渐


2016-07-06
变。 <StackPanel> <!--对角线渐变--> <Rectangle Width="200" Height="100"> <Rectangle.Fill> <LinearGradientBrush StartPoint="0,0" EndPoint="1,1"> <GradientStop Color="Yellow" Offset="0.0" /> <GradientStop Color="Red" Offset="0.25" /> <GradientStop Color="Blue" Offset="0.75" /> <GradientStop Color="LimeGreen" Offset="1.0" /> </LinearGradientBrush> </Rectangle.Fill> </Rectangle> </StackPanel> 这段代码使用默认坐标系来设置起点和终点。 如果不在LinearGradientBrush中指定MappingMode


2016-07-06
属性,则渐变使用默认坐标系。默认坐标系规定控件边界框的左上角为(0,0),右下角为(1,1)。此时StartPoint和EndPoint都使用相对于边界框左上角的百分比来表示(用0~1之间的值来表示)。其中0表示0%,1表示100%。如果将 MappingMode 属性设置为“Absolute”,即采用绝对坐标系,则渐变值将不再与控件的边界框相关,而是由控件的宽度和高度决定。


2016-07-06
一般使用默认坐标系实现渐变效果。 线性渐变画笔的渐变停止点位于一条直线上,即渐变轴上。可以使用画笔的StartPoint和EndPoint属性更改直线的方向和大小,例如创建水平和垂直渐变、反转渐变方向以及压缩渐变的范围等。 渐变停止点(GradientStop)的Color属性指定渐变轴上Offset处的颜色。Offset属性指定渐变停止点的颜色在渐变轴上的偏移量位置,这是一个范围从0~1的Double值。渐变停止点的偏移量值越接近0,颜色越接近渐变起点;值越接近1,颜色越接近渐变终点。 渐变停止点之间每个点的颜色按两个边界渐变停止点指定的颜色组合执行线性内插。


2016-07-06
线性渐变(LinearGradientBrush)和径向渐变(RadialGradientBrush)都可以用两种或多种颜色渐变填充一个区域。两者的区别在于,LinearGradientBrush 总是沿一条直线定义渐变色填充区域,当然也可以通过图形变换或设定直线方向的起止点被旋转到任何位置;而RadialGradientBrush以一个椭圆为边界,从中心点开始由内向外逐渐填充渐变的颜色。 径向渐变也叫仿射渐变,意思是画笔由原点(GradientOrigin)和辐射到的范围(Center、RadiusX、RadiusY)来


2016-07-06
定义。渐变从原点(GradientOrigin)开始由强到弱逐渐向外围辐射,中心点和半径(Center、RadiusX、RadiusY)指定辐射到的椭圆范围,Center 属性指定椭圆的圆心。渐变轴上的渐变停止点指定辐射的颜色和偏移量。 下面的 XAML 代码表示用仿射渐变画笔绘制矩形的内部。 <StackPanel> <Rectangle Width="200" Height="100"> <Rectangle.Fill> <RadialGradientBrush GradientOrigin="0.5,0.5" Center="0.5,0.5 " RadiusX="0.5" RadiusY="0.5"> <GradientStop Color="Yellow" Offset="0" /> <GradientStop Color="Red" Offset="0.25" /> <GradientStop Color="Blue" Offset="0.75" /> <GradientStop Color="LimeGreen" Offset="1" /> </RadialGradientBrush> </Rectangle.Fill > </Rectangle>


2016-07-06
</StackPanel>


2016-07-06
利用WPF设计器实现画笔变换 在WPF设计器中,可直接用鼠标对各种控件进行平移(Translate)、旋转(Rotate)、缩放(Scale)、扭曲(Skew)、反转(Flip)等变换。 将鼠标放在控件的四个角的外侧可看到旋转符号,放在四条边的中间外侧可看到扭曲符号。拖放中心点可修改以哪一个点为旋转中心(即绕哪个点旋转,中心点默认在控件的左上角)。如果希望反转控件(水平翻转、垂直翻转),可通过【属性】窗口来设置。


2016-07-06
在C#中,属性(Property)是类对外公开的字段,用get和set访问器实现,这些属性实际上是公共语言运行时属性,简称CLR属性。 除了CLR属性之外,我们还要掌握依赖项属性和附加属性的概念。 1.依赖项属性 在WPF中,为了用XAML描述动态变化的属性值以及用XAML实现数据绑定,除了CLR属性之外,每个控件又用 DependencyProperty 类对 CLR 属性做了进一步的封装和扩展,这些与CLR属性对应的封装和扩展后的属性称为依赖项属性。 依赖项属性的用途是提供一种手段,让系统在XAML或者C#代码中用其他来源的值自动计算依赖项属性的值。有了这种技术,开发人员就可以在样式、主题、数据绑定、动


2016-07-06
画、元数据重写、属性值继承以及WPF设计器集成等情况下,对每个界面元素都定义一个与其CLR属性对应的依赖项属性,然后用多种方式使用这个依赖项属性,从而达到灵活控制界面元素的目的。 例如有一个Name属性为button1的按钮,在XAML中或者【属性】窗口中都可以直接设置Width依赖项属性的值: <Button Name="button1" Width="100"/> 而在 C#代码中,一般情况下通过 CLR 属性获取或设置该依赖项属性的值即可(button1.Width),此时系统会自动根据上下文处理与其对应的依赖项属性。但在动画等功能中,由于要确保它的基值(用CLR属性保存)不变,动画改变的只是与该CLR属性对应的依赖项属性,此时就必须通过SetProperty方法改变它的依赖项属性的值。动画结束后,再利用CLR属性还原基值(原始值)。


2016-07-06
之亦然。 • 在XAML以及【属性】窗口中,都是用依赖项属性来描述控件的某个属性的,此时WPF会自动维护与该依赖项属性对应的CLR属性。 • 在C#代码中,开发人员绝大部分情况下都是使用CLR属性获取或修改控件的某个属性值,此时系统会自动处理与该CLR属性对应的依赖项属性。只有在实现动画等特殊功能时,才需要设置系统无法判断该如何处理的依赖项属性的相关信息。 正是因为这些原因,用 WPF 设计界面时,除了动画等特殊功能以外,我们一般没有必要时刻去关注它到底是依赖项属性还是CLR属性,只需要统统将其作为属性来处理即可。比如设置界面中某个控件的宽度时,在XAML中是利用Width属性来设置,在C#代码中仍然是利用Width属性来设置。


2016-07-06
.附加属性 除了依赖项属性之外,在XAML中还有一个功能,该功能可以让开发人员在某个子元素上指定其父元素的属性,以


2016-07-06
这种方式声明的属性称为附加属性。 定义附加属性的一般形式为: 父元素类型名.属性名 例如: <DockPanel> <CheckBox DockPanel.Dock="Top">Hello</CheckBox> </DockPanel> 这段代码中的DockPanel.Dock就是一个附加属性,这是因为CheckBox元素本身并没有Dock这个属性,它实际上是其父元素DockPanel的属性。但是在CheckBox元素内声明了这个附加属性后,WPF分析器就可以确定该CheckBox相对于DockPanel的停靠方式。 附加属性和依赖项属性的最大不同是,依赖项属性声明的是元素自身的属性;而附加属性声明的是其父元素的属性,只是它将父元素的属性“附加”到这个元素上而已。


2016-07-06
事件 目前市场上流行的输入设备有4种,分别是键盘、鼠标、触笔和触控。针对这4种输入设备, WPF分别提供了Keyboard类、Mouse类、Stylus类和Touch类,WPF内部自动将这些类以附加事件的形式提供并在WPF元素树上传播,同时在属性窗口中公开类中对应的事件。


2016-07-06
1.在XAML中注册事件 在XAML中,声明事件的一般形式为: 事件名="事件处理程序名" 或者: 子元素类型名.事件名="事件处理程序名" 有两种声明事件的方法,开发人员可根据对事件的熟悉情况选择其中的一种。 方法1:通过事件列表附加事件。


2016-07-06
例如,选中某个 Button 元素,然后通过【属性】窗口找到 MouseDoubleClick 事件,双击其右边的输入框,它就会自动生成对应的附加事件,XAML代码示意如下: <Button Name="btn1" Content="B1" MouseDoubleClick="btn1_MouseDoubleClick"/> 方法2:在XAML中直接键入事件名称。 此时智能提示会帮助完成事件选择,并会自动添加事件处理程序的代码段。


2016-07-06
.在C#代码中注册事件 在 C#代码中注册事件的办法和 C#程序设计基础中介绍的办法相同。例如,在构造函数中键入“Button1.MouseDoubleClick +=”以后,再按<Tab>键,系统就会自动添加事件处理程序的代码段。


2016-07-06
.事件处理程序中的参数 所有WPF事件处理程序默认都提供两个参数。例如: private void OkButton_Click(object sender, RoutedEventArgs e)


2016-07-06
这里的参数sender报告附加该事件的对象,参数e是数据源的相关数据。 在WinForm应用程序中,由于事件不存在路由,所以都是用sender来判断是哪个控件引发事件,而在 WPF 应用程序中,绝大部分情况下都是用 e.Source 来判断事件源是谁。另外,如果是判断图形图像中重叠的部分,则应该用e.OriginalSource,靠命中测试来判断(只命中不是null的对象)。


2016-07-06
4.事件路由策略 路由是指在嵌套的元素树中,从某个元素开始,按照某种顺序依次查找其他元素的过程。路由事件是指通过路由将事件转发到其他元素的过程。 WPF 中的事件路由使用以下三种策略之一:直接、冒泡和隧道。这种路由方式和 Web 标准中使用的路由策略相同。因此,理解了WPF的事件路由策略,同时也就明白了在Web应用程序中如何使用事件。 (1)直接 直接(Direct)是指该事件只针对元素自身,而不会再


2016-07-06
去路由到其他元素。这种用法和WinForm应用程序中事件的用法相同。


2016-07-06
(2)冒泡 冒泡(Bubble)是指从事件源依次向父元素方向查找(即“向上”查找)。就像下面的水泡向上冒一样,直到查找到根元素为止。冒泡查找的目的是搜索父元素中是否包含针对该元素的附加事件声明。利用内部“冒泡”处理这个原理,我们就可以在某个父元素上一次性地为多个子元素注册同一个事件。例如: XAML: <Window ……> <Border BorderBrush="Gray" BorderThickness="1" Margin="154,233,201,109"> <StackPanel Background="LightGray" Orientation="Horizontal" Button.Click="Button_Click" Margin="3,33,-3,65"> <Button Name="YesButton" Content="是"


2016-07-06
Width="54" /> <Button Name="NoButton" Content="否" Width="65"/> <Button Name="CancelButton" Content="取消" Width="64"/> </StackPanel> </Border> </Window> C#: private void Button_Click(object sender, RoutedEventArgs e) { FrameworkElement source = e.Source as FrameworkElement; switch (source.Name) { case "YesButton":


2016-07-06
…… break; case "NoButton": …… break; case "CancelButton": …… break; } } 在这个元素树中,StackPanel元素中的Button.Click指明了Click事件的事件源是其子元素的某个Button,因此YesButton、NoButton和CancelButton都会引发Click事件,至于哪个引发,要看用户单击的是哪个按钮。


2016-07-06
这段代码中冒泡的含义是:当用户单击按钮时,它先看这个按钮有没有附加事件,如果有,则直接执行对应事件的处理程序,然后再向上查找其父元素(即冒泡),每当在父元素中找到一个针对按钮的附加事件声明,它就去执行与该附加事件对应的事件处理程序,找到多少个就执行多少次。


2016-07-06
在【属性】窗口中,凡是没有包含“Preview”前缀的事件都是冒泡路由事件,即都可以在父元素中用附加事件的办法来声明。


2016-07-06
在父元素内用“子元素名.事件名”的形式声明事件时有一个技巧,就是先在子元素内添加一个事件(如 Click="Button_Click"),然后将其剪切到父元素中,最后再添加子元素类型前缀(如改为 Button.Click="Button_Click")。之所以这样做,是因为父元素自身可能没有子元素对应的事件。


2016-07-06
鼠标事件 常用的鼠标事件有鼠标单击、双击,鼠标进入控件区域、悬停于控件区域、离开控件区域等


2016-07-06
6.键盘事件 WPF 提供了基础的键盘类(System.Windows.Input.Keyboard 类),该类提供有关键盘状态的信息。Keyboard的事件也是通过UIElement等XAML基元素类的事件向外提供。


2016-07-06
WPF控件有两个共同的基本模型,分别称为控件模型和内容模型。


2016-07-06
WPF控件模型 WPF应用程序的控件模型在System.Windows.Control类中实现,所有WPF控件默认都继承自Control类。从使用


2016-07-06
的角度来看,WPF控件的基本模型和Web标准的CSS盒模型非常相似,样式控制的设计思路也和CSS样式控制的实现办法相似。


2016-07-06
从示意图中可以看出,每个控件都由4个区域组成,这4个区域从里向外分别如下。 • 内容:指显示控件内容的区域,可以是文本、图像或


2016-07-06
其他控件元素。 • Padding:内边距。即边框和内容之间的矩形环区域。 • 边框:即内边距和外边距之间的黑色矩形环区域。 • Margin:外边距。指边框和图中虚线包围的矩形环区域,表示该控件和其他控件之间的距离。 在WPF控件中,这4个区域分别用对应的属性来表示,但只有部分控件公开了Padding属性和边框属性。


2016-07-06
1.外边距(Margin) Margin属性和Padding属性是使用最多的两个属性,深刻理解这两个属性的含义,对界面设计和布局非常重要。 Margin 用于描述某元素所占用的矩形区域与其容器元素的矩形区域之间的距离,也叫外边距。利用该属性可以精确地控制元素在其容器中的相对位置。


2016-07-06
在XAML中,一般用特性语法来描述Margin属性。常用有两种形式,一种是用一个值来描述,例如下面的代码表示按钮周边4个方向的外边距都是10。 XAML: <Button Name="Button1" Margin="10">按钮1</Button> C#: Button1.Margin = new Thickness(10); Button1.Content = "按钮1"; 另一种是按照“左、上、右、下”的顺序,用4个值分别描述4个方向的外边距。例如下面的代码表示Button2按钮的左、上、右、下的外边距分别是0、10、0、10: XAML: <Button Name="Button2" Margin="0,10,0,10">按钮2</Button>


2016-07-06
数字的线表示(终端只延伸到网格线),则调整窗口(或页面)大小时,该控件相对于所在单元格左上角的位置保持不变。


2016-07-06
C#: Button2.Margin = new Thickness(0, 10, 0, 10); 也可以先在XAML中选择某个元素,然后在【属性】窗口中分别设置这4个值。 另外,在 WPF 设计器中,用鼠标选择某个控件后,它就会自动在控件边缘与其近邻边缘之间用带数字的线(表示保持不变的外边距)和虚线(表示可变的外边距)来指示左、上、右、下4个边的外边距,如图8-2所示。   图8-2 外边距(Margin)的含义  在图8-2(a)中,Button1控件的上边距和左边距用带数字的线表示(终端延伸到窗口),则它相对于窗口(或页面)左上角是固定不变的,即调整窗口大小时,该控件相对于窗口左上角的位置保持不变。 在图8-2(b)中,Button2控件的上边距和左边距用带


2016-07-06
2.内边距(Padding) Padding属性用于控制元素内部与其子元素或文本之间的间距,其用法和Margin属性的用法相似。例如: XAML: <Border Background="LightBlue" BorderBrush="Black" BorderThickness="2" CornerRadius="45" Padding="25"> </Border> C#: myBorder = new Border(); myBorder.Background = Brushes.LightBlue; myBorder.BorderBrush = Brushes.Black;


2016-07-06
myBorder.BorderThickness = new Thickness(2); myBorder.CornerRadius = new CornerRadius(45); myBorder.Padding = new Thickness(25); 这段代码中的最后一行表示Border的四个方向的内边距都是25。 当然也可以像Margin属性那样,按照“左、上、右、下”的顺序,用4个值分别描述4个方向的内边距。


2016-07-06
3.水平对齐(HorizontalAlignment) 除了Margin属性和Padding属性外,要灵活地控制元素的位置,我们还需要掌握另外两个常用的属性:HorizontalAlignment属性和VerticalAlignment属性。 HorizontalAlignment 属性声明元素相对于其父元素的水平对齐方式


2016-07-06
4.垂直对齐(VerticalAlignment) VerticalAlignment属性描述元素相对于其父元素的垂直对齐方式。可能的取值分别为Top(顶端对齐)、Center


2016-07-06
(中心对齐)、Bottom(底端对齐)和Stretch(默认,垂直拉伸)。


2016-07-06
用XAML描述控件元素时,一般语法形式为 <控件元素名> 内容模型 </控件元素名> 从语法上可以看出,WPF内容模型是构成控件内容的基础。


2016-07-06
1.Text Text内容模型表示一段字符串文本。TextBox、PasswordBox都属于Text内容模型。 下面是TextBox的XAML语法,它表示其内容模型是Text: <TextBox> Text


2016-07-06
</TextBox>


2016-07-06
2.Content Content内容模型表示该内容只有“一个”对象,该对象可以是文本、图像以及其他元素。


2016-07-06
在C#中使用对象的Content属性获取或设置内容。由于Content是Object类型,因此控件的内容可以是任何对象。像 Button、RepeatButton、CheckBox、RadioButton、Image 等都属于这种模型。 下面是Button的XAML语法,它表示其内容模型是Content。 <Button> Content </Button> 下面的代码表示如何用XAML声明Button对象。 <Button Name="button1" Content="这是一个按钮"/> 下面的代码表示如何用C#设置Button对象的Content。 button1.Content="这是一个按钮";


2016-07-06
HeaderedContent表示其内容模型为一个标题和一个内容项,二者都是任意对象。 在C#中,使用对象的HeaderedContent属性获取或设置标题和内容项。 这里有一点需要提醒,TabItem 是一个特殊类型的内容控件,利用它可设置内容和标题。该控件与其他内容控件一样,也可以设置其Content属性。此外,还可以为具有Header属性的内容设置标题。Header属性属于Object类型,因此与Content属性一样,对标题可以包含的内容也没有限制。 TabControl、TreeView等控件均包含TabItem。


2016-07-07
4.Items Items 表示一个项集合。可以通过设置控件的 Items 属性来直接填充该控件的每一项。Items属性的类型为ItemCollection,该集合是泛型PresentationFrameworkCollection<T>。 在C#代码中,也可以使用Add方法向现有集合中添加


2016-07-07
项,但不能向使用ItemsSource属性创建的集合中添加项。 在XAML中,利用ItemsControl的ItemsSource属性,可以将实现IEnumerable的任何类型用作ItemsControl的内容。通常使用ItemsSource来显示数据集合或将ItemsControl绑定到集合对象。设置ItemsSource属性时,会自动为集合中的每一项创建容器。


2016-07-07
5.HeaderedItems 该内容模型表示一个标题和一个项集合。 6.Children 该内容模型表示一个或多个子元素。Children属性的类型为UIElementCollection。 UIElementCollection只能包含UIElement对象,布局控件一般采用这种内容模型。


2016-07-07
WPF的布局分类 WPF的布局类型分为两大类:绝对定位布局和动态定位布局。 1.绝对定位布局 绝对定位布局是指子元素使用相对于布局元素左上角(0,0)的坐标(x、y)来描述。在这种布局模式下,当调整布局元素的大小(Width、Height)时,子元素的坐标位置不会发生变化,所以称为绝对定位布局。 传统的WinForm应用程序使用的就是绝对布局。而在WPF应用程序中,使用绝对定位布局的控件只有Canvas控件。 2.动态定位布局 动态定位布局是指布局元素内的子元素位置以及排列顺序随着页面或窗口的大小变化动态调整。在WPF中,除了Canvas布局元素内的子元素采用绝对布局外,其他布局元


2016-07-07
素内的元素都是采用动态布局。 WinForm应用程序由于默认采用的是绝对布局,导致其实现各种变换(如旋转、缩放、动画及三维实现等)的布局非常困难,在硬件执行速度始终是瓶颈的那个时代,采用绝对布局是一种非常合理的解决办法。但是,随着GPU加速技术的广泛流行和图形硬件的快速发展,2D、3D图形的执行速度已经不是问题,因此绚丽多彩而又逼真的动态变化的界面应用逐渐成为用户的基本功能需求。在这种情况下,WinForm 技术已经不能适应时代的发展,这也是 WPF 应用程序能快速流行的主要原因之一。


2016-07-07
网格(Grid) Grid是最常用的动态布局控件,也是所有动态布局控件中唯一可按比例动态调整分配空间的控件。该控件定义由行和列组成的网格区域,在网格区域内可以放置其他控件,放置的这些控件都自动作为Grid元素的子元素。 Grid内的子元素中还可以嵌套Grid。子元素使用以下附加属性来定位。 • Grid.Row、Grid.Column:指定子元素所在的行和列。


2016-07-07
在C#代码中,使用Grid.SetRow 方法和Grid.SetCol方法指定子元素所在的行和列。 • Grid.RowSpan:使该子元素跨多行。例如Grid.RowSpan="2"表示跨2行。 • Grid.ColumnSpan:使该子元素跨多列。例如Grid. ColumnSpan ="2"表示跨2列。 有两种方式让Grid自动调整行高和列宽。 • 在Grid的行定义或列定义的开始标记内,用Auto表示行高或列宽,此时它会自动显示单元格内子元素包含的全部内容,即使内容改变也是如此。 • 在 Grid 的行定义或列定义的开始标记内,用星号(n*)根据加权比例分配网格的行和列之间的可用空间。当n为1时,可直接用一个星号(*)表示。


2016-07-07
例如Grid共有4列,第0列到第3列的宽度分别为2*、Auto、4*、Auto,则它首先按第1列和第3列元素的内容分配宽度,然后再将剩余的宽度按2∶4的比例分配给第0列和第2列。当运行程序改变窗口的大小时,会自动按这个


2016-07-07
原则重新分配宽度。


2016-07-07
堆叠面板(StackPanel) StackPanel 用于将其子元素按纵向或横向顺序排列或堆叠。没有重叠的时候称为排列,有重叠的时候称为堆叠。其常用属性为Orientation属性,表示排列或堆叠的方向,默认为纵向,如果希望横向排列或堆叠,将该属性设置为“Horizontal”即可。 在实际应用中,一般先用Grid将整个界面划分为需要的行和列,然后将StackPanel放在某个单元格内,再对该StackPanel内的多个子控件进行排列或堆叠。


2016-07-07
画布(Canvas) Canvas用于定义一个区域,称为画布。在该画布内的所有子元素都用相对于该区域左上角的坐标位置x和y来定位,单位默认为像素。画布的左上角坐标为(0,0),向右为x轴正方向,向下为y轴正方向。 Canvas的Width和Height属性默认为零,由于Canvas内的子元素是以坐标位置来定位的,所以这些子元素的垂直对齐和水平对齐不起作用。 常用属性如下。 • Canvas.Left和Canvas.Top附加属性:子元素一般使


2016-07-07
用Canvas.Left和Canvas.Top附加属性指定其相对于Canvas容器左上角的位置,Canvas.Left表示x坐标,Canvas.Top表示y坐标。 • Canvas.ZIndex附加属性:该附加属性也叫Z顺序,即三维空间中沿Z轴排列的顺序。利用该附加属性可设置Canvas内子元素重叠的顺序,该值可以是正整数,也可以是负整数,默认值为0。ZIndex值大的元素会盖住ZIndex值小的元素。 • ClipToBounds属性:当绘制内容超出Canvas范围时,true表示超出的部分被自动剪裁掉, false表示不剪裁。


2016-07-07
Canvas的优点是执行效率高,缺点是其子元素无法动态定位,也无法自动调整大小。 在C#代码中,可以通过Children属性访问Canvas子对象集合。


2016-07-07
边框(Border) Border用于在某个元素周围绘制边框,或者为某元素提供背景。其内容模型为child,即Border的子元素只能有一个,但这个子元素内可以包含多个元素。 常用属性如下。 • CornerRadius:获取或设置边框的圆角半径。 • BorderThickness:获取或设置边框的粗细。常用有两种表示形式,一种是用一个值表示(如 BorderThickness="5"),另一种是按左、上、右、下的顺序表示(如


2016-07-07
BorderThickness="15,5,15,5")。 • Padding:获取或设置Border与其包含的子对象之间的距离。 例如: <Border Background="Coral" Width="300" Padding="10" CornerRadius="20"> <TextBlock FontSize="16">带圆角边框的文本</TextBlock> </Border> 如果希望在边框内放置多个元素,可将另一个布局控件(如StackPanel)作为Border的子元素。例如: <Border BorderThickness="5" BorderBrush="Blue" > <StackPanel Grid.Column="0" Grid.Row="0"> <TextBlock Text="One"/> <TextBlock Text="Two"/> <TextBlock Text="Three"/>


2016-07-07
</StackPanel> </Border> 实际上,对任何一个元素,凡是需要边框的地方都可以用Border来实现。


2016-07-07
停靠面板(DockPanel) DockPanel 用于定义一个区域,并使该区域内的子元素在其上、下、左、右各边缘按水平或垂直方式依次停靠。 其常用属性如下。 • LastChildFill:该属性默认为true,表示DockPanel 的最后一个子元素始终填满剩余的空间。如果 DockPanel 内只有一个子元素,此时由于它同时也是最后一个子元素,所以默认会填满DockPanel空间。如果将该属性设置为false,还必须为最后一个子元素显式指定停靠方向。 • DockPanel.Dock:当DockPanel内有多个子元素时,每个子元素都可以用DockPanel.Dock附加属性指定其在父元素中的停靠方式。


2016-07-07
Focusable:默认情况下,DockPanel不接收焦点。要强制使DockPanel接收焦点,可将该属性设置为true。 【例8-5】演示DockPanel的基本用法。


2016-07-07
按钮(Button、RepeatButton) 按钮(Button)是最基本的控件之一。在按钮上除了可显示一般的文字之外,还可以显示图像,或者同时显示图像和文字。 RepeatButton和Button类似,但RepeatButton在从按下按钮到释放按钮的时间段内会自动重复引发其Click事件。利用Delay属性可指定事件的开始时间,利用Interval属性可控制重复的间隔时间。


2016-07-07
文本块(TextBlock)和标签(Label) 这两个控件都可以用来显示文本。一般情况下,当要显示一些简短的语句时,应使用 TextBlock元素;当显示的文本极少时,可以使用Label。另外,将控件绑定到字符串时,用TextBlock比用Label效率高。 1.TextBlock TextBlock主要用于显示可格式化表示的只读文本信息。最常用的是Text属性,例如: <TextBlock Margin="10" FontFamily="Arial" FontSize="20" Text="TextBlock" /> 2.Label Label的内容模型是Content,因此它还可以包含其他对象。一般将Label与TextBox一起使用,用于显示描述性信息、验证信息或输入指示信息。例如: XAML: <Label Name="ageLabel" >年龄:</Label>


2016-07-07
C#: Label ageLabel = new Label(); ageLabel.Content = "年龄:";


2016-07-07
文本框(TextBox、PasswordBox、RichTextBox) TextBox、PasswordBox 和 RichTextBox 控件都属于文本框控件。文本框控件的主要作用是让用户输入或编辑文本。 1.TextBox TextBox控件用于显示或编辑纯文本字符。常用属性如下。 • Text:表示显示的文本。 • MaxLength:用于限制用户输入的字符数。 • TextWrapping:设置控制是否自动转到下一行,当其值为“Wrap”时,该控件可自动扩展以容纳多行文本。 • BorderBrush:设置边框颜色。


2016-07-07
• BorderThickness:设置边框宽度,如果不希望该控件显示边框,将其设置为0即可。


2016-07-07
PasswordBox控件用于密码输入,常用属性如下。 • PasswordChar属性:设置掩码,即不论输入什么字符,显示的都是用它指定的字符。 • Password属性:输入的密码字符串。 • PasswordChanged事件:当密码字符串改变时发生。 除了这两个属性之外,其他用法和TextBox相同。


2016-07-07
RichTextBox 控件用于复杂格式的文本输入。当需要用户编辑已设置格式的文本、图像、表或其他支持的内容时,可选择RichTextBox控件。


2016-07-07
使用TextBox、PasswordBox和RichTextBox时,一定要注意,如果希望检测文本是否发生更改,TextBox 和 RichTextBox 控件应该使用 TextChanged 事件,PasswordBox 控件应该使用PasswordChanged事件,绝不能使用KeyDown、MouseDown或者MouseUp事件来判断。因为这3个控件对这些事件做了特殊的内置处理,强制使用KeyDown事件有可能会出现“偶尔”无法响应的情况,而对 MouseDown、MouseUp 事件则根本不会响应


2016-07-07
单选按钮一般用于从多个选项中选择一项。 RadioButton的内容模型是一个ContentControl,即它所包含的对象元素可以是任何类型(字符串、图像或面板等),但只能包含一个对象元素。 • GroupName属性:分组。将同一组的多个RadioButton的该属性设置为同一个值。用户一次只能选择同一组中的一项,一旦某一项被选中,同组中其他的 RadioButton 将自动变为非选中状态。 • IsChecked属性:判断是否选中某个单选按钮,如果被选中,则为true,否则为false。


2016-07-07
复选框(CheckBox) 复选框一般用于让用户同时选择多个选项(选中、不选中),或者选择某一选项的“是、否、不确定”3种不同的状态之一。 CheckBox控件继承自ToggleButton,一般用于让用户选择一个或者多个选项。可以用选中表示“是”,未选中表示“否”。该控件也可以表示 3 种状态,如当一个树形结构的某个节点所包含的子节点有些“选中”有些“未选中”,此时该表示节点的状态就可以用“不确定”来表示


2016-07-07
CheckBox的内容模型是一个ContentControl,即它可以包含任何类型的单个对象(如字符串、图像、面板等)。 常用属性和事件如下。 • Content属性:显示的文本。 • IsChecked属性:true表示选中,false表示未选中,none表示不确定。 • IsThreeState 属性:如果支持3 种状态,则为true;否则为false。默认值为false。如果该属性为true,可将IsChecked属性设置为null作为第3种状态。 • Click事件:单击复选框时发生。利用该事件可判断是三种状态中的哪一种。 • Checked事件:复选框选中时发生。 • UnChecked事件:复选框未选中时发生。 每一个CheckBox所代表的选中或未选中都是独立的,当用多个CheckBox控件构成一组复合选项时,各个 CheckBox 控件之间互不影响,即用户既可以只选择一项,也可以


2016-07-07
同时选中多项,这就是复选的含义。


2016-07-07
列表框(ListBox)和下拉框(ComboBox) ListBox用于显示一组选项,内容模型都是Items,即每个选项既可以是字符串,也可以是图像。 ComboBox是TextBox和可弹出的ListBox的组合,它除了比ListBox多了一个TextBox以外,对于每个选项的操作与ListBox的用法完全相同。 这两个控件的常用属性、方法和事件如下。 • Count属性:获取列表项的个数。 • SelectedIndex属性:获取当前选定项从0开始的索引号,未选择任何项时该值为−1。 • SelectedItem属性:获取当前选定的项,未选择任何项时该值为null。 • SelectionMode属性:选择列表项的方式,有以下取值。 ■ Single(默认值):每次只能选择一项。 ■ Multiple:每次可选择多项,单击对应项选中,再次


2016-07-07
击取消选中。 ■ Extended:按下<Shift>键可同时用鼠标选择多个连续项,按下<Ctrl>键可同时用鼠标选择多个不连续的项。 • Items.Add方法:向ListBox的项列表添加项。 • Items.Clear方法:从集合中移除所有项。 • Items.Contains方法:确定指定的项是否位于集合内。 • Items.Remove方法:从集合中移除指定的对象。 • SelectionChanged事件:当选择项发生改变时引发此事件。 一般将ListBox(或者ComboBox)和数据绑定一起使用。当将ListBox(或者ComboBox)绑定到数据源时,通常需要获取 ListBoxItem (或者 ComboBoxItem )选项,此时可通过ItemContainerGenerator来实现。例如: private void GetIndex0(object sender, RoutedEventArgs e)


2016-07-07
{ ListBoxItem lbi = (ListBoxItem)(lb.ItemContainerGenerator.ContainerFromIndex(0)); Item.Content = "第0项是:" + lbi.Content.ToString() + "."; }


2016-07-07
菜单(Menu)和快捷菜单(ContextMenu) Menu 控件称为菜单,用于将关联的操作分组或提供上下文帮助,该控件可以显示在窗口的任何一个位置,但一般显示在窗口的顶部。


2016-07-07
ContextMenu 控件称为快捷菜单,也叫右键快捷菜单或上下文菜单。该控件除了是右键弹出菜单外,其他用法与Menu控件的用法相同。 这两个控件的菜单项都是通过MenuItem来实现的,MenuItem内还可以嵌套MenuItem,从而实现多级菜单。 在MenuItem中,设置IsCheckable="true"可让其对应菜单项上有勾选的记号(默认为false)。另外,在Header中,可以用InputGestureText设置快捷键,还可以通过Command设置系统命令(剪切、复制、粘贴等)。


2016-07-07
工具条(ToolBar、ToolBarTray)和状态条(StatusBar) ToolBar 一般显示在窗口上方,它可以由多个 Button、CheckBox、RadioButton、ComboBox等排列组成,通过这些项可以快速地执行程序提供的一些常用命令。


2016-07-07
ToolBarTray是ToolBar的容器,该容器内可放置多个ToolBar,并可以用鼠标拖动调整ToolBar在容器中的排列顺序。 StatusBar一般显示在窗口下方,以水平排列形式显示图像和状态信息。


2016-07-07
Image是一个框架元素,一般用它来显示单帧图像。 该控件可显示下列图像类型:.bmp、.gif、.ico、.jpg、.png、.wdp和.tif。但是,Image控件不支持gif、tif等多帧图像的动画显示


2016-07-07
可以用该控件的Stretch属性获取或设置图像的拉伸方式。例如: <Image Source="/images/img1.jpg" Stretch="Fill" /> 用Image显示图像时,如果不指定图像的宽度和高度,它将按图像的原始大小加载和显示;如果只指定图像的宽度或者高度之一,而不是同时指定两者,此时它会自动保持原始图像的宽高比,不会产生扭曲或变形的情况;如果同时指定图像的高度和宽度而没有用Stretch属性指定拉伸模式,则它将自动按Uniform方式拉伸图像。


2016-07-07
WPF还提供了很多项目开发中非常实用的其他控件。例如日期控件、Popup控件、


2016-07-07
ProgressBar控件、ToolTip控件,以及用于文档显示与查看的流文档控件,用于设计类似于Office 2007样式的Ribbon控件等。


2016-07-07
XAML 资源是指用 XAML 描述的在应用程序中的不同位置可以重用的对象,例如样式(Style)、画笔(Brush)等


2016-07-07
都是XAML资源。注意XAML资源和扩展名为.resx的资源文件不是一个概念。换句话说,XAML 资源的扩展名是.xaml 而不是.resx,这些文件的“生成操作”属性都是“Page”,而且这些文件都会被编译到程序集中。


2016-07-07
在VS2012中,如果将窗口(Window)、导航窗口(NavigationWindow)、页(Page)、流文档(FlowDocument)或资源字典(ResourceDictionary)添加到项目中,其“生成操作”属性默认都是“Page”。


2016-07-07
1.声明和引用XAML资源 在XAML中,用元素的Resources属性来声明XAML资源。例如: <StackPanel> <StackPanel.Resources> <SolidColorBrush x:Key="MyBrush" Color="Gold"/> <Style x:Key="Title" TargetType="TextBlock"> <Setter Property="HorizontalAlignment" Value="Center" />


2016-07-07
<Setter Property="FontSize" Value="30" /> </Style> </StackPanel.Resources> </StackPanel> 这段 XAML 用 StackPanel 的 Resources 属性为这个 StackPanel 声明了两个资源,一个是SolidColorBrush,另一个是Style。其中的x:Key是一种XAML标记扩展,它的作用是为该XAML资源设置唯一的键。在典型的WPF应用程序中,x:Key的使用率约占90%以上。 在元素的Resource属性中声明了XAML资源以后,就可以在该元素的子元素中利用XAML标记扩展引用声明的XAML资源。 XAML标记扩展是指在特性语法中将被扩展的特性值用大括号({和})括起来,然后由紧跟在左大括号后面的字符串来标识被扩展的特性类型。例如: <Page.Resources> <Style x:Key="TitleText" TargetType="TextBlock">


2016-07-07
<Setter Property="HorizontalAlignment" Value="Right" /> <Setter Property="FontFamily" Value="楷体" /> <Setter Property="FontSize" Value="16" /> </Style> </Page.Resources> …… <TextBlock Style="{StaticResource TitleText}">你好!</TextBlock> 在最后一行XAML中,Style特性值中的大括号就是标记扩展,这行XAML的意思是以静态资源的方式引用TitleText中定义的样式。


2016-07-07
.静态资源和动态资源 根据引用XAML资源的方式,可将XAML资源分为静态资源和动态资源。 (1)静态资源


2016-07-07
静态资源(StaticResource)是指用{StaticResource keyName}标记扩展引用的资源。例如在Style中声明SolidColorBrush对象后,就可以在XAML元素的开始标记内将其作为静态资源来引用: <DockPanel.Resources> <SolidColorBrush x:Key="MyBrush" Color="Gold"/> </DockPanel.Resources> <Button Background="{StaticResource MyBrush}" Content="金色背景" /> WPF在加载XAML的过程中,会首先查找所有静态资源,并将资源值替换为实际的属性值。换言之,这种将静态资源替换为具体属性值的过程是在加载窗口或页的过程中一次性完成的,因此以后执行时效率比较高,但也正是因为它不是每次使用属性值时都去查找资源引用,所以无法在执行过程中动态改变它的值。 (2)动态资源 动态资源(DynamicResource)是指用{DynamicRe


2016-07-07
source keyName}标记扩展引用的资源。例如: <Page.Resources> <Style x:Key="TitleText" TargetType="TextBlock"> <Setter Property="HorizontalAlignment" Value="Right" /> <Setter Property="FontFamily" Value="楷体" /> <Setter Property="FontSize" Value="16" /> </Style> </Page.Resources> <TextBlock Style="{DynamicResource TitleText}">你好!</TextBlock> 在程序运行过程中,每次用到某个属性的值时,WPF都会去查找该属性引用的资源。这种解决办法增加了应用程序的灵活性,提高了应用程序的开发效率。但由于程序运行过程中每次查找资源都需要时间,因此动态资源的执行速度没有静态资源快。


2016-07-07
WPF应用程序中的样式是利用XAML资源来实现的,即在XAML资源中用Style元素声明样式和模板,并在控件中引用它。Style元素的常用形式为: <Style x:Key=键值 TargetType="控件类型" BasedOn="其他样式中定义的键值">


2016-07-07
在Style元素的开始标记内,用x:Key为样式设置键值;用TargetType指定控件的类型;用BasedOn继承其他XAML资源中已经定义的样式。这种方式既实现了类似CSS样式级联的效果,又大大增加了WPF样式控制的灵活性。 1.隐式样式设置(只声明TargetType) 在Style元素的开始标记内,可以只声明TargetType而不声明x:Key,此时x:Key的值将隐式设置为和TargetType的值相同,该样式对其控制范围内的所有TargetType中声明的控件类型都起作用。例如: <Style TargetType="Button"> <Setter Property="Foreground" Value="Green"/> <Setter Property="Background" Value="Yellow"/> </Style> 该样式表示对它所控制范围内的所有的 Button 控件都起


2016-07-07
2.显式样式设置(只声明x:Key) 声明了x:Key的样式称为显式样式设置,即控件只有显式用Style特性引用该x:Key的值时才会起作用。 如果只声明x:Key而不声明TargetType,则必须在Setter中用Property="Control.Property"设置Setter对象的属性。例如: <Style x:Key="Style1"> <Setter Property="Control.Foreground" Value="Green"/> <Setter Property="Control.Background" Value="Yellow"/> </Style> 这样一来,Button、TextBox、TextBlock等各种类型的控件都可以通过Style1引用这种样式。例如:


2016-07-07
<Button Style="{StaticResource Style1}"/>


2016-07-07
3.同时声明x:Key和TargetType 如果同时声明x:Key和TargetType,则只有引用了x:Key的值且控件类型为TargetType中指定的类型的控件才会起作用,而且引用了显式样式的控件将不再应用隐式样式。例如: <Window.Resources> <Style x:Key="t1" TargetType="TextBlock"> <Setter Property="Foreground" Value="White" /> <Setter Property="Background" Value="Blue" /> </Style> <Style TargetType="TextBlock"> <Setter Property="Foreground" Value="White" /> <Setter Property="Background" Value="Red" /> <Setter Property="Width" Value="100" /> </Style> </Window.Resources>


2016-07-07
<StackPanel> <TextBlock Text="文本1" Style="{StaticResource t1}" /> <TextBlock Text="文本2" /> </StackPanel> 此时,由于文本1引用了样式t1,因此隐式样式对它将不再起作用。另外,只有文本1才会应用t1样式,而t1对文本2不起作用。


2016-07-07
4.样式继承(声明中包含BasedOn) 如果样式声明中包含BasedOn,则该样式将继承BasedOn中定义的样式。其效果就是将该样式和BasedOn中的样式合并起来共同起作用。例如: <Style x:Key="Style1"> <Setter Property="Control.Background" Value="Yellow"/> </Style>


2016-07-07
<Style x:Key="Style2" BasedOn="{StaticResource Style1}"> <Setter Property="Control.Foreground" Value="Blue"/> </Style> 对于该样式来说,凡是引用Style2的控件将同时具有Style1和Style2中设置的样式。其效果相当于 <Style x:Key="Style2"> <Setter Property="Control.Background" Value="Yellow"/> <Setter Property="Control.Foreground" Value="Blue"/> </Style> 如果Style1和Style2中有重复的属性设置,则Style2中的属性设置将覆盖Style1中同名的属性设置(和类继承的概念一致)。


2016-07-08
在Style元素中,用Setter设置元素的属性实现样式定义,用EventSetter设置事件。 1.属性设置 在<Style>和</Style>之间,既可以用特性语法声明一个或多个Setter对象,也可以用属性语法设置属性的值。 (1)用特性语法定义Setter 用特性语法定义Setter时,每个Setter都必须包括Property属性和Value属性。例如: <Setter Property="FontSize" Value="32pt" /> 如果存在多个Setter具有相同的Property,则最后的Setter中的Property有效。同样,如果在元素中用内联方式设置的属性和Setter中设置的属性相同,则内联方式设置的


2016-07-08
属性有效。 (2)用属性语法定义Setter 一般情况下,应该尽量用特性语法定义样式。当某些属性无法用特性语法来描述时,也可以用属性语法来实现,此时在Setter元素中定义Property属性,在Setter元素的子元素中定义Value属性。例如: <Style x:Key="t1" TargetType="TextBlock"> <Setter Property="Foreground"> <Setter.Value> <LinearGradientBrush StartPoint="0.5,0" EndPoint="0.5,1"> <LinearGradientBrush.GradientStops> <GradientStop Offset="0.0" Color="#90C117" /> <GradientStop Offset="1.0" Color="#5C9417" /> </LinearGradientBrush.GradientStops> </LinearGradientBrush>


2016-07-08
</Setter.Value> </Setter> <Setter Property="RenderTransform"> <Setter.Value> <TranslateTransform X="0" Y="10"/> </Setter.Value> </Setter> </Style> 2.事件设置 在XAML资源的<Style>和</Style>之间,可以用EventSetter设置事件。例如: XAML: <Window.Resources> <Style TargetType="Button"> <Setter Property="Background" Value="AliceBlue"/> <EventSetter Event="Click"


2016-07-08
Handler="Button_Click"/> </Style> </Window.Resources> <StackPanel> <Button Content="按钮1"/> <Button Content="按钮2" Click="Button2_Click"/> </StackPanel>


2016-07-08
C#: private void Button_Click(object sender, RoutedEventArgs e) { string btnContent = (e.Source as Button).Content.ToString(); MessageBox.Show(btnContent); }


2016-07-08
private void Button2_Click(object sender, RoutedEventArgs e) { string btnContent = (e.Source as Button).Content.ToString(); MessageBox.Show(btnContent); e.Handled = true; } 在Button2_Click事件中,如果不加e.Handled = true;,则单击此按钮时,将弹出两次消息框,一次是自身引发的,另一次是样式引发的。


2016-07-08
1.内联式 内联式是指在元素的开始标记内直接用特性语法声明元素的样式。例如: <StackPanel> <TextBlock FontSize="24" FontFamily="楷体">文本1</TextBlock> <TextBlock FontSize="24" FontFamily="楷体">文本2</TextBlock> </StackPanel> 在这段XAML中,FontSize和FontFamily都是内联表示形式。 内联式适用于单独控制元素样式的情况。这种方式的优点是设置样式直观、方便;缺点是无法一次性设置所有窗口或页面中相同的样式。一般情况下,如果某个元素的样式与其他元素的样式不同,或者具有相同样式的元素比较少,可以采用内联式。


2016-07-08
.框架元素样式 框架元素是指从 FrameworkElement 或 FrameworkContentElement 继承的元素,根元素(Window、Page、UserControl等)只是一种特殊的框架元素。 框架元素样式是指在框架元素(包括根元素)的Resource属性中定义的样式,这种样式的作用范围为该元素的所有子元素。


2016-07-08
应用程序样式 应用程序样式是指在App.xaml文件的Application.Resources属性中声明的样式。这种样式的作用范围为整个应用程序项目,对项目中的所有窗口或页面都起作用。


2016-07-08
4.资源字典 资源字典是指在单独的XAML文件中用ResourceDictionary定义的样式。在元素样式、应用程序样式中都可以包含ResourceDictionary。例如: <Style> <Style.Resources> <ResourceDictionary Source="Dictionary1.xaml"/> </Style.Resources> </Style> 定义资源字典后,既可以让其只对某个元素或者某一页起作用,也可以对项目的所有元素都起作用。另外,还可以在一个ResourceDictionary中合并其他的ResourceDictionary。


2016-07-08
使用C#代码定义和引用样式 除了用XAML定义和引用样式外,还可以用C#代码来实现相同的功能。 用XAML定义的资源如果声明了键(Key),则可以在C#代码中访问这些资源。实际上,不论是哪种 XAML 资源,编译或执行应用程序的时候,这些 XAML 资源最终都会被整合到 WPF应用程序的ResourceDictionary对象中,供C#代码访问。


2016-07-08
假如页面中有一个名为border1的Border控件,并且用XAML定义了下面的样式: <Border Name="border1"> <Border.Resources> <Style x:Key="backgroundKey" TargetType="Border"> <Setter Property="Background" Value="Blue" /> </Style> </Border.Resources> </Border> 则Border.Resources也可以用下面的C#代码来实现: ResourceDictionary d1 = border1.Resources; d1.Add("backgroundKey", Brushes.Blue); 在C#代码中,可通过对象的Resources["key"]直接访问某个XAML资源。另外,WPF还提供了FindResource(key)方法和TryFindResource(key)方法来搜索XAML资源,两者的区别是如果找不到资源,FindResource(key)方法会产生异


2016-07-08
常,而TryFindResource(key)方法则返回null而不产生异常。 下面的代码演示了如何查找XAML资源,并将其作为静态资源来引用: Brush b1 = (Brush)border1.TryFindResource("backgroundKey"); if (b1 != null) border1.Background = b1; 如果将b1作为动态资源来引用,则需要调用SetResourceReference方法来实现: if (b2 != null) border1.SetResourceReference(Border.BorderBrushProperty, b2); 也可以从资源集合中获取定义的样式,然后将其分配给元素的 Style 属性。注意资源集合中的项都属于Object类型,必须先将查找到的样式强制转换为Style,然后才能将其分配给Style属性。


2016-07-08
下面的代码为名为textblock1的TextBlock设置定义的TitleText样式:


2016-07-08
textblock1.Style = (Style)(this.Resources["TitleText"]); 样式一旦应用,便会密封并且无法更改。如果要动态更改已应用的样式,必须创建一个新样式来替换现有样式。


2016-07-08
在 XAML 资源的 Style 元素中,可以利用模板自定义控件的外观。另外,触发器也是 WPF应用程序中常用的技术


2016-07-08
WPF提供了两种模板化技术,一种是样式模板化,另一种是数据模板化。 1.样式模板化 样式模板化是指利用控件模板(ControlTemplate)定义控件的外观,从而让控件呈现出各种形式。在Style中,用Template属性定义控件的模板。 下面的代码演示了如何利用样式模板化为Separator重新定义样式: <Style TargetType="Separator"> <Setter Property="Template"> <Setter.Value> <ControlTemplate> <Rectangle Width="60" Height="10" Fill="Blue" /> </ControlTemplate>


2016-07-08
</Setter.Value> </Setter> </Style> Separator默认显示一条横线,这段代码用矩形取代了默认的横线。 对于ListBox等控件,还可以分别定义HeadTemplate和ContentTemplate。


2016-07-08
触发器(Trigger)是指某种条件发生变化时自动触发某些动作。在<Style>和</Style>之间,可以利用样式设置触发器。 1.属性触发器 属性触发器是指用控件的属性作为触发条件,即当对象的属性发生变化时自动更改对应的其他属性。有两种类型的属性触发器,一种是 Trigger,用于单条件触发;另一种是 MultiTrigger,用于多条件触发。 下面的代码演示了Trigger的基本用法。 <Style TargetType="Button"> <Setter Property="Width" Value="60"/> <Style.Triggers> <Trigger Property="IsMouseOver" Value="True"> <Setter Property="Width" Value="80"/> </Trigger> </Style.Triggers> </Style>


2016-07-08
这段Style定义一个Trigger元素,该触发器的作用是:当按钮的IsMouseOver属性变为True时,将自动将按钮的Width属性设置为80。当IsMouseOver属性为False,即触发条件失效时,宽度回到默认Setter的设置值60。


2016-07-08
2.事件触发器 事件触发器(EventTrigger)是指用路由事件(RoutedEvent)作为触发条件,即当引发指定的路由事件时启动一组操作,例如播放动画等。 下面的代码演示了EventTrigger的基本用法。 <Style TargetType="Button"> <Setter Property="Height" Value="30" /> <Style.Triggers> <EventTrigger RoutedEvent="Mouse.MouseEnter"> <EventTrigger.Actions> <BeginStoryboard> <Storyboard> <DoubleAnimation Duration="0:0:0.2" Storyboard.TargetProperty="Height"


2016-07-08
To="90" /> </Storyboard> </BeginStoryboard> </EventTrigger.Actions> </EventTrigger> <EventTrigger RoutedEvent="Mouse.MouseLeave"> <EventTrigger.Actions> <BeginStoryboard> <Storyboard> <DoubleAnimation Duration="0:0:1" Storyboard.TargetProperty="Height" /> </Storyboard> </BeginStoryboard> </EventTrigger.Actions> </EventTrigger> </Style.Triggers> </Style> 3.数据触发器


2016-07-08
数据触发器分为两种DataTrigger用控件的DataContext的属性作为触发条件。MultiDataTrigger用控件的DataContext的多个属性作为触发条件。 属性触发器只检查 WPF 的附加属性,而数据触发器则可检查任何一种可绑定的属性。属性触发器一般用来检查WPF可视元素的属性,而数据触发器则通常用来检查不可视对象的属性。


2016-07-12
匿名类型中每个属性的类型是由编译器自动推断的。 在语句块内用var声明的变量称为隐式类型的局部变量。 实际上,匿名类型和隐式类型的局部变量仍然都属于强类型,只不过判断具体类型的工作由编译器自动完成而已。也正是由于这个原因,所以“字段”不能使用 var 来声明,而且用 var 声明的“局部变量”不能为null,这是因为编译器无法推断它到底是哪种类型。


2016-07-12
匿名类型和隐式类型的局部变量在LINQ表达式中特别有用,例如下面的代码输出所有成绩大于80的值: int[ ] scores = new int[ ] { 97, 92, 81, 60 }; var q = from score in scores where score > 80 select score; foreach (var i in q) {


2016-07-12
Console.WriteLine(i); }


2016-07-12
用C#创建一个对象时,可以用一条语句同时实现创建对象并同时对对象的一部分属性或者全部属性进行初始化,而无需显式调用构造函数。


2016-07-12
StudentInfo si = new StudentInfo { Name="张三", Age=20 };


2016-07-12
List list = new List { 1, int.Parse(s) };


2016-07-12
集合是指一组组合在一起的性质类似的类型化对象。将紧密相关的数据组合到一个集合中,可以更有效地对其进行管理,如用foreach来处理一个集合的所有元素等。 对于普通的集合,虽然可以使用System命名空间下的Array类和System.Collections命名空间下的类添加、移除和修改集合中的个别元素或某一范围内的元素,甚至可以将整个集合复制到另一个集合中,但是由于这种办法无法在编译代码前确定数据的类型,运行时很可能需要频繁地依靠装箱与拆箱进行数据类型转换,导致运行效率降低,而且出现运行错误时,系统提示的信息也含糊其辞,让人莫名其妙,所以实际项目中一般用泛型来实现。或者说,使用泛型能够使开发的项目性能好、出错少。 泛型集合是一种强类型的集合,它能提供比非泛型集合好得多的类型安全性和性能。编写程序时,应该尽量使用泛型集合类,而不要使用早期版本提供的非泛型集合类。


2016-07-12
ystem.Collections.Generic.List<T>泛型类表示可通过索引访问的强类型对象列表,列表中可以有重复的元素。该泛型类提供了对列表进行搜索、排序和操作的方法。例如: List<string> list1 = new List<string>(); List<int> list2 = new List<string>(10,20,30); List<T>泛型列表提供了很多方法,常用的方法如下。 • Add方法:将元素添加到列表中。 • Insert方法:在列表中插入一个新元素。 • Contains方法:测试该列表中是否存在某个元素。 • Remove方法:从列表中移除带有指定键的元素。 • Clear方法:移除列表中的所有元素。 如果是数字列表,还可以对其进行求和、求平均值以及


2016-07-12
求最大数、最小数等。例如: List<int> list = new List<int>( ); list.AddRange(new int[ ] { 12, 8, 5, 20 }); Console.WriteLine(list.Sum( ));  //结果为45 Console.WriteLine(list.Average( )); //结果为11.25 Console.WriteLine(list.Max( ));  //结果为20 Console.WriteLine(list.Min( ));  //结果为5 排序列表(SortedList<Key, KeyValue>)的用法和列表(List<T>)的用法相似,区别仅是排序列表是按键(Key)进行升序排序的结果。另外,根据键(Key)还可获取该键对应的值(KeyValue)。


2016-07-12
Dictionary<TKey, TValue>泛型类提供了一组“键/值”对,字典中的每项都由一个值及其相关联的键组成,通过键可检索值。当向字典中添加元素时,系统会根据需要自动增大容量。 一个字典中不能有重复的键。 Dictionary<TKey, TValue>提供的常用方法如下。 • Add方法:将带有指定键和值的元素添加到字典中。 • TryGetValue方法:获取与指定的键相关联的值。 • ContainsKey方法:确定字典中是否包含指定的键。


2016-07-12
Remove方法:从字典中移除带有指定键的元素。 排序字典(SortedDictionary<TKey, TValue>)的用法和字典的用法相似,区别仅是排序字典中保存的是按键(Tkey)进行升序排序后的结果。


2016-07-12
LINQ是一组查询技术的统称,其主要思想是将各种查询功能直接集成到C#语言中,不论是对象、XML还是数据库,都可以用LINQ编写查询语句。换言之,利用LINQ查询数据源就像在C#中使用类、方法、属性和事件一样,完全用 C#语法来构造,而且还具有完全的类型检查和智能提示(IntelliSense)。


2016-07-12
延迟执行和立即执行 LINQ查询表达式由一组类似于SQL的声明性语法编写的子句组成。每个子句包含一个或多个表达式,而且表达式又可以包含子表达式。 所有LINQ查询操作都由以下3部分组成。 • 获取数据源。数据源可以是数组、XML文件、SQL数据库、集合等。


2016-07-12
 创建查询。定义查询表达式,并将其保存到某个查询变量中。查询变量本身并不执行任何操作并且不返回任何数据,它只是存储在以后某个时刻执行查询时为生成结果而必需的信息。 • 执行查询。 LINQ查询表达式必须以from子句开头,并且必须以select或group子句结尾。在第一个from子句和最后一个select或group子句之间,查询表达式可以包含一个或多个where、orderby、join、let甚至附加的from子句。还可以使用into关键字将join或group子句的结果作为附加查询子句的源数据。 执行查询时,一般利用 foreach 循环执行查询得到一个序列,这种方式称为“延迟执行”。例如: int[] numbers = { 0, 1, 2, 3, 4, 5, 6 }; var q = from n in numbers where n % 2 == 0 select n;


2016-07-12
foreach (var v in q) { Console.WriteLine("{0}", v); } 对于聚合函数,如 Count、Max、Average、First,由于返回的只是一个值,所以这类查询在内部使用foreach循环实现,而开发人员只需要调用LINQ提供的对应方法即可,这种方式称为“立即执行”。例如: int[] numbers = { 0, 1, 2, 3, 4, 5, 6 }; var q = from n in numbers where n % 2 == 0 select n; Console.WriteLine("{0}", q.Count()); 还有一种特殊情况,就是直接调用 Distinct 方法得到不包含重复值的无序序列,这种方式也是立即执行的。例如:


2016-07-12
int[] numbers = { 10, 11, 10, 11, 14, 14, 16 }; var q = (from n in numbers where n % 2 == 0 select n).Distinct(); foreach (var v in q) { Console.WriteLine(v); } 也可以在创建查询时,调用 ToList<TSource>或 ToArray<TSource>方法强制立即执行查询。例如: int[] numbers = { 0, 1, 2, 3, 4, 5, 6 }; var list = (from n in numbers where n % 2 == 0 select n).ToList();


2016-07-12
from子句用于指定数据源和范围变量,由于后面的子句都是利用范围变量来操作的,所以查询表达式必须以from子句开头。


2016-07-12
where 子句用于指定筛选条件,即只返回筛选表达式结果为 true的元素。筛选表达式也可用C#语法来构造。


2016-07-12
orderby 子句用于对返回的结果进行排序,ascending 关键字表示升序,descending 关键字表示降序。


2016-07-12
使用LINQ时,有一点需要注意,如果查询的是数据库,而且数据库中的表字段是char类型或者 nchar 类型且长度为 1,则常量必须用单引号引起来,如果数据库中的表字段是 char 类型或者nchar类型且长度大于1,或者表字段是varchar或者nvarchar类型,则常量用双引号引起来。


2016-07-12
group子句用于按指定的键分组,group后面可以用by指定分组的键。


2016-07-12
select 子句用于生成查询结果并指定每个返回的元素的“形状”或“类型”。除了前面所举的select子句的用法外,还可以用select子句让范围变量只包含成员的子集,如查询结果只包含数据表中的一部分字段等。当 select 子句生成除源元素副本以外的内容时,该操作称为“投影”。使用投影转换数据是LINQ查询表达式的又一种强大功能。


2016-07-12
利用 LINQ 的 from 子句也可以直接查询多个对象,唯一的要求是每个对象中的成员需要至少有一个与其他成员关联的元素。


2016-07-12
在.NET框架中,与操作系统环境相关的类主要有两个:一个是Environment类,该类除了提供当前环境和操作系统平台相关的信息外,还提供了获取本地逻辑驱动器和特殊文件夹的方法;另一个是DriveInfo类,提供了本地驱动器相


2016-07-12
关的详细信息。


2016-07-12
1.Environment类 使用 Environment 类可检索与操作系统相关的信息,如命令行参数、退出代码、环境变量设置、调用堆栈的内容、自上次系统启动以来的时间,以及公共语言运行库的版本等。


2016-07-12
StringBuilder sb = new StringBuilder(); String[] drives = Environment.GetLogicalDrives(); sb.AppendLine("本机逻辑驱动器:" + String.Join(", ", drives)); sb.AppendLine("操作系统版本:" + Environment.OSVersion.VersionString); sb.AppendLine("是否为64位系统:" + Environment.Is64BitOperatingSystem); sb.AppendLine("计算机名:" + Environment.MachineName); sb.AppendLine("处理器个数:" + Environment.ProcessorCount); sb.AppendLine("系统启动后经过的毫秒数:" + Environment.TickCount); sb.AppendLine("登录用户名:" + Environment.UserName);


2016-07-12
DriveInfo类提供了比Environment类的GetLogicalDrives方法更为详细的信息。 使用DriveInfo可以确定当前可用的驱动器以及这些驱动器的类型,还可以通过查询来确定驱动器的容量和剩余空间。


2016-07-12
下面的代码演示了DriveInfo类的基本用法。 DriveInfo[] allDrives = DriveInfo.GetDrives( ); foreach (DriveInfo d in allDrives)


2016-07-12
{ Console.WriteLine("Drive {0}", d.Name); Console.WriteLine("文件类型: {0}", d.DriveType); if (d.IsReady == true) { Console.WriteLine("卷标: {0}", d.VolumeLabel); Console.WriteLine("文件系统: {0}", d.DriveFormat); Console.WriteLine("当前用户可用空间:{0} bytes", d.AvailableFreeSpace); Console.WriteLine("总可用空间:{0} bytes", d.TotalFreeSpace); Console.WriteLine("驱动器总容量:{0} bytes ", d.TotalSize); } }


2016-07-12
Directory类提供了一些静态方法,利用它可对磁盘和目录进行管理,如复制、移动、重命名、创建、删除目录等


2016-07-12
使用CreateDirectory方法创建多级子目录时,也可以直接指定路径,例如,同时创建test目录和其下的t1一级子目录和t2二级子目录的代码为 Directory.CreateDirectory(@"c:\test\t1\t2"); 或者写为 Directory.CreateDirectory("c:\\test\\t1\\t2");


2016-07-12
Directory类的Delete方法用于删除指定的目录,常用的方法原型为: public static void Delete(string path, bool recursive) 其中,参数path为要移除的目录的名称。path参数不


2016-07-12
区分大小写,可以是相对于当前工作目录的相对路径,也可以是绝对路径。recursive是一个布尔值,如果要移除path中的目录(包括所有子目录和文件),则为true;否则为false。 例如,删除C盘根目录下的test目录,以及test目录中的所有子目录和文件,可以用下面的代码实现: Directory.Delete(@"c:\test", true);


2016-07-12
在System.IO命名空间下,.NET框架提供有一个File类,利用它可对文件进行各种操作,如判断文件是否存在、创建、复制、移动、删除、读写文件等。


2016-07-12
判断某个路径是目录还是文件 下面的代码演示了如何判断某个路径是目录还是文件: if ((File.GetAttributes(path) & FileAttributes.Directory) == FileAttributes.Directory) { Console.WriteLine("{0}是目录", path); } else


2016-07-12
{ Console.WriteLine("{0}是文件", path); }


2016-07-12
对于开发人员来说,除了对文件进行管理外,还可以利用File类对文件内容进行各种操作,如创建文件、打开文件、保存文件、修改文件、追加文件内容等。


2016-07-12
常见的文件编码方式有ASCII编码、Unicode编码、UTF8编码和ANSI编码。 在C#中,保存在文件中的字符默认都是Unicode编


2016-07-12
码,即一个英文字符占两个字节,一个汉字也是两个字节。这种编码虽然能够表示大多数国家的文字,但由于它比ASCII占用大一倍的空间,而对能用ASCII字符集来表示的字符来说就显得有些浪费。为了解决这个问题,又出现了一些中间格式的字符集,即 UTF(Universal Transformation Format,通用转换格式)。目前流行的UTF字符编码格式有UTF-8、UTF-16以及UTF-32。 UTF-8是Unicode的一种变长字符编码,一般用1~4个字节编码一个Unicode字符,即将一个Unicode字符编为1到4个字节组成的UTF-8格式。UTF-8是字节顺序无关的,它的字节顺序在所有系统中都是一样的,因此,这种编码可以使排序变得很容易。 UTF-16将每个码位表示为一个由1~2个16位整数组成的序列。 UTF-32将每个码位表示为一个32位整数。 我国的国家标准编码常用有GB2312编码和GB18030编码,其中GB2312提供了65 535个汉字,GB18030提供了27 484个汉字。


2016-07-12
在GB2312编码中,汉字都是采用双字节编码。GB18030则是对GB2312的扩展,每个汉字的编码长度由2个字节变为1~4个字节。 由于世界上不同的国家或地区可能有自己的字符编码标准,而且由于字符个数不同,这些编码标准无法相互转换。为了让操作系统根据不同的国家或地区自动选择对应的编码标准,操作系统将使用2个字节来代表一个字符的各种编码方式统称为ANSI编码。例如,在简体中文操作系统下,ANSI编码代表GB2312编码。 在System.Text命名空间中,有一个Encoding类,用于表示字符编码。对文件进行操作时,常用的编码方式有如下几种。 • Encoding.Default:表示操作系统的当前ANSI编码。 • Encoding.Unicode:Unicode编码。 • Encoding.UTF8:UTF8编码。


2016-07-12
.ReadAllText方法和AppendAllText方法 用 ReadAllText 方法可打开一个文件,读取文件的每一行,并将每一行添加为字符串的一个元素,然后关闭文件。对于Windows系列操作系统来说,一行就是后面跟有下列符号的字符序列:回车符(“\r”)、换行符(“\n”)或回车符后紧跟一个换行符,所产生的字符串不包含文件终止符。即使引发异常,该方法也能保证关闭文件句柄。常用原型为: public static string ReadAllText(string path, Encoding encoding) 读取文件时,ReadAllText方法能够根据现存的字节顺序标记来自动检测文件的编码。可检测到的编码格式有 UTF-8 和 UTF-32。但对于汉字编码(GB2312 或者 GB18030)来说,如果第 2个参数不是用Encoding.Default,该方法可能无法自动检测出来是哪种编码,因此,对文本文件进行处理时,一般在代码中指定所用的编码。


2016-07-12
AppendAllText方法用于将指定的字符串追加到文件中,如果文件不存在则自动创建该文件,常用原型为: public static void AppendAllText(string path, string contents, Encoding encoding) 此方法可打开指定的文件,使用指定的编码将字符串追加到文件末尾,然后关闭文件。即使引发异常,该方法也能保证关闭文件句柄。 下面的代码演示了ReadAllText方法和AppendAllText方法的用法。 string path = @"c:\temp\MyTest.txt"; if (File.Exists(path)) { File.Delete(path); }


2016-07-12
string appendText = "你好。" + Environment.NewLine; File.AppendAllText(path, appendText,Encoding.De


2016-07-12
fault); string readText = File.ReadAllText(path,Encoding.Default); Console.WriteLine(readText);


2016-07-12
ReadAllLines 方法打开一个文本文件,将文件的所有行都读入一个字符串数组,然后关闭该文件。与ReadAllText方法相似,该方法可自动检测UTF-8和UTF-32编码的文件,如果是这两种格式的文件,则不需要指定编码。 WriteAllLines方法创建一个新文件,在其中写入指定的字符串数组,然后关闭文件。如果目标文件已存在,则覆盖该文件。


2016-07-12
流是字节序列的抽象概念,如文件以及输入输出设备等均可以看成流。简而言之,流是一种向后备存储器写入字节和从后备存储器读取字节的方式。 流也是进行数据读取操作的基本对象,流提供了连续的字节流存储空间。虽然数据实际存储的位置可以不连续,甚至可以分布在多个磁盘上,但我们看到的是封装以后的数据结构,是连续的字节流抽象结构,这和一个文件也可以分布在磁盘上的多个扇区道理一样。


2016-07-12
除了和磁盘文件直接相关的文件流以外,还有多种其他类型的流,如分布在网络中、内存中和磁带中的流,分别称为网络流、内存流和磁带流。所有表示流的类都是从抽象基类 Stream 继承的。 流有如下几种操作。 • 读取:从流中读取数据到变量中。 • 写入:把变量中的数据写入流中。 • 定位:重新设置流的当前位置,以便随机读写。 StreamReader类提供了利用流来按行读取文本文件信息的方法。 如果不指定编码,StreamReader的默认编码为UTF-8,而不是当前系统的ANSI编码。由于UTF-8可以正确处理Unicode字符并在操作系统的本地化版本上提供一致的结果,因此如果文本文件是通过应用程序创建的,直接用默认的UTF-8编码即可。


2016-07-12
try { using (StreamReader sr = new StreamReader("TestFile.txt")) { String line; while ((line = sr.ReadLine( )) != null) { Console.WriteLine(line); } } } catch (Exception e) { Console.WriteLine("The file could not be read:"); Console.WriteLine(e.Message); }


2016-07-12
StreamWriter类提供了按行写入文本信息的方法。与StreamReader类似,如果不指定编码, StreamWriter默认使用UTF-8编码,而不是当前系统的ANSI编码。 下面的代码演示了如何使用StreamWriter类向文本文件写入文本信息。 try { using (StreamWriter sw = new StreamWriter("TestFile.txt")) { sw.WriteLine("First line"); sw.WriteLine("The date is: {0}", DateTime.Now); } } catch (Exception e) {


2016-07-12
Console.WriteLine("The file could not be write:"); Console.WriteLine(e.Message); } 除了对文本文件进行操作外,还可以利用流对其他类型的文件进行操作。此时程序员可以采用File类的Open方法先创建一个FileStream对象,然后使用FileStream对象对文件进行读取、写入、打开和关闭操作。由于FileStream能够对输入输出进行缓冲,因此可以提高系统的性能。


2016-07-12
WPF是.NET框架的一个子集,WPF应用程序的关键设计思想是将界面标记描述(XAML)与实现代码(C#)有效分离,从而让美工(界面设计)和开发人员(代码实现)可真正同步进行。另外,WPF底层是通过GPU硬件加速来实现而不是靠纯软件的GDI+来实现,这是WPF和WinForm的本质区别。 开发在Windows 7操作系统上运行的C/S客户端应用程序时,使用WPF应用程序能发挥最大的运行性能,这种优势是WinForm应用程序所无法比拟的。 将WPF应用程序通过ClickOnce技术部署在某个Web服务器上以后,客户端即可下载、安装并独立运行它。另外,在 Web 服务器上再次部署新的升级版本时,客户端还可以自动感知和升级。或者说, WPF 应用程序的服务器部署以及客户端下载和安装升级过程与我们常见的 QQ、飞信、360 安全卫士等目前流行的各种软件的下载、安装和升级过程非常类似。


2016-07-12
在VS2012中,WPF应用程序通过从Application类继


2016-07-12
承的App类的XAML文件(App.xaml)和代码隐藏文件(App.xaml.cs)协同配合共同公开Application类中应用程序的定义。开发人员通过App类,可以定义在整个应用程序范围内都可以使用的资源和公共属性,定义以后,就可以在其他类中直接引用这些资源和属性。另外,还可以通过该类提供的方法随时关闭应用程序。 不管当前应用程序打开了多少个窗口,也不管当前窗口是否为主窗口,一旦在 WPF 应用程序中调用了 Application 或者 App 类的 Shutdown 方法,都会立即关闭应用程序。 Shutdown 方法的典型用法为 App.Current.Shutdown(); 或者 Application.Current.Shutdown(); 如果所有窗口都已关闭或者主窗口已关闭,都会自动关闭应用程序。


2016-07-12
在“解决方案资源管理器”中,双击App.xaml打开该文件,观察从哪个地方指定WPF应用程序默认启动的窗口。相关代码如下: <Application x:Class="ch07.App" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"


2016-07-12
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" StartupUri="MainWindow.xaml"> 在这段代码中,可看到StartupUri的值为MainWindow.xaml,这就是WPF应用程序默认启动的窗口。如果希望程序运行时启动另一个窗口,修改StartupUri的值即可。


2016-07-12
)在 XAML 编辑器中,鼠标右键单击“Click=”代码,选择【定位到事件处理程序】,它就会自动在MainWindow.xaml.cs中添加对应的代码。


2016-07-12
XAML命名空间和x:前缀编程构造 XAML(可扩展应用程序标记语言)是一种基于XML(可扩展标记语言)且遵循XML结构规则的声明性标记语言。XAML文件是具有.xaml扩展名的XML文件,编码默认使用UTF-8。 在WPF应用程序中,Window元素、Page元素和Application元素都必须包含x:Class声明,否则无法在代码隐藏文件中用C#控制XAML文件中的对象元素。 1.x:前缀编程构造 根元素的xmlns:x用于XAML命名空间映射,目的是为


2016-07-12
了通过x:前缀编程构造来声明可被其他XAML和C#代码引用的对象。 在WPF应用程序中,可以用Name特性指定对象的名称属性,也可以在【属性】窗口中设置该属性。例如: <Button Name="okButton" Content="确定" /> <Button Name="cancelButton" Content="取消" /> 但是,XAML中的Name特性就像局部变量一样受其所在范围的限制,如果在所选元素的范围内无法访问元素的Name特性(如样式模板化、动画等),则可以用x:Name特性来描述。 x:Name通过特定子系统或FindName等方法,为运行时使用的XAML元素(即用C#代码引用XAML元素)提供标识。例如: <Button x:Name="okButton" Content="确定" /> <Button x:Name="cancelButton" Content="取消" /> 指定了x:Name以后,就可以在C#代码中通过它来引用XAML元素了。


2016-07-17
WPF 提供了三种数据绑定技术:Binding、MultiBinding 和 PriorityBinding。这三种 Binding的基类都是BindingBase,而BindingBase又继承于MarkupExtension。 在WPF中,ContentControl(如Button)和ItemsControl(如ListBox和ListView)都提供了内置的功能,使单个


2016-07-17
数据项或数据项集合可以进行灵活的数据绑定,并可以生成排序、筛选和分组后的视图。


2016-07-17
在System.Windows.Data命名空间下,WPF提供了一个Binding类,利用该类可将目标的附加属性与数据源的值绑定在一起。数据源可以是任何修饰符为public的属性,包括控件属性、数据库、XML或者CLR对象的属性等。 Binding类继承的层次结构为: System.Object System.Windows.Markup.MarkupExtension System.Windows.Data.BindingBase System.Windows.Data.Binding 可以看出,Binding类继承自BindingBase,而BindingBase又继承自MarkupExtension,所以可直接用绑定标记扩展来实现数据绑定。 绑定标记扩展的特性语法格式为:


2016-07-17
<object property="{Binding declaration}" .../> 格式中的object为绑定目标,一般为WPF元素;property为目标属性;declaration为绑定声明。绑定声明可以有零个或多个,如果有声明,每个声明一般都以“绑定属性=值”的形式来表示,绑定属性是指 Binding 类提供的各种属性,值是指数据源。如果有多个声明,各声明之间用逗号分隔。例如: <Slider Name="slide1" Maximum="100" /> <TextBlock Text="{Binding ElementName=slide1,Path=Value}" /> 这里的ElementName=slide1表示绑定的元素名为slide1,Path=Value表示绑定到slide1控件的Value属性值。这样一来,当拖动Slide控件的滑动条时,TextBlock的Text属性值也会自动更改。


2016-07-17
由于绑定标记扩展的特性语法用法比较简洁,所以一般用这种形式实现数据绑定即可。但是在有些情况下,可能无法用特性语法来表示,此时可以用属性语法来描述,例如: <TextBlock>


2016-07-17
<TextBlock.Text> <Binding ElementName="slide1" Path="Value" /> </TextBlock.Text> </TextBlock> 实际上,用 Binding 类实现数据绑定时,不论采用哪种形式,其本质都是在绑定声明(declaration)中利用Binding类提供的各种属性来描述绑定信息。


2016-07-17
1.绑定和绑定表达式(Binding、BindingExpression)


2016-07-17
BindingExpression 是维持绑定源与绑定目标之间连接的基础对象。由于一个 Binding 实例中可包含多个BindingExpression实例,所以当创建一个名为binding1的Binding对象后,就可以通过该对象绑定多个属性,让每个绑定属性对应binding1中的一个BindingExpression实例,从而实现多个属性共享同一个Binding对象的目的。 假如有下面的XAML: <Slider Name="slide1" Width="100" Maximum="100" /> <Rectangle Name="r1" Height="15" Fill="Red" /> <TextBlock Name="t1" /> 则下面的C#代码就可以让r1和t1共享同一个名为b1的Binding实例: Binding b1 = new Binding() { ElementName = slide1.Name,


2016-07-17
Path = new PropertyPath(Slider.ValueProperty), StringFormat= "[{0:##0}%]" }; BindingOperations.SetBinding(r1, Rectangle.WidthProperty, b1); BindingOperations.SetBinding(t1, TextBlock.TextProperty, b1); 在这段代码中,有两个BindingExpression实例共享b1对象。 另外,如果要绑定的对象是FrameworkElement或FrameworkContentElement,还可以通过对象名直接调用该对象的SetBinding方法实现绑定,例如: r1.SetBinding(Rectangle.WidthProperty, b1); 其效果和用BindingOperations提供的静态方法相同。 如果要获取某绑定控件的Binding对象或者BindingExpression对象,可用类似下面的C#代码来实现:


2016-07-17
Binding b = BindingOperations.GetBinding(t1, TextBlock.TextProperty); BindingExpression be = BindingOperations.GetBindingExpression( t1, TextBlock.TextProperty); 通过绑定控件得到与其对应的Binding对象或者BindingExpression对象以后,就可以对其进行进一步操作,如控制更新时间、验证输入结果等。


2016-07-17
进行数据绑定时,目标属性必须是依


2016-07-17
赖项属性,而对源属性则没有此要求。另外,绑定源并不仅限于CLR对象的属性,还可以是XML、ADO.NET等。 WPF提供了以下数据绑定方式(XAML中用Mode属性描述,C#中用BingdingMode枚举描述)。 • OneWay:单向绑定。当源发生变化时目标也自动变化。这种模式适用于绑定的控件为隐式只读控件的情况(如学号),或者目标属性没有用于进行更改的控件接口的情况(如表的背景色)。如果不需要监视目标属性的更改,使用OneWay绑定模式可避免TwoWay绑定模式的系统开销。 • TwoWay:双向绑定。当源或目标有一方发生变化时,另一方也自动变化。这种绑定模式适用于可编辑或交互式的UI方案。 • OneTime:单次绑定。当应用程序启动或数据上下文(DataContext)发生更改时才更新目标,此后源的变化不再影响目标。这种绑定模式适用于绑定静态的数据,它实质上是OneWay绑定的简化形式,在源值不更改的情况下可以提供更好的性能。 • OneWayToSource:反向绑定。当目标发生变化时源


2016-07-17
也跟着变化,这种方式与 OneWay 绑定刚好相反。 • Default:如果不声明绑定模式,默认为 Default,该方式自动获取目标属性的默认 Mode值。一般情况下,可编辑控件属性(如文本框和复选框的属性)默认为双向绑定,而多数其他属性默认为单向绑定。确定依赖项属性绑定在默认情况下是单向还是双向的C#编程方法是:使用依赖项属性的GetMetadata方法获取属性的属性元数据,然后检查其BindsTwoWayByDefault属性的布尔值。 例如: <TextBlock Text="{Binding ElementName=listBox1, Path=SelectedItem.Content, Mode=OneWay}"/> 要使用TwoWay方式,可用TextBox来实现: <TextBox Text="{Binding ElementName=listBox1, Path=SelectedItem.Content, Mode=TwoWay}" />


2016-07-17
此时如果修改TextBox的Text属性,将其改为另一种颜色,由于使用的绑定模式是双向绑定,因此listBox1中的选项也会自动改变。 3.控制更新源的时间(UpdateSourceTrigger) TwoWay 和 OneWayToSource 默认都会自动侦听目标属性的更改,并将这些更改传播回源。对于这两种模式,如果希望改变更新源的时间,可以通过设置UpdateSourceTrigger属性来实现,其取值有三个。 • Explicit:用C#代码调用BindingExpression的UpdateSource方法时才更新源。 • LostFocus:当目标控件失去焦点时自动更新源。 • PropertyChanged:目标控件的绑定属性每次发生更改时都会自动更新源。 不同的依赖项属性具有不同的默认 UpdateSourceTrigger 值。大多数依赖项属性的默认UpdateSourceTrigger值都为PropertyChanged,也就是说,只要目标属性更改,就会自动更新源。


2016-07-17
但是,对于TextBox等文本编辑控件来说,当使用TwoWay或OneWayToSource绑定模式时,其UpdateSourceTrigger的默认值是LostFocus而不是PropertyChanged。之所以这样设置,是因为每次键击之后都进行更新会降低性能,这种默认设置在大多数情况下是合适的,但在某些特殊情况下可能需要修改该属性的值才能满足需求,如在即时通信软件中,要让一方知道另一方正在键入或编辑信息,此时可将另一方的UpdateSourceTrigger属性设置为PropertyChanged。另外,要在TextBox失去焦点时再验证输入的数据是否正确,可将UpdateSourceTrigger属性设置为LostFocus;要实现单击某个提交按钮时才通过 C#代码调用 UpdateSource 方法更新源,可将UpdateSourceTrigger属性设置为Explicit。 4.绑定路径语法(Path属性) 使用Path属性可以指定数据源的属性路径。Path取值有以下可能性。 (1)Path 的值为源对象的属性名,如 Path="Text"。另外,在 C#中还可以通过类似语法指定属性的子属性。


2016-07-17
(2)当绑定到附加属性时,需要用圆括号将其括起来,例如Path=(DockPanel.Dock)。 (3)用方括号指定属性索引器,还可以使用嵌套的索引器。例如,Path=list[0]。另外,还可以混合使用索引器和子属性,如Path=list.Info[id, Age]。 (4)在索引器内部,可以使用多个由逗号分隔的索引器参数,还可以使用圆括号指定每个参数的类型。例如,Path="[(sys:Int32)42,(sys:Int32)24]",其中sys映射到System命名空间。 (5)如果源为集合视图,则可以用斜杠(/)指定当前项。例如,Path=/表示绑定到视图中的当前项。另外,还可以结合使用属性名和斜杠,如Path=/Offices/ManagerName表示绑定到源集合中的当前项。 (6)可以使用点(.)路径绑定到当前源。例如Text="{Binding}"等效于Text="{Binding Path=.}"。 5.数据转换 用XAML来描述数据绑定时,WPF提供的类型转换器


2016-07-17
能将一些类型的值转换为字符串表示形式。但在有些情况下,可能还需要开发人员自定义转换器,如当绑定的源对象是类型为DateTime的属性时,在这种情况下,为了使绑定正常工作,需要先将该属性值转换为自定义的字符串表示形式。 要将转换器与绑定关联,一般先创建一个实现IValueConverter接口的类,然后实现两个方法:Convert方法和ConvertBack方法。 实现IValueConverter接口时,由于转换器是分区域性的,所以Convert和ConvertBack方法都有指示区域性信息的culture参数。如果区域性信息与转换无关,在自定义转换器中可以忽略该参数。另外,最好用ValueConversionAttribute特性向开发工具指示转换所涉及的数据类型。


2016-07-17
简单数据绑定 使用 Binding 对象建立绑定时,每个绑定实际上都由四


2016-07-17
部分组成:绑定目标、目标属性、绑定源、要使用的源值的路径。 如果不指定绑定源,绑定将不会起任何作用。 1.在单个属性中直接指定绑定源 对于数据源是单个数据的情况,有三种直接指定绑定源的方式。 • ElementName:源是另一个WPF元素。 • Souce:源是一个CLR对象。 • RelativeSource:源和目标是同一个元素。 这三种方式是相互排斥的,即每次只能使用其中的一种方式,否则将会引发异常。 (1)用ElementName绑定到其他控件 Binding类的ElementName属性用于指明数据源来自哪个元素。当将某个WPF元素绑定到其他WPF元素时,该属性很有用。


2016-07-17
数据模板化 数据模板化是指利用数据模板(DataTemplate)将控件和多项数据自动绑定在一起。 由于绑定到集合需要用数据模板来实现,所以这一节我们先了解如何使用 DataTemplate,然后再学习其他相关的技术。 1.利用ObservableCollection<T>类实现集合 在动态数据绑定中,为了让集合中的插入、删除等操作可以自动更新UI,要求集合必须实现INotifyCollectionChanged接口,此接口公开一个事件,只要基础集合发生更改,都会引发该事件。 WPF在System.Collections.ObjectModel命名空间下提


2016-07-17
供了一个ObservableCollection<T>类,该类是公开INotifyCollectionChanged接口的数据集合的内置实现。将目标控件绑定到集合时,应该优先考虑用ObservableCollection<T>来实现,而不是用List<T>来实现,这是因为List<T>对象不会自动引发集合更改事件。另外,ObservableCollection<T>添加或移除项时不需要重新生成整个列表,所以运行效率高、速度很快。 使用ObservableCollection<T>集合时需要注意一点,由于INotifyCollectionChanged接口只是对集合中的项进行绑定,但并不深入到项的内部,所以如果集合中每一项的对象包含多个属性,而且希望这些属性值变化时也都能自动从源传送到目标,则所有这些属性都必须实现INotifyPropertyChanged接口。如果只需要项在更改时才更新绑定结果,则不要求每一项的属性都实现INotifyPropertyChanged接口。


2016-07-17
数据验证的基本概念 在 WPF 应用程序中实现数据验证功能时,最常用的办法是将数据绑定与验证规则关联在一起。WPF提供了两种内置的验证规则,除此之外还可以自定义验证规则。 1.ValidationRule类 不论是内置的验证规则还是自定义验证规则,由于所有这些规则都继承自ValidationRule类,所以要理解其内部是如何验证的,我们必须先了解ValidationRule类提供的属性和方法。 (1)ValidatesOnTargetUpdated属性 该属性获取或设置更新绑定目标时是否执行验证规则,如果是则为true,否则为false。例如验证字符串时,如果将该属性设置为true,则会在启动后立即执行验证规则。 (2)ValidationStep属性


2016-07-17
该属性获取或设置什么时候执行验证规则。取值有 RawProposedValue(在任何转换前运行ValidationRule)、ConvertedProposedValue(在转换值后运行ValidationRule)、UpdatedValue(在源更新后运行ValidationRule)、CommittedValue(在将值提交给数据源后运行ValidationRule)。默认值是RawProposedValue。 由于其他验证规则都继承自ValidationRule类,所以所有验证规则中都可以使用这两个属性。 (3)Validate方法 ValidationRule类提供的验证方法中有一个Validate方法,其重载形式如下。 public abstract ValidationResult Validate(Object value, CultureInfo cultureInfo) public virtual ValidationResult Validate( Object value, CultureInfo cultureInfo, BindingExpressionBase owner) public virtual ValidationResult Validate(


2016-07-17
Object value, CultureInfo cultureInfo, BindingGroup owner) 参数中的value表示要检查的绑定目标的值,cultureInfo表示区域性信息。 可以看出,第1个Validate方法是一个抽象方法,即要求扩充类必须实现这个方法。绑定引擎对目标进行验证时,会自动调用验证规则中指定的所有Validate方法来检查源值的合法性。 2.内置的ExceptionValidationRule验证规则 WPF提供的第一种内置的验证规则是用继承自ValidationRule的ExceptionValidationRule类来实现的。该规则检查在“绑定源属性”的更新过程中引发的异常。更新源时,如果有异常(如类型不匹配)或不满足条件,它会自动将异常添加到错误集合中,在 UI 中只需要利用模板绑定该错误集合即可显示验证错误信息。 3.内置的DataErrorValidationRule验证规则 WPF提供的第二种内置的验证规则是用继承自Valida


2016-07-17
tionRule的DataErrorValidationRule类来实现的。该规则检查由实现IDataErrorInfo接口的对象所引发的错误(包括默认的转换器产生的异常),开发人员可在实现的接口中通过目标属性字符串直接自定义被绑定对象在验证过程中出现的错误信息。 4.自定义验证规则类 除了可直接使用内置的验证规则外,还可以自定义从ValidationRule类派生的类,通过在派生类中实现Validate方法来创建自定义的验证规则。 5.Binding类提供的与数据验证有关的常用属性 Binding不但提供了数据绑定功能,还提供了与数据验证有关的属性。由于数据验证是将验证规则和绑定模型关联在一起来实现的,所以我们需要了解相关的常用属性。 • ValidatesOnExceptions 属性:获取或设置是否包含 ExceptionValidationRule。若包含则为true,否则为false。 • ValidatesOnDataErrors 属性:获取或设置是否包含 DataErrorValidationRule,若包含则为true,否则为false。


2016-07-17
• UpdateSourceTrigger属性:获取或设置绑定源更新的执行时间。 • ValidationRules 属性:获取用于检查用户输入有效性的规则集合。此属性只能在 XAML 中通过集合语法进行设置,或者通过访问集合对象并使用它的各种方法(如Add方法)来进行设置。


2016-07-17
PF应用程序中利用实体数据模型访问数据库的基本用法。  12.1 创建数据库和表 为了使本书的例子复制到其他计算机上后能够继续调试运行而不修改任何代码,本章的所有例子均采用SQL Server 2012 Express LocalDB数据库(简称LocalDB数据库)作为示例。这一节我们学习如何创建LocalDB数据库,并在数据库中添加、编辑表结构和数据。  12.1.1 ADO.NET数据访问技术 ADO.NET 是微软提出的数据访问模型,该架构通过特定


2016-07-17
的应用程序编程接口(API),对数据库进行创建、检索、更新、删除等操作。 ADO.NET 的最大特点就是在提供保持连接访问方式的基础上,还支持对数据的断开连接方式的访问,由于断开连接方式减少了与数据库的活动连接数目,所以既能减轻数据库服务器的负担,同时又提高了数据处理的效率。


2016-07-17
在ADO.NET中,可以多种方式访问数据库,下面我们先简单了解常用的几种方式,以便对将要选择的技术有一个整体认识。 1.利用DataSet访问数据库 访问SQL Server数据库的第1种方式是用DataSet来实现,这是ADO.NET刚推出时提供的技术,用于在断开连接方式下对数据进行处理,在VS2005、VS2008、VS2010和VS2012中都可以使用,目前仍有很多开发人员仍在使用它。 这种方式将驻留在本机内存中的DataSet作为中间层,即应用程序和DataSet进行交互,DataSet再和数据库进行交互。


2016-07-17
2.利用LINQ to SQL访问数据库 访问数据库的第2种方式是用LINQ to SQL来实现,在VS2008、VS2010和VS2012中都可以使用。 这种方式直接和SQL Server数据库进行交互,执行效率高,速度快,但该方式只能访问SQL Server,不支持其他类型的数据库。 在LINQ to SQL中,先利用O/R设计器构建模型,再利用该模型传递SQL语句,执行SQL命令,也可以用LINQ语法直接访问SQL Server。使用这种技术时,一般用它设计自定义的中间层对象模型(中间件),然后将其做成.dll文件供其他应用程序调用。 3.利用实体框架和LINQ to Entities访问数据库 访问数据库的第3种方式是用实体框架和LINQ to Entities来实现,这是微软建议的数据库访问方式,在VS2008、VS2010和VS2012中均可使用。在.NET数据访问技术中将这种架构的封装称为EDM(Entity Data Model,ADO.NET实体数据模型)。


2016-07-17
EDM的设计思路是让应用程序和实体数据模型交互,实体数据模型再和数据库交互。 该技术和前两种方式不同,用这种技术实现的模型是一种开放式的架构,所有数据库供应商都可以实现这种模型。换言之,在 VS2012 中利用该模型可支持多种类型的数据库(包括 SQL Server、Oracle、DB2、MySQL等),而且可由数据库供应商直接提供该模型的数据库访问引擎,例如SQL Server 针对VS2012 的数据库访问引擎由开发商微软公司提供,Oracle针对VS2012 的数据库访问引擎由开发商Oracle公司提供,DB2针对VS2012的数据库访问引擎由开发商IBM公司提供。这样做的好处是只要数据库版本升级,数据库供应商就能及时提供与数据库版本对应的数据库访问引擎,既能保证时效性,同时也避免了版权纠纷等其他问题。 微软最初将这种模型内置在.NET框架中,但由于数据库版本的升级,原来内置的模型很容易引起对新版本数据库的不支持,所以从VS2012开始,微软也不再将该模型内置在.NET框架内,而是和其他数据库供应商一样,改为以数据库为主体,单独提供与数据库版本对应的实体框架模型数据库引擎。


2016-07-17
在VS2012中,微软的办法是通过内置的NuGet提供实体框架模型的版本自动升级(也可以单独从NuGet网站下载、安装),目前的最新版本是5.0版,支持SQL Server 2012数据库,也支持SQL Server 2008和SQL Server 2005等早期版本的数据库,但不再支持已淘汰的SQL Server 2000。


2016-07-17
SQL Server 2012 Express LocalDB:这是Express的一种轻型免费版本,该版本具备所有可编程性功能,但在用户模式下运行,并且具有快速的零配置安装和必备组件要求较少的特点。安装VS2012时已经自动包含了这个版本,所以在VS2012开发环境下可以直接创建SQL Server 2012 Express LocalDB数据库,并在库中添加或修改表、存储过程、视图以及函数。


2016-07-17
LocalDB是基于服务的数据库。安装后其默认的实例名为“(LocalDB)\v11.0”,一般用默认实例即可,但也可以用命名实例。 通过应用程序访问数据库时,VS2012会自动将该.mdf文件附加到LocalDB的默认实例中,当不再使用数据库时,LocalDB便将.mdf文件从默认实例中自动分离出来。


2016-07-17
实体框架提供了数据库优先、模型优先和代码优先三种开发模式。这三种开发模式各有特点,开发团队可根据实际项目需要,选择其中的一种模式。 1.数据库优先(Database First)


2016-07-17
数据库优先(Database First)是指先创建数据库,然后再根据数据库生成对应的实体数据模型(.edmx文件)。


2016-07-17
2.模型优先(Model First) 模型优先(Model First)是指先利用开发工具提供的模板创建实体数据模型(.edmx文件),然后再根据实体数据模型生成数据库。 这种模式是先用实体框架设计器创建模型,然后由设计器生成DDL(数据定义语言)语句来创建数据库。该模式仍然用.edmx文件来存储模型和映射信息。


2016-07-17
3.代码优先(Code First) 代码优先(Code First)是指先编写数据模型代码,然后再根据代码(classes)生成数据库;或者先编写创建数据库的代码,然后再从数据库用代码生成实体数据模型。 这种模式的基本思路是,不论是否存在数据库,开发人员都可以利用实体框架,用C#语言直接编写类和属性分别表示数据库中的表和列,然后通过实体框架提供的API处理数据库和代码所表示的概念之间的映射,而不是用.edmx文件保存映射关系。实体框架提供的数据访问API基于DbContext类,该类还提供了用于数据库优先模式和模型优先模式的工作流。 如果还没有创建数据库,利用该模式还能自动创建数据库。当修改模型后,还可以自动删除已创建的数据库并重新创建它。


2016-07-17
图形图像处理涉及的类非常多,这是因为屏幕上显示的所有信息本质上都是用图形或者图像绘制出来的。将图形、图像、文本、视频处理以后,一般都需要将其按某种方式绘制出来。为了实现绘制功能,WPF提供了3个主要的抽象基类:Shape类、Drawing类和Visual类,大部分对象的绘制功能都从这3个类之一派生,用这3个类的派生类创建的绘图对象分别称为Shape对象、Drawing对象和Visual对象。 根据实现的功能不同,Shape、Drawing和Visual分别放在不同的命名空间中。 • System.Windows.Media命名空间:提供了颜色、画笔、几何图形、图像、文本、音频、视频等丰富媒体处理功能。Drawing类和Visual类都在该命名空间内。 • System.Windows.Media.Imaging命名空间:提供了对图像进行编码和解码的类。


2016-07-17
• System.Windows.Controls 命名空间:提供除了形状控件外的其他各种WPF 控件,这些控件都是从Visual类派生的,包括Image控件以及我们在前面的章节中学习过的其他各种控件,这些控件按功能进行了分类,并提供了专门的实现。 • System.Windows.Shapes 命名空间:提供了基本的几何图形形状控件,这些控件都是从Shape类派生的,而由于Shape又是从Visual派生而来的,所以这些控件本质上也都是从Visual派生的。 WPF分别提供Shape、Drawing和Visual的动机在于让开发人员能更好地根据项目需要,合理选择不同的技术来处理内存消耗与应用程序性能的关系。System.Windows.Shapes命名空间中的控件和System.Windows.Controls命名空间中的控件用于顶层功能处理,继承自Drawing的对象用于中层功能处理,继承自Visual的对象用于底层功能处理。


2016-07-17
System.Windows.Media 命名空间下的 Geometry 类是定义二维几何图形的抽象基类,从该类的扩充类创建的对象统称为Geometry对象


2016-07-17
1.路径几何图形 路径几何图形是指把一系列图形按照某种方式组合在一起构成的形体。 WPF提供了两种定义路径几何图形的类:PathGeometry和StreamGeometry。这两个类提供了描绘由直线、弧线和曲线组成的多个复杂图形的方法。 Path控件专门用于绘制定义的路径几何图形。该类虽然是从Shape类继承的,但它实际上是利用Geometry对象来定义图形,然后再通过它的下列属性实现绘制功能。 • Fill属性:描述用哪种画笔(Brush)填充封闭的形


2016-07-17
状。 • Stroke属性:描述用钢笔(Pen类的实例)的哪种Color或Brush绘制形状的轮廓。 • StrokeThickness属性:描述轮廓的粗细。 (1)PathGeometry PathGeometry是PathFigure对象的集合,用于创建基本形状以及组合后的复杂形状。 PathFigureCollection中的每个PathFigure由一个或多个线段(Segment)组成


2016-07-17
(2)StreamGeometry StreamGeometry也是定义一个可包含曲线、弧线和直线的复杂几何形状,由于它的运行效率很高,所以是描绘物体表面装饰的理想选择。 选择用PathGeometry还是用StreamGeometry,可按下面的原则处理:当需要高效率描绘复杂的几何图形而且不使用数据绑定、动画或修改时,可考虑使用 StreamGeometry ,否则使用PathGeometry。另外,凡是能用StreamGeometry实现的功能也都可以用PathGeometry来实现。


2016-07-17
绘制基本图形 基本图形包括直线、折线、矩形、椭圆、多边形以及曲线等。 1.直线 LineGeometry类用StartPoint和EndPoint定义直线的起点和终点,也可以用路径标记语法来描述它。 直线没有填充功能,绘制直线时,即使设置了Fill属性也不会起作用。


2016-07-17
2.折线 折线是将一系列的点依次用直线相连,当这些点之间的距离很近时,其效果与曲线就很相似了。 有两种绘制折线的方法,一种是用LineGeometry实现,另一种是用PolyLine实现。


2016-07-17
3.矩形 RectangleGeometry类使用System.Windows命名空间下的Rect结构来定义矩形的轮廓,该结构指定矩形的左上角位置以及矩形的高度和宽度,另外还可以通过设置RadiusX和RadiusY属性创建圆角矩形。 用RectangleGeometry绘制矩形时,可以用Path元素的Stroke和StrokeThickness指定绘制的轮廓颜色和轮廓宽度,用Path元素的Fill属性指定填充区域(纯色、渐变色、图案、图像等)


2016-07-17
4.椭圆 EllipseGeometry类通过中心点(Center属性)、x半径(RadiusX属性)和y半径(RadiusY)来定义椭圆的形状。当x半径和y半径相同时,其效果就是一个圆


2016-07-17
5.多边形 多边形是由3条或3条以上的边组成的闭合图形,包括规则多边形和不规则多边形。 常用有两种实现多边形的技术,一种是用 PathGeometry或者 StreamGeometry来实现,另一种是用继承自Shape的Polygon或者PolyLine实现。不论采用哪种方


2016-07-17
式,其设计思路都是先生成一系列的点,然后再将这些点依次连接构成封闭的多边形。 如果将最后一个点和首个点相连,就是Polygon,否则就是PolyLine。


2016-07-17
将格式化文本转换为图形 在有些应用中,我们可能需要将文本字符串转换为离散的路径几何图形,然后再对其做进一步的处理,例如绘制空心字、沿文字的笔画进行移动的动画等。 将文本转换为Geometry对象的关键是使用Formatted


2016-07-17
Text对象,该对象用于创建格式化的文本。常用的构造函数语法为 public FormattedText( string textToFormat,   //要显示的文本 CultureInfo culture,   //文本的特定区域性 FlowDirection flowDirection, //读取文本的方向 Typeface typeface,   //设置文本格式时应使用的字体系列、粗细、样式和拉伸 double emSize,    //设置文本格式时应使用的字号 Brush foreground    //用于绘制每个标志符号的画笔 ) 创建 FormattedText 对象之后,即可使用该对象提供的 BuildGeometry 方法和 BuildHighlight Geometry方法将文本转换为Geometry对象,前者返回格式化文本的几何图形,后者返回格式化文本的边界框的几何图形。 由于转换后得到的Geometry对象变成了用笔画组合的


2016-07-17
几何图形,因此可以继续对其笔画和笔画内的填充区域做进一步的处理。笔画是指转换为图形后文本的轮廓,填充是指转换为图形后文本轮廓的内部区域。


2016-07-17
图像处理是指对各种图像格式的文件以及绘图结果进行处理,包括图像的编码、解码、元数据存储和读取、创建、加载、保存、压缩、解压缩、显示、绘制、剪裁、合并、平铺、拉伸、旋转、缩放、蒙版以及将矢量图形转换为图像等。 显示单个图像时,用 Image 控件实现即可;将图像拉伸或平铺到窗口、页面或者其他 WPF元素上时,一般用画笔(ImageBrush、DrawingBrush、VisualBrush)来实现。 除了 Image、ImageBrush、DrawingBrush、VisualBrush 这些常用的对象以外,其他托管的图像处理API都在


2016-07-17
ystem.Windows.Media.Imaging命名空间中。


2016-07-17
System.Windows.Media.Imaging 命名空间中提供了很多图像处理的类,其中最常用的类是BitmapSource类、BitmapFrame类和BitmapImage类。除此之外,还有从BitmapSource继承的其他类。 1.BitmapSource类 BitmapSource 类用于对图像进行解码和编码,它是 WPF 图像处理管线的基本构造块。该类表示具有特定大小和分辨率的单个不变的像素集,可以用它表示多帧图像中的单个帧,也可以表示在BitmapSource上执行转换的结果。 对于位图解码方案,BitmapSource基于用户计算机操作系统上已安装的编解码器自动发现并对其编码和解码。 BitmapSource最大为232个字节(64GB),如果图像的高度为h像素,宽度为w像素,每通道32位,共4通道,则h×w=64GB/(4B×4B)=4GB,即可加载的图像像素数(h×w)最大可达到4GB(32MB宽度×32MB高度)。换


2016-07-17
言之,只要内存容量足够大,即使是超级巨幅图像,利用BitmapSource也能轻松对其进行处理。 2.BitmapFrame类 BitmapFrame是BitmapSource派生类中比较常用的类之一,该类用于存储图像格式的实际位图数据。利用BitmapFrame能将各种格式的图像转换为位图,然后对其进行处理,如灰度处理、旋转、缩放、裁切等。 3.BitmapImage类 BitmapImage 也是从 BitmapSource 类派生的,它是一个为了加载 XAML 而优化的专用BitmapSource。 BitmapImage 的特点是只加载自动缩放后的结果,而不是先在内存中缓存原始大小的图像然后再对其进行缩放,所以该方式与直接指定Image的宽度或高度相比能大大节省内存的容量。 可以用 BitmapImage 的属性或者用其他 BitmapSource 对象(如 CroppedBitmap 或FormatConvertedBitmap)来转换图像。如缩放、旋转、更改图像的像素格式或裁切图像等。


2016-07-17
用Image控件显示图像时,除了用特性语法直接指定Source来绘制图像以外,还可以利用属性语法在Image的Source属性中用BitmapImage来绘制图像,这是建议的绘制图像的方式,比特性语法描述的运行效率高。例如: XAML: <Image Width="100"> <Image.Source> <BitmapImage DecodePixelWidth="100" UriSource="/images/img1.jpg" /> </Image.Source> </Image> 注意用BitmapImage作为Image的Source时,必须确保声明的DecodePixelWidth与Image的Width的值相同,DecodePixelHeight与Image的Height值相同。 另外,这段代码中没有同时指定宽度和高度,而是只指定二者之一,这样做可以保持原始图像的宽高比不变。如果同时指定 Image 的宽度和高度,而不指定它的 Stretch 属


2016-07-17
性,由于 Stretch属性的默认值是“Fill”,因此有可能会拉伸图像而引起变形。 下面用C#实现相同的功能: int width=100; Image myImage = new Image(); myInage.Width = width; // 注意:BitmapImage.UriSource必须在BeginInit和EndInit块之间 BitmapImage myBitmapImage = new BitmapImage(); myBitmapImage.BeginInit(); myBitmapImage.UriSource = new Uri("/images/img1.jpg"); myBitmapImage.DecodePixelWidth = width; myBitmapImage.EndInit(); myImage.Source = myBitmapImage;


2016-07-17
1.图像格式编码 图像编码是指将图像数据转换为特定图像格式的过程。可以用已编码的图像数据创建新的图像文件。 下面的示例演示使用编码器保存一个新创建的位图图


2016-07-17
像。 FileStream stream = new FileStream("new.bmp", FileMode.Create); BmpBitmapEncoder encoder = new BmpBitmapEncoder(); TextBlock myTextBlock = new TextBlock(); myTextBlock.Text = "Codec Author is: " + encoder.CodecInfo.Author.ToString(); encoder.Frames.Add(BitmapFrame.Create(image)); encoder.Save(stream);


2016-07-17
(2)创建BitmapSource 下面的代码演示了如何创建BitmapSource,创建后用它设置Image的Source属性即可。运行效果见图13-8中左侧的图。 private BitmapSource CreateBitmapSource() { // 定义BitmapSource使用的参数 PixelFormat pf = PixelFormats.Bgr32; int width = 200; int height = 200; int rawStride = (width * pf.BitsPerPixel + 7) / 8; byte[] rawImage = new byte[rawStride * height]; // 初始化图像数据 Random value = new Random(); value.NextBytes(rawImage); // 创建BitmapSource


2016-07-17
BitmapSource bitmap = BitmapSource.Create(width, height, 96, 96, pf, null, rawImage, rawStride); return bitmap; } (3)从图像文件中创建BitmapImage 下面的代码演示了如何从图像文件中创建位图(BitmapImage)。运行效果见图13-8中的中间图。 private BitmapImage CreateBitmapImage() { BitmapImage bi = new BitmapImage(); // 注意BitmapImage.UriSource必须在BeginInit/EndInit块内 bi.BeginInit(); bi.UriSource = new Uri("/Resources/Content/img1.jpg", UriKind.Relative); bi.EndInit();


2016-07-17
return bi; } 另外还要注意,在这段代码中,用C#代码读取的图像文件是相对于当前的.exe文件的路径而言的。由于项目的Resources/Content文件夹下的所有文件的【生成操作】属性都是“内容”,【复制到输出目录】属性都是“如果较新则复制”,所以这段代码读取的图像文件路径实际上是bin/Debug/Resources/Content/img1.jpg,而不是项目下的Resources/Images/img1.jpg。 (4)用BitmapFrame创建BitmapSource 下面的代码演示了如何用 BitmapFrame 创建 BitmapSource,并演示了如何将编码后的 TIFF图像保存到文件中。观察bin\Debug下的Empty.tif可看到创建的文件。运行效果见图13-8中的右侧图。 private BitmapSource CreateTifImageFile() { int width = 200;


2016-07-17
int height = width; int stride = width / 8; byte[] pixels = new byte[height * stride]; Random value = new Random(); value.NextBytes(pixels); List<System.Windows.Media.Color> colors = new List<System.Windows.Media.Color>(); colors.Add(System.Windows.Media.Colors.Red); colors.Add(System.Windows.Media.Colors.Blue); colors.Add(System.Windows.Media.Colors.Green); BitmapPalette myPalette = new BitmapPalette(colors); BitmapSource image = BitmapSource.Create(width, height, 96, //DpiX 96, //DpiY PixelFormats.Indexed1, myPalette, pixels, stride); FileStream stream = new FileStream("empty.tif", FileMode.Create); TiffBitmapEncoder encoder = new


2016-07-17
iffBitmapEncoder(); textBlock3.Text += "\r\n编码器:" + encoder.CodecInfo.FriendlyName; encoder.Frames.Add(BitmapFrame.Create(image)); encoder.Save(stream); return image; }


2016-07-17
TileBrush类是一个抽象基类,由于大部分WPF元素的区域都可以用继承自TileBrush的画笔来绘制


2016-07-17
1.基本组件 TileBrush类包括三个主要的组件:内容、图块和输出区域。 (1)内容 从TileBrush继承的画笔类型不同,“内容”的含义也不同。 • 如果画笔为ImageBrush,则表示“内容”为图像。此时用ImageSource属性指定ImageBrush的内容。 • 如果画笔为DrawingBrush,则表示“内容”为绘图。此时用Drawing属性指定DrawingBrush的内容。 • 如果画笔为VisualBrush,则表示“内容”为可视元素。此时用Visual属性指定VisualBrush的内容。 (2)图块 图块是用“内容”构造出来的基本块。TileBrush 提供了一个 Stretch属性,该属性用 Stretch枚举指定如何用“内容”来构造图块。


2016-07-17
(3)输出区域 输出区域指如何用“图块”填充目标区域。TileBrush 提供了一个 TileMode 属性,该属性用TileMode指定如何填充目标区域,包括平铺、水平翻转、垂直翻转等。例如Ellipse用Fill属性指定将图块填充到椭圆的封闭区域,Button 用 Background 指定将图块填充到背景区域,TextBlock用Foreground指定将图块填充到文本的笔画区域等。 2.拉伸图块(Stretch) 从 TileBrush 继承的画笔类型都可以用 Stretch 属性控制如何拉伸“图块”。该属性用 Stretch枚举来表示,可用的枚举值有如下几个。 • None:图块保持其原始大小。 • Fill:调整图块的大小以填充目标尺寸,不保留纵横比。 • Uniform:在保留图块原有纵横比的同时调整图块的大小,以适合目标尺寸。 • UniformToFill:在保留图块原有纵横比的同时调整图


2016-07-17
块的大小,以填充目标尺寸。如果目标矩形的纵横比与图块的纵横比不相同,则对图块进行剪裁(将目标矩形尺寸以外的部分裁剪掉)以适合目标矩形的大小。 下面的代码演示了如何在ImageBrush中使用Stretch。 <Rectangle Width="125" Height="175" Stroke="Black" StrokeThickness="1"> <Rectangle.Fill> <ImageBrush Stretch="None" ImageSource="/images/img1.jpg"/> </Rectangle.Fill> </Rectangle>


2016-07-17
3.平铺方式(TileMode) 从TileBrush继承的画笔类型都可以用TileMode属性控制如何用“图块”填充“输出区域”。 TileMode属性用TileMode枚举来定义,可用的枚举值如下。 • None:不平铺。仅绘制基本图块。 • Tile:平铺。绘制基本图块,并通过重复基本图块来填充剩余的区域,使一个图块的右边缘靠近下一个图块的左边缘,底边缘和顶边缘也是如此。 • FlipX:与Tile相同,只不过图块的交替列水平翻转。 • FlipY:与Tile相同,只不过图块的交替行垂直翻转。 • FlipXY:FlipX和FlipY的组合。


2016-07-17
4.Viewport和ViewportUnits TileBrush默认生成单个图块并拉伸此图块以完全填充输出区域,Viewport属性决定了基本图块的大小和位置,ViewportUnits属性决定了Viewport是使用绝对坐标还是相对坐标。从TileBrush继承的画笔类型都可以用Viewport属性和ViewportUnits属性。 如果Viewport使用相对坐标,则显示的图像是相对于输出区域的大小而言的,输出区域的左上角用点(0,0)表示,右下角用(1,1)表示,其他坐标(0和1之间的值)表示该区域内的其他位置。 如果Viewport使用绝对坐标,将ViewportUnits属性设置为Absolute即可。


2016-07-17
另外,还可以通过Viewbox和ViewboxUnits属性来实现。Viewbox属性决定了基本图块的大小和位置,ViewboxUnits属性决定了Viewbox是使用绝对坐标还是相对坐标。 5.Transform和RelativeTransform TileBrush类提供了两个变换属性:Transform和RelativeTransform。使用这些属性可以旋转、缩放、扭曲和平移画笔的内容。 在动画与多媒体一章中,我们已经学习了 Transform 的基本用法,这一节我们学习如何用RelativeTransform对画笔应用变换。 向画笔的RelativeTransform属性应用变换时,变换会在其输出映射到绘制区域之前进行。处理顺序如下。 (1)确定画笔输出。对GradientBrush而言意思是确定渐变区域。对TileBrush而言意思是将Viewbox映射到Viewport。 (2)将画笔输出投影到1×1的变换矩形上。 (3)如果指定了RelativeTransform,则对画笔输出进


2016-07-17
行RelativeTransform变换。 (4)将变换后的输出投影到要绘制的区域。 (5)如果指定了Transform,则对画笔输出进行Transform变换。 由于画笔输出是在映射到1×1矩形的情况下进行RelativeTransform,因此变换中心和偏移量值都是相对的。例如用RotateTransform将画笔输出绕其中心旋转45度,可将RotateTransform的CenterX指定为0.5,将CenterY也指定为0.5。


2016-07-17
Viewport3D控件 System.Windows.Controls命名空间下的Viewport3D控件是在二维平面上呈现三维场景的容器控件。一个窗口或页面中可以包含多个由Viewport3D组成的3D场景,就像在一个页面中可以有多个二维控件一样。不过,一般情况下,应尽可能在一个三维场景中呈现各种三维模型。 1.WPF三维坐标系 在WPF中,三维坐标系中的每个点由X、Y、Z三个坐标轴来描述,原点(0,0,0)默认在三维坐标系的中心,如果X轴正方向朝右,Y轴的正方向朝上,则Z轴的正方向从原点指向屏幕外。 2.Viewport3D类 在Viewport3D内,可指定一个照相机以及由一个或多个从Visual3D继承的对象。除此之外, Viewport3D还具有其他二维元素所具有的属性,例如动画、触发器等。 Viewport3D类继承的层次结构如下。


2016-07-17
Object→DispatcherObject→DependencyObject →Visual→UIElement→FrameworkElement→Viewport3D Viewport3D的子元素可指定以下属性。 (1)Camera属性 Camera 属性用于指定该场景使用哪种照相机。如果不指定照相机,Viewport3D 会在加载时自动创建一个默认的相机。不过,一般情况下,都需要开发人员明确指定使用哪种相机。 下面的代码将透视相机(PerspectiveCamera)放在三维坐标(0,0,7)处,如果相机的观察点是三维坐标系的原点(0,0,0),还需要将LookDirection设置为(0,0,-7)。 <Viewport3D.Camera> <PerspectiveCamera Position="0,0,7" LookDirection="0,0,-7"/> </Viewport3D.Camera>


2016-07-17
2)Children属性 该属性用于获取Viewport3D的所有Visual3D构成的集合。 3.ModelVisual3D对象 ModelVisual3D类用于在Viewport3D内呈现三维模型。 (1)Content属性 ModelVisual3D的Content属性用于获取或设置从Model3D继承的对象。Model3D是一个抽象类,该类为三维模型提供命中测试、坐标转换和边界框计算等通用的功能,其继承的层次结构如下。 Object→DispatcherObject→DependencyObject→Freezable→Animatable→Model3D


2016-07-17
(2)Children属性 ModelVisual3D的 Children属性用于获取ModelVisual3D的子级的集合。利用该属性可将其子级的集合组合在一起作为一个对象来处理。 在XAML中也可以省略该属性声明,例如: <ModelVisual3D> <ModelVisual3D>……</ModelVisual3D> <ModelVisual3D>……</ModelVisual3D> </ModelVisual3D> 这段代码和下面的代码是等价的。 <ModelVisual3D> <ModelVisual3D.Children> <ModelVisual3D>……</ModelVisual3D> <ModelVisual3D>……</ModelVisual3D> </ModelVisual3D.Children> </ModelVisual3D>


2016-07-17
(3)Transform属性 ModelVisual3D的Transform属性用于对该对象进行三维变换(平移、旋转、缩放等)


2016-07-17
WPF提供了多种类型的相机,其中最常用的有两种:透视相机(PerspectiveCamera类)和正交相机( OrthographicCamera 类)


2016-07-17
1.Camera类和ProjectionCamera类 Camera类是所有相机的基类,其作用是为三维场景指定观察位置等基本信息。 ProjectionCamera 类的作用是指定不同的投影方式以及其他属性来更改观察者查看三维模型的方式。例如指定相机的位置、方向、视野以及定义场景中“向上”方向的向量。该类继承的层次结构如下。 Object→DispatcherObject→DependencyObject→Freezable→Animatable →Camera→ProjectionCamera 由于相机可以位于场景中的任何位置,因此当相机位于模型内部或者紧靠模型时,可能无法区分不同的对象。为了能正确观察场景中的所有模型,可通过 ProjectionCamera 提供的NearPlaneDistance指定相机拍摄的最小距离,用


2016-07-17
FarPlaneDistance指定相机拍摄的最大距离,不在NearPlaneDistance和FarPlaneDistance范围内的对象将不再绘制。 2.透视相机(PerspectiveCamera) 透视相机也叫远景相机,这种相机提供消失点透视功能,是在实际应用项目中最常用的相机。该类是从ProjectionCamera类继承而来的,继承的层次结构如下。 Object→DispatcherObject→DependencyObject→Freezable→Animatable →Camera→ProjectionCamera→PerspectiveCamera 透视相机的工作原理与普通照相机的镜头类似,观察的对象有“远小近大”的效果。例如让相机沿Z轴朝向观察目标移动,当相机位置靠近Z轴中心时,随着Z坐标值越来越小,观察的对象也会越来越大。


2016-07-17
3.正交相机(OrthographicCamera) 正交相机只是描述了一个侧面平行的取景框,而不是侧面汇集在场景中某一点的取景框,因此对观察的对象没有透视感,观察目标也不会随着距离的变化而变小或变形。换句话说,用这种相机观察某个对象时,不论距离远近,该对象都一样大。 OrthographicCamera类也是从ProjectionCamera类继承而来的,继承的层次结构如下。 Object→DispatcherObject→DependencyObject→Freezable→Animatable →Camera→ProjectionCamera→OrthographicCamera


2016-07-17
下面是WPF提供的光类型,这些光类型都是从基类Light派生而来的。 1.环境光(AmbientLight) 环境光将光投向三维场景中的各个方向,它能使所有3D对象都能均匀受光,与被光照的3D对象的位置或方向无关。但是要注意,如果只用环境光,则这些 3D 对象可能会像褪了色的物体一样。为了获得最佳的光照效果,一般还需要添加非环境光。


2016-07-17
2.锥形投射光(SpotLight) 锥形投射光的投射效果和手电筒的光照效果类似,这种光既有位置又有方向,而且离目标越远光照效果越暗。 • InnerConeAngle属性:内部锥角。表示光最亮的中心部分的角度。 • OuterConeAngle属性:外部锥角。表示光较暗的外围部分的角度。 如果创建强光,需要将内部锥角和外部锥角设置为同一值。如果内部锥角值大于外部锥角值,则以外部锥角值为准。 锥形投射光不会影响到位于锥形发光区域以外的三维对象部分。 3.定向直射光(DirectionalLight) 定向直射光沿着特定的方向均匀地投射到 3D 对象,其效果与地球上的阳光照射效果相似,即光的强度衰减可以忽略不计。或者说,这种光没有位置,只有方向。


2016-07-17
4.点光(PointLight) 点光从一个点向所有方向投射光,其效果与普通的灯泡照明效果相似。PointLight 公开了多个衰减属性,这些属性确定了光源的亮度如何随距离的增加而减小。


2016-07-17
材料(Meterial) 材料也叫材质,用于描述 3D 模型表面的特征,包括颜色、纹理和总体外观。为了和开发工具的叫法一致,我们仍然将其称为材料。 材料也是靠照明来体现的,它和光的不同之处是,光影响的是场景中所有的 3D 模型,而材料影响的是该3D模型自身的表面。 在二维图形中,用Brush类在屏幕的指定区域绘制颜色、图案、渐变或其他可视化内容。但是,由于三维对象的外观是照明模型,而不是仅仅显示表面的颜色或图案,所以还需要指定使用哪种材料。材料的质量不同,其反射光的方式也会不同,如让有些材料表面粗糙、有些材料表面光滑,有些材料可以吸收光,而有些材料则具有自发光功能等。


2016-07-17
1.漫反射材料(DiffuseMaterial) DiffuseMaterial类用于确定三维对象在环境光(AmbientLight)照射下材料的颜色,其效果和墙面的喷漆相似。该类常用的属性如下。 • AmbientColor 属性:获取或设置一种颜色,该颜色表示材料在 AmbientLight 指定的某种颜色照射下材料本身的反射颜色。 • Color属性:获取或设置应用到材料的颜色。 • Brush属性:获取或设置应用到材料的画笔。


2016-07-17
2.高光反射材料(SpecularMaterial) SpecularMaterial类使对象自身产生对强光的反射,从而产生表面坚硬、发亮等效果。这种材料对光照反射的颜色由材料的颜色属性来决定。


2016-07-17
• Color属性:获取或设置应用到材料的颜色。 • Brush属性:获取或设置应用到材料的画笔。 • SpecularPower 属性:获取或设置材料反射光照的角度值,值越大,反射闪光的大小和锐度也越大。


2016-07-17
3.自发光材料(EmissiveMaterial) EmissiveMaterial类使模型表面所发出的光与画笔设置的颜色相同,其效果就像材料正在发射与Brush的颜色相同的光一样,但它只对该模型本身起作用,即这种效果不会影响别的模型。 • Color属性:获取或设置材料的颜色。 • Brush属性:获取或设置材料的画笔。


2016-07-17
有两种三维建模的方式,一种是利用各种3D建模工具建模,另一种是编写C#代码实现动态建模。 用3D建模工具建模时,又有两种办法,一种是利用VS2012自带的模型编辑器直接创建三维模型,另一种是利用其他专业建模工具创建三维模型。


2016-07-17
VS2012自带了一个3D模型编辑器,利用它可直接创建和编辑三维模型。 模型编辑器可直接创建和编辑的三维模型只有FBX格式,但可以将其另存为OBJ格式或者DAE格式的三维模型文件。


2016-07-17
1.使用LINQ to Entities加载对象 这种方式称为延迟加载(Lazy Loading),即先利用LINQ to Entities定义查询语句,然后再通过foreach或者通过数据绑定获取查询结果时才将数据加载到对象中。下面的代码演示了如何使用LINQ to Entities加载Student对象。 using (var context = new MyDbEntities()) { var q = from t in context.Student where t.ChengJi >= 60 select new { 学号 = t.XueHao,姓名 = t.XingMing, 性别 = t.XingBie, 成绩 = t.ChengJi }; dataGrid1.ItemsSource = q.ToList(); }


2016-07-17
2.使用Load方法加载对象


2016-07-17
这种方式称为显式加载,即通过Load方法将数据加载到实体中。例如: using (var db = new MyDbEntities()) { db.Student.Load(); //相当于执行不带条件的LINQ查询语句 dataGrid1.ItemsSource = db.Student.Local; //相当于执行ToList方法 } 下面通过例子说明这两种方式的具体用法。


2016-07-17
如果是窗口,可在当前窗口的Closing事件中调用实例的Dispose方法。例如: public partial class Window1 : Window { private MyDbEntities context = new MyDbEntities(); public Window1() { InitializeComponent(); this.Closing += Window1_Closing; } void Window1_Closing(object sender, System.ComponentModel.CancelEventArgs e) { context.Dispose(); }


2016-07-17
如果是Page,可在当前页的UnLoaded事件中调用实例的Dispose方法。例如: public partial class Page1 : Page { MyDbEntities context = new MyDbEntities(); public Page1 () { InitializeComponent(); this.Unloaded += Page1_Unloaded; } void Page1_Unloaded(object sender, RoutedEventArgs e) { context.Dispose(); }


2016-07-17
2.使用using语句实例化实体框架上下文 第二种方式是使用using语句实例化实体框架上下文,然后在using块内利用该实例提供的属性和方法操作数据库,此时退出using块后就会立即释放该实例。一般用法为: using(var context = new MyDbEntities()) { //语句块 }


2016-07-21
修改对象中的数据时,常用有两种办法。 第一种办法是使用实体框架和 LINQ to Entities 修改数据,即先利用查询得到要修改的实体对象,修改后再调用实体对象上下文的SaveChanges方法将其保存到数据库中,这是建议的修改办法。 第二种办法是通过MyDbEntities对象(从DbContext继承的实体数据模型上下文)的Dababase属性调用ExecuteSqlCommand方法,在该方法中直接传递要执行的SQL语句(修改、添加、删除等操作)。但是由于传递SQL语句只有在执行时才能发现SQL语句是否有语法错误,因此一般不使用这种办法。


2016-07-21
private void btn_Click(object sender, RoutedEventArgs e) { Button btn = e.Source as Button; if (btn.Content.ToString() == "使用LINQ修改") { var q = from t in context.Student where t.XingMing == "张三玉" select t;


2016-07-21
foreach (var v in q) { v.ChengJi += 10; } int i = context.SaveChanges(); txtInfo.Text = "使用LINQ修改了" + i + "条记录"; ShowResult(); } else if (btn.Content.ToString() == "使用SQL修改") { var db = context.Database; try { int i = db.ExecuteSqlCommand( "update Student set ChengJi=ChengJi+10 where XingMing={0}", "张三玉"); txtInfo.Text = "使用SQL修改了" + i + "条记录"; ShowResult(); }


2016-07-21
catch (Exception ex) { MessageBox.Show("执行失败:" + ex.Message); } } } private void ShowResult() { //由于SQL直接更改数据库,不修改实体上下文 //所以必须获取新的实体上下文实例才能得到最新的数据 if (context != null) { context.Dispose(); context = new MyDbEntities(); } var q = from t in context.Student where t.XingMing == "张三玉" select new { 姓名 = t.XingMing, 成绩 =


2016-07-21
t.ChengJi }; dataGrid1.ItemsSource = q.ToList(); } }


2016-07-21
private void btn_Click(object sender, RoutedEventArgs e) { string s = (e.Source as Button).Content.ToString(); if (s == "添加数据") { using (var context = new MyDbEntities()) { Student student = new Student() { XueHao = "06001001",


2016-07-21
XingMing = "胡启", XingBie = "男", XueYuanID = "01", ChengJi = 77 }; try { context.Student.Add(student); context.SaveChanges(); } catch(Exception ex) { MessageBox.Show("添加失败:" + ex.Message); } } ShowResult(); } else if (s == "删除数据") {


2016-07-21
using (var context = new MyDbEntities()) { var q = from t in context.Student where t.XueHao == "06001001" select t; foreach (var v in q) { context.Student.Remove(v); } context.SaveChanges(); } ShowResult(); } } }


2016-07-21
实际上,DataGrid控件是一个功能非常多的控件,利用该控件,除了可以显示、编辑数据之外,还可以利用它进行灵活的样式控制和数据校验处理。 将DataGrid控件添加到WPF窗口或页面后,该控件默认具有的功能主要如下。 • 支持自动排序。用鼠标单击某个列标题,则对应的列就会自动按升序或降序排序(单击升序,再单击降序)。字母顺序区分大小写。 • 支持自动调整大小功能。双击标题之间的列分隔符,该分隔符左边的列会自动按照单元格的内容展开或收缩。 • 单击DataGrid左上角的矩形块可以选择整个表,单击每行左边的矩形块可以选择整行。 • 支持调整列宽功能。在标题区拖动列分隔符可调整显示的列宽。 • 支持编辑功能。双击单元格或者按<F2>键可直接编辑单元格内容。在编辑模式下,按<Enter>键提交更改,或者按<Esc>键将单元格恢复为更改前的值。


2016-07-21
• 如果用户滚动至网格的结尾,将会看到用于添加新记录的行。用户可在该行中直接添加数据,DataGrid控件会自动将其添加到ItemsSource中。


2016-07-21
DataGrid 通过 ItemsSource 属性来绑定数据。当设置该属性后,DataGrid控件将自动生成对应的列,生成列的类型取决于列中数据的类型。 1.DataGrid提供的列类型 DataGrid默认提供的列类型有以下几种。 • DataGridTextColumn:String类型,默认用字符串显示该列的内容。 • DataGridCheckBoxColumn:Boolean类型,默认用CheckBox控件显示该列的内容。 • DataGridComboBoxColumn:Enum类型,默认用ComboBox控件显示该列的内容。


2016-07-21
• DataGridHyperlinkColumn:Uri类型,默认用Hyperlink控件显示该列的内容。 • 自定义类型:用DataGridTemplateColumn自定义其他数据类型。 对于自定义类型来说,一般在资源字典中定义模板,在App.xaml中合并资源字典。


2016-07-22
下面的代码演示了如何分别定义显示模板和编辑模板。 <DataTemplate x:Key="PhotoTemplate"> <Image Height="30" Source="{Binding Photo}" /> </DataTemplate> <DataTemplate x:Key="EditingPhotoTemplate"> …… </DataTemplate> 下面的代码演示了如何在页的DataGrid中引用定义的模板。


2016-07-22
<DataGrid Name="DG1" ItemsSource="{Binding}" AutoGenerateColumns="False" > <DataGrid.Columns> <DataGridTemplateColumn Header="照片" CellTemplate="{StaticResource PhotoTemplate}" CellEditingTemplate="{StaticResource EditingPhotoTemplate}" /> </DataGrid.Columns> </DataGrid> DataGrid的AutoGenerateColumns属性控制是否自动生成列,该属性默认为true。用XAML描述绑定的列时,需要将该属性设置为false。


2016-07-22
2.自定义日期类型 对于日期类型的数据,可在自定义模板中让其按照“yyyy-MM-dd”的格式显示,编辑时可利用DatePicker控件显示日历。 3.导入图像


2016-07-22
用DataGrid编辑数据时,若要实现图像导入的功能,可以先得到选定的行,将其转换为绑定的实体对象,然后再获取对象对应的属性,即可实现照片导入。在 C#代码中,可通过 DataGrid的SelectedCells属性获取选定的单元格,通过SelectedItem获取选定的一行,通过SelectedItems属性获取选定的所有行。


2016-07-28
如果在数据库中又创建了新表或修改了现有表的结构,对应的实体数据模型也必须更新。更新办法是在实体数据模型的设计界面中单击鼠标右键,在快捷菜单中选择“从数据库更新模型”,然后按提示更新。


2016-08-11
利用partial修饰符可将类的定义分布在多个文件中,编译器编译带有partial修饰符的类时,会自动将这些文件合并在一起。例如将一个类的编写工作同时分配给3个人,一人负责写一个文件,文件名分别为MyClassP1.cs、MyClassP2.cs、MyClassP3.cs,每个文件内都使用下面的形式定义MyClass类: public partial class MyClass { //...... } 这样一来,就可以让多个人同时在一个类中分别实现自己的代码,而且还能相互看到其他文件中


2016-08-11
MyClass类定义的成员(属性、方法等)。 partial修饰符的另一个用途是隔离自动生成的代码和人工书写的代码,例如Windows窗体应用程序、WPF应用程序等采用的都是这种办法。 使用partial修饰符时,如果该类是从基类和接口继承的,只需要在一个文件内声明即可,不需要每个文件都声明。如果每个文件都声明,必须保证这些声明完全一致。


2016-08-11
用static声明的静态成员在外部只能通过类名称来引用,不能用实例名来引用。


2016-08-11
结构是值类型,它和类的主要区别是结构中的数据保存在堆栈(Stack)中而不是保存在堆(Heap)中。另外,所有结构也都和类一样默认隐式地从 Object 类继承,但结构不能继承自其他结构继承。


2016-08-11
定义一个结构后,执行时系统只是在内存的某个临时位置保存结构的定义。当声明结构类型的变量时,调用结构的构造函数也是使用new运算符,但它只是从临时位置将结构的定义复制一份到栈中,而不是在堆中分配内存。


2016-08-18
通过委托调用方法 定义了委托类型以后,就可以像使用其他类型一样使用委托。 通过委托,可将方法作为实体赋值给变量,也可以将方法作为委托的参数来传递。下面的方法将f作为参数,f为自定义的委托类型MyFunction: public static double[] Apply(double[] a, MyFunction f) { double[] result = new double[a.Length]; for (int i = 0; i < a.Length; i++) result[i] = f(a[i]); return result; } 假如有下面的静态方法: public static double Square(double x) { return x * x; } 那么,就可以将静态的Square方法作为MyFunction


2016-08-18
类型的参数传递给Apply方法: double[] a = {0.0, 0.5, 1.0}; double[] squares = Apply(a, Square); 除了可以通过委托调用静态方法外,还可以通过委托调用实例方法。例如: class Multiplier { double factor; public Multiplier(double factor) { this.factor = factor; } public double Multiply(double x) { return x * factor; } } 则可以用下面的代码将Multiply方法作为MyFunction类型的参数传递给Apply方法: Multiplier m = new Multiplier(2.0); double[] doubles = Apply(a, m.Multiply);


2016-08-18
实际上,也可以使用匿名函数创建委托,这是即时创建的“内联方法”。由于匿名函数可以查看外层方法的局部变量,因此在这个例子中也可以不创建D2对象,而是直接写出实现的代码: double[] doubles = D1.Apply(a, (double x) => x * 2.0); 这是用 Lambda 表达式来实现的,限于篇幅,本书不再介绍 Lambda 表达式的用法,有兴趣的读者可参考相关资料。 另外,定义了委托类型后,还可以像创建类的实例那样来创建委托的实例。委托实例封装了一个调用列表,该列表包含一个或多个方法,每个方法都是一个可调用的实体。


2016-08-19
输出参数 输出参数(output parameter)用于传递返回的参数,用out关键字声明。格式为 out 参数类型 参数名 对于输出参数来说,调用方法时提供的实参的初始值并


2016-08-19
不重要,因此声明时也可以不赋初值。除此之外,输出参数的用法与引用参数的用法类似。 由于return语句一次只能返回一个结果,当一个方法返回的结果有多个时,仅用return就无法满足要求了。此时除了可以利用数组让其返回多个值之外,还可以用out关键字来实现。


2016-08-19
参数数组 参数数组用于向方法传递可变数目的实参,用params关键字声明。例如,System.Console 类的Write和WriteLine方法使用的就是参数数组。它们的声明如下。 public class Console { public static void Write(string fmt, params object[] args) {...} public static void WriteLine(string fmt, params object[] args) {...} ...... } 如果方法有多个参数,只有最后一个参数才可以用参数数组声明,并且此声明的参数数组的类型必须是一维数组类


2016-08-19
型。 参数数组的用法与常规的数组类型参数的用法完全相同。但是,在调用具有参数数组的方法时,既可以传递参数数组类型的单个实参,也可以传递参数数组的元素类型的任意数目的实参。实际上,参数数组自动创建了一个数组实例,并用实参对其进行初始化。


2016-08-22
WPF是.NET框架的一个子集,WPF应用程序的关键设计思想是将界面标记描述(XAML)与实现代码(C#)有效分离,从而让美工(界面设计)和开发人员(代码实现)可真正同步进行。另外,WPF底层是通过GPU硬件加速来实现而不是靠纯软件的GDI+来实现,这是WPF和WinForm的本质区别。 开发在Windows 7操作系统上运行的C/S客户端应用程序时,使用WPF应用程序能发挥最大的运行性能,这种优势是WinForm应用程序所无法比拟的。 将WPF应用程序通过ClickOnce技术部署在某个Web服务器上以后,客户端即可下载、安装并独立运行它。另外,在 Web 服务器上再次部署新的升级版本时,客户端还可以自动感知和升级。或者说, WPF 应用程序的服务器部署以及客户端下载和安装升级过程与我们常见的 QQ、飞信、360 安全卫士等目前流行的各种软件的下载、安装和升级过程非常类似。


2016-09-19
WPF提供的动画类型都在System.Windows.Media.Animation命名空间下。


2016-09-19
1.使用WPF动画计时系统 WPF内部实现了一套高效的动画计时系统,开发人员可利用它直接对控件的附加属性值进行动画处理。在动画计时系统下,按照动画处理的技术,可分为基本动画、关键帧动画和路径动画。但如果从是否可交互以及受支持的程度来看,又可以分为本地动画、时钟动画和演示图板动画。表10-2为不同的动画实现技术的区别。其中“基于实例”指的是在WPF的计时系统中直接用C#代码对控件的属性值进行动画处理;“可交互”是指在WPF的计时系统中,是否可以控制动画的暂停、继续、停止等交互操作。


2016-09-19
对于演示图板动画来说,WPF提供了两种演示图板:ParallelTimeline和Storyboard,这两个类都是从 Timeline 类继承的,Storyboard 是一个更大范围的演示图板模型,可以将其认为是ParallelTimeline的集合。在一个Storyboard中可以包含多个ParallelTimeline,每个ParallelTimeline又可以包含多个TimeLine。 对控件的属性进行动画处理时,技术选择的原则如下。 • 如果不需要交互(启动、暂停、继续、停止等),使用本地动画(仅用 C#代码)实现比较方便,当然也可以用Storyboard来实现(XAML或者C#)。 • 如果需要交互,而且对象个数不多,使用Storyboard和XAML比较方便。


2016-09-19
• 如果对象个数非常多,而且这些对象处理的属性相同,同时又需要交互控制,此时使用时钟动画效率比较高,但只能用C#来实现。


2016-09-19
2.不使用WPF动画计时系统 如果不使用WPF的动画计时系统,也可以用CompositionTarget的Rendering事件逐帧进行动画处理。在这种方式下,由于每帧都会自动调用一次此事件处理程序,因此可在该事件处理程序中根据用户与最后一组对象交互的情况,重新计算下一帧将要显示的内容。 既然逐帧动画不使用WPF的动画计时系统,当然也不能利用WPF的动画计时行为进行暂停、继续、停止等交互操作。换言之,只能自己编写C#代码来实现交互功能。 逐帧动画无法在样式、控件模板或数据模板中进行定义。


2016-09-19
除了逐帧动画以外,本地动画、时钟动画以及演示图板动画都是基于Timeline类来实现的。


2016-09-19
1.Storyboard Storyboard 类是一组时间线的容器,它由一条总时间线控制,在它包含的所有时间线上定义的动画都可以并行执行,而且还可以通过该容器整体控制动画的启动、暂停、继续、停止等交互操作。 使用Storyboard进行动画处理需要完成下面的步骤。 • 声明一个Storyboard以及一个或多个动画。 • 使用TargetName和TargetProperty附加属性指定每个动画的目标对象和属性。 • 启动Storyboard执行动画。 启动Storyboard的办法有两种:一种是在C#代码中调用Storyboard类提供的Begin方法来实现;另一种是在XAML中利用Trigger或DataTrigger来实现。 2.Timeline 时间线(Timeline)表示一个总时间段,总时间段由一个或多个子时间段组成,在每条时间线的每个子时间段内,都可以定义一个动画。


2016-09-19
Timeline 类是定义计时行为的抽象基类。该抽象类提供了控制动画播放的属性,从该类继承的各种动画类都可以使用这些属性。包括基本动画、关键帧动画、路径动画以及自定义动画。 (1)Duration属性 该属性是一个 TimeSpan类型,表示动画持续的时间,在 XAML中用“时:分:秒”的形式表示。默认情况下,当时间线到达Duration指定的值时,就会停止播放动画。 使用该属性时需要注意,即使子时间线的长度大于 Storyboard 时间线本身的长度,当Storyboard停止播放时,它的所有子时间线也会立即停止播放。例如: <Storyboard x:Key="Storyboard1" Duration="0:0:3"> <DoubleAnimation Storyboard.TargetName="Rectangle1" To="500" Duration="0:0:5" Storyboard.TargetProperty="Width" /> <DoubleAnimation Storyboard.TargetName="Rectangle2" To="200"


2016-09-19
Duration="0:0:2" Storyboard.TargetProperty="Width" /> </Storyboard> 在这段XAML代码中,虽然Rectangle1的Duration设置为5秒,但是由于Storyboard的Duration设置为3秒,所以该动画播放3秒就全部结束了。 同样道理,如果演示图板中的某条时间线包含子时间线,只要这条时间线停止动画,它的子时间线也会立即停止动画。


2016-09-19
(2)RepeatBehavior属性 该属性指定时间线播放的重复行为。默认情况下,重复次数为 1.0,即播放一次时间线。RepeatBehavior="0:0:10"表示重复到 10 秒为止;RepeatBehavior="2.5x"表示重复 2.5 次;RepeatBehavior="Forever"表示一直重复,直到手动停止或停止计时系统为止。 至于如何重复,还要看其他属性。例如Timeline提供有一个IsCumulative属性,该属性指示是否在重复过程中累加动画的基值(默认为false,即不改变基值)。基值是指在XAML中指定的初始值,假如在XAML中设置某个Label的Width="50",则其基值就是50,此时如果宽度动画从50 变化到70(From="50" To="70"),RepeatBehavior="6x",并


2016-09-19
且IsCumulative="true",则第1次从基值50渐变到70,然后基值变为70;第2次再从基值(70)到90,然后基值变为90;第3次从基值(90)到110;依此类推。 如果IsCumulative="false",则重复的6次全部都是从50到70,即基值不变。


2016-09-19
(3)AutoReverse属性 该属性指定Timeline在每次向前迭代播放结束后是否继续反向迭代播放。 假设动画的任务是将宽度从50~100播放。如果Au


2016-09-19
oReverse="true",则当播放到宽度为100后就会继续将宽度从100~50反向播放。 另外,如果AutoReverse="true"并且RepeatBehavior设置为重复次数,则一次重复是指一次向前迭代和一次向后迭代。例如: <DoubleAnimation Storyboard.TargetName="MyRectangle" Storyboard.TargetProperty="Width" From="0" To="100" Duration="0:0:5" RepeatBehavior="2" AutoReverse="True" /> 在这段XAML中,重复次数为2,实际上总共用了20秒时间。第1次向前迭代5秒,向后迭代5秒;第2次又是向前迭代5秒,向后迭代5秒。 如果容器时间线(包括Storyboard以及Storyboard内每个ParallelTimeline的Timeline)有子Timeline对象,则当容器时间线反向时,它的子Timeline对象也会立即跟着反向。


2016-09-19
(4)BeginTime属性 该属性用于指定时间线开始的时间。如果不指定开始时间,开始时间默认为0。 对于容器内的每条时间线来说,注意其开始时间是相对于父时间线来说的。例如在下面的XAML中,由于父容器Storyboard从第2秒开始,因此第1个动画从第2秒开始播放,第2个动画从第5秒开始播放(2秒+3秒)。 <Storyboard BeginTime="0:0:2"> <DoubleAnimation Storyboard.TargetName="MyRectangle1" Storyboard.TargetProperty="Width" From="0" To="100" Duration="0:0:5"/> <DoubleAnimation Storyboard.TargetName="MyRectangle1"


2016-09-19
Storyboard.TargetProperty="Width" From="0" To="100" Duration="0:0:3" /> <DoubleAnimation Storyboard.TargetName="MyRectangle1" Storyboard.TargetProperty="Width" From="0" To="100" Duration="0:0:5"/> <DoubleAnimation Storyboard.TargetName="MyRectangle2" Storyboard.TargetProperty="Width" From="0" To="100" Duration="0:0:3" BeginTime="0:0:3" /> </Storyboard> 如果将时间线的开始时间设置为null,则会阻止时间线播放。在XAML中可以用x:Null标记扩展来指定Null值。 (5)FillBehavior属性 该属性表示当 Timeline 到达活动期的结尾时,即动画播放结束时,是停止动画播放(FillBehavior="Stop")还是保持动画结束时的值(FillBehavior="HoldEnd"),默认值为


2016-09-19
HoldEnd”。例如将宽度动画从100变化到200,如果FillBehavior="HoldEnd",则该动画结束后宽度仍保持为200;如果FillBehavior="Stop",则动画结束后宽度将立即还原为100。 如果停止某个动画所在的容器时间线,则该动画将立即停止播放,属性的值还原为初始值。注意“动画播放结束”的含义是指播放到其活动期的结尾后其时间线的计时仍在继续,只是看不到动画有变化而已;“停止动画播放”是指停止时间线的计时,即不再进行动画播放。 (6)控制时间线速度的属性 Timeline类提供了3个控制时间线速度的属性。 ① SpeedRatio属性。 该属性指定Timeline相对于其父时间线的时间进度速率。大于1的值表示增加Timeline及其子Timeline对象的速率,0和1之间的值表示减慢其速率。值1表明该Timeline的进度速率与其父时间线相同。 容器时间线的SpeedRatio设置会影响它的所有子


2016-09-19
Timeline对象。 ② AccelerationRatio属性。 该属性指定动画期间时间线相对于 Duration 的加速度,该值必须在 0.0 和 1.0 之间。例如AccelerationRatio="0.4"表示加速度为40%,AccelerationRatio="1.0"表示加速度为100%。加速度的效果类似于开车时踩油门,车速从慢逐渐变快。 ③ DecelerationRatio属性。 该属性指定动画期间时间线相对于 Duration 的减速度,其值必须在 0.0 和 1.0 之间。例如DecelerationRatio="0.6"表示减速度为60%。其效果类似于开车时踩刹车,速度从快逐渐变慢。


2016-09-19
基本动画是一种随着时间变化自动将元素的某个依赖项属性从起始值逐渐过渡到结束值的过程。基本动画是用From/To或者From/By来实现的,每个动画最多只能指定两个值,完成过渡所需的时间由该动画的Duration来确定。 如果一个动画需要指定两个以上的值,应该用关键帧动画来实现,而不是用基本动画来实现。


2016-09-19
基本动画类型 WPF提供的基本动画可控制多种类型,这些动画的命名约定为 <类型>Animation 可用的<类型>有:Byte、Int16、Int32、Int64、Single、Double、Decimal、Color、Point、Size、Thickness、Rect、Vector、Vector3D、Quaternion、Rotation3D。


2016-09-19
在这些动画类型中,常用的有DoubleAnimation、ColorAnimation和PointAnimation。 DoubleAnimation适用于对Rectangle、Button、Label等控件的宽度、高度、不透明度等这些Double类型的附加属性进行动画处理,如滑动效果、拉帘效果、渐入渐出效果等。 ColorAnimation适用于对颜色(前景色、背景色、填充色等)进行渐变动画处理。 PointAnimation适用于对位置进行动画处理。 由于基本动画是用From/To或者From/By来实现的,因此只需要在相邻的两个时间点分别设置控件的属性的起始值和结束值,系统就会自动利用计时器在两个相邻的时间点之间让属性从起始值逐渐变化到结束值。


2016-09-19
用Storyboard实现基本动画 基本动画、关键帧动画、路径动画都可以使用Storyboard来实现。用Storyboard实现动画时,既可以用C#代码来编写,也可以用XAML来描述。 用XAML描述动画时,一般在Window.Resource或者Page.Resource内用样式、控件模板或者数据模板来定义动画。在Window.Triggers或者Page.Triggers中定义事件触发器。 一个XAML文件中可以定义多个Storyboard。 每个Storyboard都必须指明以下内容。 • 在Storyboard中,用x:Key定义一个关键字,以便让事件触发器知道使用的是哪个演示图板。


2016-09-19
• 在动画中,指明该演示图板应用的目标对象名(Storyboard.TargetName)和属性类型(Storyboard.TargetProperty)。 例如: <Window.Resources> <Storyboard x:Key="Storyboard1"> <DoubleAnimation To="150" Duration="0:0:0.2" Storyboard.TargetName="button1" Storyboard.TargetProperty="Width" /> </Storyboard> </Window.Resources> <Window.Triggers> <EventTrigger RoutedEvent="FrameworkElement.Loaded"> <BeginStoryboard Storyboard="{StaticResource Storyboard1}" /> </EventTrigger>


2016-09-19
</Window.Triggers> 用C#代码实现时,需要创建Storyboard的实例,并通过Storyboard提供的SetTarget静态方法指明动画和目标对象,通过SetTargetProperty静态方法指明动画和附加属性的类型。例如: DoubleAnimation da1 = new DoubleAnimation() { To = 100, Duration = new Duration(TimeSpan.FromSeconds(0.2)), AutoReverse = true, RepeatBehavior = RepeatBehavior.Forever }; Storyboard story = new Storyboard(); Storyboard.SetTarget(da1, btn1); Storyboard.SetTargetProperty(da1, new


2016-09-19
PropertyPath(Button.WidthProperty)); story.Children.Add(da1);


2016-09-19
1.淡入淡出 淡入是指让元素逐渐进入视野,淡出是指让元素逐渐从视野中消失。实现这两种效果的办法就是对元素的Opacity属性(不透明度,范围为0.0~1.0)进行动画处理。由于Opacity属性的类型是Double,所以这种动画效果使用DoubleAnimation来实现。


2016-09-19
2.滑动效果 除了淡入淡出效果外,还可以实现滑动效果。滑动效果实际上就是对宽度和高度进行动画处理。由于宽度和高度都是double类型,所以用DoubleAmimation即可实现。


2016-09-19
关键帧动画本质上也是通过修改附加属性的值来实现动画效果的,它和基本动画的区别是:关键帧动画可以在每段动画中同时指定多个关键时间点和关键属性值,而基本动画每段最多只能指定两个值。 关键帧动画是利用演示图板(Storyboard)来实现的。在Storyboard的每条子时间线内,可以定义多个关键时间点,并可以在这些关键时间点处定义属性值的变化。关键时间点和关键属性值共同构成关键帧。 一个XAML文件中可以包含多个Storyboard。


2016-09-19
关键帧动画类型是内插关键帧的容器。在关键帧动画类型的开始标记内,需要用Storyboard.TargetName指定控件的类型,用Storyboard.TargetProperty指定控件的属性。另外,还可以在开始标记内指定该动画的总持续时间(Duration)、如何重复(RepeatBehavior)、是否反向播放(AutoReverse)等。 关键帧动画类型的命名约定为: <类型>AnimationUsingKeyFrames 例如,要进行动画处理的属性为按钮的宽度,由于宽度的类型为 Double,所以<类型>为Double,对应的关键帧动画类型为DoubleAnimationUsingKeyFrames。 在每个关键帧动画类型的开始标记和结束标记之间,都可以使用多种内插关键帧类型,每个内插关键帧都由关键时间和关键值组成。例如: <DoubleAnimationUsingKeyFrames Storyboard.TargetName="Button" Storyboard.TargetProperty="(FrameworkElement.Width)" >


2016-09-19
<DiscreteDoubleKeyFrame Value="400" KeyTime="0:0:4" /> <EasingDoubleKeyFrame Value="500" KeyTime="0:0:3" /> <SplineDoubleKeyFrame KeySpline="0.6,0.0 0.9,0.00" Value="0" KeyTime="0:0:6"/> </DoubleAnimationUsingKeyFrames> 这段XAML中的KeyTime表示关键时间,即从前一个关键帧到该关键帧经过的时间;Value表示关键值,即当前关键时间处的目标动画值。DiscreteDoubleKeyFrame、EasingDoubleKeyFrame和SplineDoubleKeyFrame都是内插关键帧类型。 内插关键帧类型是利用内插方法来实现的,内插关键帧类型的命名约定为: <内插方法><类型>KeyFrame 例如,DiscreteDoubleKeyFrame表示该内插关键帧的<内插方法>为离散内插,关键帧<类型>为Double;EasingDoubleKeyFrame表示<内插方法>为线性内插;SplineDou


2016-09-19
bleKeyFrame表示<内插方法>为样条内插。


2016-09-19
这三种内插方法的含义如下。 • 离散(Discrete):关键帧从上一个关键值突然变化到当前值,而不是平滑变化。例如,两个相邻关键帧的值分别为100和200,持续期间为5秒,则在5秒之前一直都是100,到第5秒时突然变为200。例如,闪烁效果(用Boolean类型的动画实现)就属于离散内插。 • 线性(Linear或Easing):指在相邻关键值之间创建平滑的过渡,一般使用Easing实现线性内插。Easing 的含义是它不但能对属性值进行平滑的动画处理,而且还可以使用缓动函数(Easing Function)实现特殊的效果。 • 样条(Spline):通过三次贝塞尔曲线的两个控制点控


2016-09-19
制过渡方式。 除了这些内插方法外,还可以用自定义函数实现动画效果。


2016-09-19
Blend for VS2012是微软研制的一个专业设计工具,其主要用途是以可视化的时间线快速制作XAML动画以及快速创建应用程序原型。将WPF设计器和Blend结合使用,能让开发人员和设计人员(美工)协同开发同一个WPF应用程序或者Silverlight应用程序项目中的动画。换言之,既可以用Blend打开VS2012创建的WPF应用程序项目或者Silverlight应用程序项目,也可以用VS2012直接打开Blend修改后的项目。


2016-09-19
路径动画是指将元素按照某一指定的几何路径(PathGeometry对象)进行移动而形成的动画,也可以边移动边旋转,从而实现复杂的变换效果。


2016-09-19
路径动画类型 WPF提供的路径动画类型有PointAnimationUsingPath、DoubleAnimationUsingPath和Matrix-AnimationUsingPath。 PointAnimationUsingPath和DoubleAnimationUsingPath都是在指定的时间段内,用PathGeometry作为动画的目标值,采用线性内插方式对两个或更多的目标值之间的属性值进行动画处理。这两种动画类型特别适用于在屏幕上对对象的位置变更进行动画处理。 MatrixAnimationUsingPath根据传入的PathGeometry生成Matrix对象。将其与MatrixTransform一起使用时会自动沿路径移动对象。如果将该动画的DoesRotateWithTangent属性设置为true,它还可以沿路径的曲线旋转对象。


2016-09-19
在Blend for VS2012中,开发人员只需要通过鼠标拖动和简单操作,即可以让其自动生成对应的路径动画。 1.在Blend中绘制形状和路径 利用Blend提供的标准矢量绘图功能,可直接绘制形


2016-09-19
状、路径和蒙版,并对其进行移动、旋转、翻转、倾斜或改变大小,也可以使用属性窗口输入精确值。


2016-09-19
Blend提供的形状如图10-10所示。选择其中的一个形状,然后在美工版上绘制即可。   图10-10 利用Shapes对象绘制形状或路径  按住<Shift>键的同时进行拖动,可使高度和宽度保持相同。按住<Alt>键的同时进行拖动,可将单击的第一个点作为中心点,而不是以该点作为所绘形状的左上角。释放鼠标按钮后,便会出现用于缩放、旋转、移动、倾斜以及可对形状对象执行的其他操作的转换图柄。 绘制路径时,既可以使用【铅笔】工具直接绘制(生成像素),也可以使用【钢笔】工具绘制路径(生成点或顶点)。用钢笔绘制时,还可以在每个点上拖动来生成曲线,若要绘制封闭路径,绘制结束后再单击第一个点即可。 路径绘制完毕后,便会出现用于缩放、旋转、移动、倾


2016-09-19
斜以及可对路径对象执行的其他操作的转换图柄。此时指针会指明利用“选择”工具能执行的操作。 要删除路径的一部分,可先用【路径选择】工具选择要删除的部分,然后按<Delete>键删除。 要重定义路径上点的控制柄,可先用【路径选择】工具选择要修改的点(或顶点)的路径,然后按住<Alt>键,并在光标变为某个光标之一时单击节点,再从节点拖离即可。


2016-09-19
在Blend for VS2012中将形状和文本转换为路径 在Blend for VS2012中将形状转换为路径的办法为:先单击【选择】工具,然后选择要转换为路径的形状,再右键单击该形状,在快捷菜单中选择【转换为路径】命令即可。注意将形状转换为路径后,将无法修改形状所特有的属性(如矩形的圆角半径)。而且,如果在转换之前向形状应用了样式,则已转换路径的属性将重置为路径的默认值(无填充画笔,黑色轮廓)。 仅当文本包含在TextBlock或RichTextBox中时,才能将文本转换为路径。将文本转换为路径的步骤为:先选择TextBlock或RichTextBox对象,然后单击【对象】菜单,指向【路径】,再单击【转换为路径】。将文本转换为路径


2016-09-19
后,整个文本块会变成一条路径,并用路径的矢量点定义原始文本中每个字符的形状。 也可以将产生的单一路径释放为多条路径,即将文本的每个字符变成一条路径,并且字符中的每个封闭循环也变成一条路径。办法为:先单击【对象】菜单,指向【路径】,然后单击【释放复合路径】按钮。 若要重新组合任意路径,例如用封闭循环构成字符的路径,先选择这些路径(按住<Ctrl>键可选择多个项),然后在【对象】菜单的【路径】下,单击【创建复合路径】按钮。


2016-09-19
WPF 应用程序提供了对语音(Speech)、音频(Audio)和视频(Video)的集成支持类,使用WPF提供的这些类,可非常容易地实现语音、音频和视频播放。


2016-09-19
Microsoft.NET框架在System.Speech.Synthesis.SpeechSynthesizer类中提供了对已安装的语音合成引擎功能的访问,这一功能在需要通过文字发音的项目中非常有用。


2016-09-19
在WPF中,播放音频或视频最简单的方法就是用Me


2016-09-19
iaElement控件来实现,该对象可播放多种类型的音频文件和视频文件,而且还能控制媒体的播放、暂停、停止以及音量和播放速度等。 WPF支持的音频文件类型有AIF、AIFC、AIFF、ASF、AU、MID、MIDI、MP2、MP3、MPA、MPE、RMI、SND、WAV、WMA和WMD等。这些文件类型都是Windows Media Player 10支持的文件格式。注意Silverlight只支持MP3和WMA文件类型。 WPF支持的视频文件类型有ASF、AVI、DVR-MS、IFO、M1V、MPEG、MPG、VOB、WM和WMV等。这些文件类型都是Windows Media Player 10支持的文件格式。注意Silverlight只支持WMV文件类型。 若要在WPF中使用音频或视频,必须在计算机上安装Windows Media Player 10 或更高版本。 使用MediaElement播放音频或视频时,有两点需要注意。 如果将媒体与应用程序一起分发,则不能将媒体文件用作项目资源,必须将其作为内容文件来处理。或者说,将被


2016-09-19
播放的媒体文件导入到项目中时,必须将文件的“类型”属性改为“内容”,而且必须将“复制到输出目录”属性设置为“如果较新则复制”或者“总是复制”,然后才能正常播放。 在MediaElement中设置媒体文件路径时,由于被播放的文件是内容文件,所以该路径是指相对于当前生成的可执行文件(调试环境下为 bin\Debug 下的.exe 文件)的相对路径(也可以将其理解为相对于项目根目录的相对路径),而不是相对于当前页面的相对路径。这一点与引用被编译的其他文件的相对路径不同,使用时要特别注意。换言之,引用内容文件时,不论内容文件是图像还是音频、视频,代码中的相对路径都是指相对于当前可执行文件的路径来说的。 MediaElement有两种使用模式:独立模式和时钟模式。在独立模式下,MediaElement的用法和Image控件的用法相似,可直接指定其Source URI。另外,设置该控件的大小时,要么仅设置宽度,要么仅设置高度,此时将 Stretch 属性设置为“Fill”可在填充时自动保持其宽高比;如果同时设置宽度和高度又希望填充时还要保持宽高比,可将Stretch属性设置为“UniformToFill”。MediaElement的常用属性和事件如下。


2016-09-19
• LoadedBehavior 属性:设置加载 MediaElement 并完成预播放后的行为(Play、Pause、Manual、Stop、Close)。默认为Play,表示加载完成后立即播放;Pause表示加载后停在第1帧;Manual 表示通过代码控制。如果希望用按钮事件控制播放、暂停等行为,需要将该属性设置为“Manual”。 • UnloadedBehavior属性:设置卸载MediaElement后的行为,默认为Close,即播放完后立即关闭媒体并释放所有媒体资源。 • MediaOpened事件:加载媒体文件后引发,利用该事件可获取视频文件的宽度和高度。对于音频文件,其宽度和高度始终为零。


2016-09-20
对于如ListBox、ListView或TreeView等从ItemElement继承的项来说,可使用Binding类的ItemsSource属性将其绑定到集合视图。 ItemsSource属性默认使用OneWay绑定模式。 1.绑定到默认集合视图 集合视图是实现ICollectionView接口的类,它位于绑定源集合顶部的层,通过它使用排序、筛选和分组查询来导航和显示源集合时,不需要更改基础源集合本身。另外,集合视图还维护着一个指向集合中的当前项的指针。当用集合视图实现数据绑定时,如果源集合实现了INotifyCollection


2016-09-20

Changed接口,则CollectionChanged事件引发的更改将自动传播到视图。 由于视图不会更改基础源集合,因此每个源集合可以有多个关联的视图。换言之,视图的用处是可利用它通过多种不同的方式来显示相同的数据。如在页面左侧显示按学号排序的任务,而在页面右侧显示按性别分组的任务。 直接将目标绑定到集合时,如果不指定自定义的视图,WPF会自动为集合创建一个默认的集合视图,并绑定到此默认视图。在演示数据模板化基本用法的例子中,实际上使用的就是默认集合视图。







  • 2
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

赤龙绕月

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值