# WPF快速入门系列(4)——深入解析WPF绑定
## 一、引言
WPF绑定使得原本需要多行代码实现的功能,现在只需要简单的XAML代码就可以完成之前多行后台代码实现的功能。WPF绑定可以理解为一种关系,该关系告诉WPF从一个源对象提取一些信息,并将这些信息来设置目标对象的属性。目标属性总是依赖属性。然而,源对象可以是任何内容,可以是一个WPF元素、或ADO.NET数据对象或自定义的数据对象等。下面详细介绍了WPF绑定中的相关知识点。
## 二、绑定元素对象
## 2.1 如何实现绑定元素对象
这里首先介绍绑定最简单的情况——绑定元素对象,即数据源是一个WPF元素对象并且源属性是依赖属性。由于依赖属性具有内置的更改通知支持,因此,当在源对象中改变依赖属性的值时,会立即更新目标对象中的绑定属性。下面通过一个简单的例子来演示下如何绑定元素对象。具体的XAML代码(这里不需要后台代码)如下所示:
在上面XAML代码中,TextBlock控件的FontSize属性绑定了Slider控件的Value属性,感觉说绑定有点拗口,你可以直接理解为TextBlock的FontSize属性的值来自与Slider控件的Value值,由于源属性Value是依赖属性,具体内置的更改通知功能,所以Slider控件Value值的改变,直接影响TextBlock控件FontSize的值。正如我们分析的那样,实际运行结果也是如此,运行结果如下图所示:
![](https://box.kancloud.cn/2016-01-23_56a2eb4250fbc.png)
当移动上图中Slider控件上的游标时,下面的文本字体大小也会跟着一起改变。具体效果这里就不贴图了,大家可以自行尝试。从中可以看到WPF绑定的强大了吧,如果放到以前WinForm开发中,你需要监听Slider的ValueChanged事件,然后在事件处理程序中去动态改变文本的字体大小。
这里Path除了可以直接绑定属性之外,还可以绑定属性的属性,如FontFamily.Source,也可以指向属性使用的索引器,如Content.Children[0]。当然你也可以执行多层次的路径,如指向属性的属性的属性等。
另外,如果绑定失败时,WPF不会引发异常来告知绑定失败的原因。例如,指定的元素或属性不存在,此时不会收到任何提示,只是在目标属性不能显示数据罢了。然而在调试模式下,你可以在输出窗口来查看绑定失败的信息,例如,在上面XAML代码,我们绑定Slider控件一个不存在的属性,如Text属性,此时在Output窗口中就会看到如下信息:
![](https://box.kancloud.cn/2016-01-23_56a2eb4263332.png)
## 2.2 绑定模式
绑定的一个最大的特点就是源属性改变时,目标属性会自动地更新。然而上面的示例有一个问题,即目标对象的改变不会自动更新源对象的属性。通过下面的例子可以看出这个问题所在。此时XAML代码修改为:
```
<Window x:Class="WPFBindingDemo.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="400">
<StackPanel>
<Slider Name="sliderFontSize" Margin="3"
Minimum="1" Maximum="40" Value="10" TickFrequency="1" TickPlacement="TopLeft"/>
<TextBlock Margin="10" Text="LearningHard" Name="lbtext"
FontSize="{Binding ElementName=sliderFontSize, Path=Value}"></TextBlock>
<!--在按钮的Click事件处理程序中去改变目标对象的FontSize的值-->
<StackPanel Orientation="Horizontal">
<Button Margin="10" Padding="5" Click="cmd_SetSmall">Set to Small</Button>
<Button Margin="10" Padding="5" Click="cmd_SetNormal">Set to Normal</Button>
<Button Margin="10" Padding="5" Click="cmd_SetLarge">Set to Large</Button>
</StackPanel>
</StackPanel>
</Window>
```
此时后台C#代码如下所示:
```
private void cmd_SetSmall(object sender, RoutedEventArgs e)
{
// 仅仅在双向模式下工作
lbtext.FontSize = 5;
}
private void cmd_SetNormal(object sender, RoutedEventArgs e)
{
sliderFontSize.Value = 20;
}
private void cmd_SetLarge(object sender, RoutedEventArgs e)
{
// 仅仅在双向模式下工作
lbtext.FontSize = 40;
}
```
具体的运行效果如下图所示:
![](https://box.kancloud.cn/2016-01-23_56a2eb42814d8.gif)
从上图可以看到,当在后台更改TextBlock的FontSize属性值,而Slider的Value值却没有进行更新。此时,你肯定会想问,能不能实现目标属性的更变也会自动改变绑定中源属性的机制呢?因为这样就不会显得那样呆板了,然而,你想到的了WPF团队肯定也想到了,WPF支持双向绑定,即从源到目标以及目标到源,要支持双向绑定,只需要设置Binding对象的[Mode](http://msdn.microsoft.com/zh-cn/library/system.windows.data.binding.mode(v=vs.110).aspx)属性为TwoWay即可,修改后的XAML代码为:
```
<StackPanel>
<Slider Name="sliderFontSize" Margin="3"
Minimum="1" Maximum="40" Value="10" TickFrequency="1" TickPlacement="TopLeft"/>
<TextBlock Margin="10" Text="LearningHard" Name="lbtext"
FontSize="{Binding ElementName=sliderFontSize, Path=Value, **Mode=TwoWay**}"></TextBlock>
<!--在按钮的Click事件处理程序中去改变目标对象的FontSize的值-->
<StackPanel Orientation="Horizontal">
<Button Margin="10" Padding="5" Click="cmd_SetSmall">Set to Small</Button>
<Button Margin="10" Padding="5" Click="cmd_SetNormal">Set to Normal</Button>
<Button Margin="10" Padding="5" Click="cmd_SetLarge">Set to Large</Button>
</StackPanel>
</StackPanel>
```
Mode属性除了可以设置OneWay,TwoWay值外,还可以设置Default、OneTime和OneWayToSource,关于这些值更详细的介绍请自行参考MSDN:[http://msdn.microsoft.com/zh-cn/library/system.windows.data.bindingmode(v=vs.110).aspx](http://msdn.microsoft.com/zh-cn/library/system.windows.data.bindingmode(v=vs.110).aspx)。
另外,除了可以在XAML中通过Binding标记地方式声明绑定外,还可以使用代码方式动态创建绑定。如上面的例子中代码创建绑定的实现代码如下所示:
还可以通过使用[BindingOperations](http://msdn.microsoft.com/zh-cn/library/system.windows.data.bindingoperations(v=vs.110).aspx)类的[ClearBinding](http://msdn.microsoft.com/zh-cn/library/system.windows.data.bindingoperations.clearbinding(v=vs.110).aspx)方法来移除数据绑定。还可以使用[ClearAllBindings](http://msdn.microsoft.com/zh-cn/library/system.windows.data.bindingoperations.clearallbindings(v=vs.110).aspx)移除一个元素的所有数据绑定。
## 2.3 绑定更新
在上面的例子中,还存在另一个问题,当通过在文本框中输入内容来改变显示的字体尺寸时,此时什么事情都不会发生,知道使用tab键将焦点转移到另外一个控件时,才会应用对应的改变。此时XAML代码如下所示:
```
<Window x:Class="WPFBindingDemo.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="400">
<StackPanel>
<Slider Name="sliderFontSize" Margin="3"
Minimum="1" Maximum="40" Value="10" TickFrequency="1" TickPlacement="TopLeft"/>
<TextBlock Margin="10" Text="LearningHard" Name="lbtext"
FontSize="{Binding ElementName=sliderFontSize, Path=Value, Mode=TwoWay}"></TextBlock>
<!--在按钮的Click事件处理程序中去改变目标对象的FontSize的值-->
<StackPanel Orientation="Horizontal">
<Button Margin="10" Padding="5" Click="cmd_SetSmall">Set to Small</Button>
<Button Margin="10" Padding="5" Click="cmd_SetNormal">Set to Normal</Button>
<Button Margin="10" Padding="5" Click="cmd_SetLarge">Set to Large</Button>
</StackPanel>
<!--添加一个输入文本框来设置文本字体大小进行测试问题-->
<StackPanel Orientation="Horizontal" Margin="5">
<TextBlock VerticalAlignment="Center">Set FontSize:</TextBlock>
<TextBox Text="{Binding ElementName=lbtext, Path=FontSize, Mode=TwoWay}" Width="100"/>
</StackPanel>
</StackPanel>
</Window>
```
后台代码实现与前面的一样,此时运行的效果如下图所示:
![](https://box.kancloud.cn/2016-01-23_56a2eb4297e17.gif)
为了明白导致这个问题的原因,这里需要深入分析下绑定表达式。当使用OneWay或TwoWay绑定时,改变后的值会立即从源传播到目标。对于滑动条,然而,从目标到源传播未必会立即发生。因为,它们的行为是由[Binding.UpdateSourceTrigger](http://msdn.microsoft.com/zh-cn/library/system.windows.data.binding.updatesourcetrigger(v=vs.110).aspx)属性控制,该属性可以使用下图列出的某个值。**注意,UpdateSourceTrigger属性值并不影响目标的更新方式,它仅仅控制TwoWay模式或OneWayToSource模式的绑定更新源的方式。**而文本框正是使用LostFocus方式从目标向源进行更新的。
![](https://box.kancloud.cn/2016-01-23_56a2eb42a9c68.png)
既然,找出了导致原因,此时可以对XAML代码进行修改,使得当用于在文本框中输入内容时将变化应用于字体尺寸,具体改变部分的XAML代码为:
另外,需要注意的是,TextBox的Text属性的默认行为是LostFocus,这是因为当用于输入内容时,文本框中文本会不断变化,从而引起多次更新。所以PropertyChanged模式可能会使应用程序运行更缓慢,所以LostFocus默认行为可以说是合理的。
要完全控制源对象的更新时机,也可以选择UpdateSourceTrigger.Explicit模式。此时就需要额外编写代码手动触发更新,此时可以添加一个Apply按钮,并在按钮的Click事件处理程序中调用[BindingExpression.UpdateSource](http://msdn.microsoft.com/zh-cn/library/system.windows.data.updatesourcetrigger(v=vs.110).aspx?query=UpdateSource)方法触发立即刷新并更新字体大小的操作。具体的实现代码如下所示:
## 三、绑定非元素对象
上面都是介绍如何链接两个元素的绑定,但是在数据驱动的应用程序中,更常见的情况是创建从一个对象中提起数据的绑定表达式。不过希望绑定的信息必须存储在一个公有属性中。因为WPF绑定不能获取私有信息或公有字段。
当绑定一个非元素对象时,不能使用Binding.ElementName属性,但可以使用以下属性中的一个:
* [Source](http://msdn.microsoft.com/zh-cn/library/system.windows.data.binding.source(v=vs.110).aspx)——该属性是指向源对象的引用,即提供数据的对象。
* [RelativeSource](http://msdn.microsoft.com/zh-cn/library/system.windows.data.binding.relativesource(v=vs.110).aspx)——该属性使用RelativeSource对象指定绑定源的相对位置,默认值为null。
* DataContext属性——如果没有使用Source或RelativeSource属性指定一个数据源,WPF会从当前元素开始在元素树中向上查找。检查每个元素的DataContext属性,并使用第一个非空的DataContext属性。当然你也可以自己设置DataContext属性。
下面通过一个例子来演示下如何绑定到非元素对象。下面的演示如何使用DataContext属性来绑定一个自定义对象的属性。首先自定义一个实现了[INotifyPropertyChanged](http://msdn.microsoft.com/zh-cn/library/system.componentmodel.inotifypropertychanged(v=vs.110).aspx)接口的类。这个接口是为了发出属性更改的通知,即实现了这个接口将会实现当源对象的公共属性发生改变时,该属性的值会立即响应到界面上显式。当然不实现这个接口的对象也可以绑定控件中,只要被绑定是公有属性就可以。具体的实现代码如下所示:
```
1 using System.ComponentModel;
2
3 namespace WPFBindingDemo
4 {
5 public class Student:INotifyPropertyChanged
6 {
7 private int m_ID;
8 private string m_StudentName;
9 private double m_Score;
10
11 public int ID
12 {
13 get { return m_ID; }
14 set
15 {
16 if (value != m_ID)
17 {
18 m_ID = value;
19 Notify("ID");
20 }
21 }
22 }
23
24 public string StudentName
25 {
26 get { return m_StudentName; }
27 set
28 {
29 if (value != m_StudentName)
30 {
31 m_StudentName = value;
32 Notify("StudentName");
33 }
34 }
35 }
36
37 public double Score
38 {
39 get { return m_Score; }
40 set
41 {
42 if (value != m_Score)
43 {
44 m_Score = value;
45 Notify("Score");
46 }
47 }
48 }
49
50 public event PropertyChangedEventHandler PropertyChanged;
51 private void Notify(string propertyName)
52 {
53 if (PropertyChanged != null)
54 {
55 this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
56 }
57 }
58 }
59 }
```
既然源数据对象以准备好了,自然接下来就是去设计WPF界面来让控件来绑定这个源对象了,具体的XAML代码如下所示:
```
<Window x:Class="WPFBindingDemo.BindingToCollection"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="BindingToCollection" Height="300" Width="300">
<StackPanel Margin="50">
<StackPanel Orientation="Horizontal" Margin="10">
<TextBlock Text="学号:" />
<TextBlock Text="{Binding Path=ID}" Width="100"/>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="10">
<TextBlock Text="姓名:" />
<TextBlock Text="{Binding Path=StudentName}" Width="100"/>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="10">
<TextBlock Text="分数:" />
<TextBlock Text="{Binding Path=Score}" Width="100"/>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="10">
<Button Content="改变姓名" Name="changeName" Click="changeName_Click_1"/>
<Button Content="改变分数" Name="changeScore" Margin="20,0,0,0" Click="ChangeScore_Click"/>
</StackPanel>
</StackPanel>
</Window>
```
对应的后台代码逻辑如下所示:
```
public partial class BindingToCollection : Window
{
private Student m_student;
public BindingToCollection()
{
InitializeComponent();
m_student = new Student() { ID = 1, StudentName = "LearningHard", Score = 60 };
// 设置Window对象的DataContext属性
this.DataContext = m_student ;
}
private void ChangeScore_Click(object sender, RoutedEventArgs e)
{
m_student.Score = 90;
}
private void changeName_Click_1(object sender, RoutedEventArgs e)
{
m_student.StudentName = "Learning";
}
}
```
完成了示例所有代码的编写之后,下面具体看看示例的运行效果,看看是否可以成功完成绑定并源对象的属性的更改会立即反应到界面中,具体的效果图如下所示:
![](https://box.kancloud.cn/2016-01-23_56a2eb42bbaa3.gif)
从上图示例的演示动画效果可以看出,上面的代码确实实现我们预期的功能。从上面代码可以看出,我们并没有对每个控件单独设置它的Source属性,而是直接设置了Window对象的DataContext属性。这样绑定的控件发现没有设置source属性或RelativeSource属性,就会从元素树中查找DataContex属性不为null的值来作为自己的DataContext。通过这样的方式可以省去重复在多个控件中设置相同的DataContext属性。
这里只是演示了绑定单个数据对象的情况,就如之前所说的,数据源还可以是XAML文件,ADO.NET数据对象、集合等,这里就不一一实现了,只要了解具体思路,具体问题具体搜索解决就好了。这里给出两个非常的好例子。
[Simple Demo of Binding to a Database in WPF using LINQ-SQL](http://www.codeproject.com/Articles/24192/Simple-Demo-of-Binding-to-a-Database-in-WPF-using)
[How to Perform WPF Data Binding Using LINQ to XML](http://www.codeproject.com/Articles/37853/How-to-Perform-WPF-Data-Binding-Using-LINQ-to-XML)
## 四、提高大列表的性能
如果绑定的数据源具有大量记录时,此时就需要考虑性能的问题了。然而,幸运的是,WPF很多列表控件都已经帮我们做好了相应的支持,这里只是提出来让大家知道这点。
对于大列表显示性能问题,WPF做了以下几种支持:
* UI虚拟化——UI虚拟化是列表仅为当前显示项创建容器对象的一种技术,例如,如果有一个具有5万条记录的列表,但是可见区域只能包含30条记录,ListBox控件只创建30个ListBoxItem对象。如果ListBox控件不支持UI虚拟化的话,它将需要生成全部5万个ListBoxItem对象,这显然需要占用更多的内存。并且分配这些对象的时间用户明显可以感觉到,这就带来了非常不好的用户体验。WPF中UI虚拟化是通过VirtualizingStackPanel容器实现的。像ListBox、ListView和DataGrid都自动使用VirtualizingStackPanel面板布局它们的子元素,所以,这些控件都默认支持虚拟化功能。然而,ComboBox需要支持虚拟化支持,必须明确提供新的ItemPanelTemplate添加虚拟化支持,具体实现如下所示:
```
<ComboBox>
<ComboBox.ItemsPanel>
<ItemsPanelTemplate>
** <VirtualizingStackPanel></VirtualizingStackPanel>**
</ItemsPanelTemplate>
</ComboBox.ItemsPanel>
</ComboBox>
```
TreeView控件也支持虚拟化,但它在默认情况下,关闭了该支持,你需要显式启用该特性,具体使用的启用代码如下所示:
* 项目容器再循环——WPF 3.5 SP1使用项目容器再循环改进了虚拟化。通常支持虚拟化的列表在滚动时,控件不断地创建新的项目容器对象来保存新的可见项。例如,当具有5万个项的ListBox控件,在滚动时,ListBox需要重新生成新的ListBoxItem对象。但是如果启用了项目容器再循环,ListBox控件会保存少量ListBoxItem对象存活,当滚动时,将新数据加到这些之前的ListBoxItem对象,从而重复使用它们。具体支持代码如下所示
项目容器再循环提供了滚动性能,并降低了内存消耗量,因为垃圾回收器不需要查找旧对象进行回收。为了确保向后兼容,除了DataGrid之后的所有列表控件默认都禁用该特性,如需支持,需要显式启用。
* 延迟滚动——为了进一步提供滚动性能,可以开启延迟滚动功能。使用延迟滚动,当用户在滚动条上拖动滚动滑块时不更新列表显示,只有用户释放了滚动滑块时才刷新。当使用常规滚动时,在拖动的同时会刷新列表,使列表显示正在改变的位置。这个特性也需要显式启用,启用代码如下:
显然,需要在响应性和易用性之间平衡。如果有一个复杂的模板和大量数据,对于提高速度可能会选择使用延迟滚动特性,但当用户需要在滚动时查看目前滚动位置,则就可以不启用该特性。
上面介绍了这么多,其实提供列表控件的性能主要在两方面:UI虚拟化提高了列表项初始化的时间,因为UI虚拟化支持一次性不初始化所有项,而在滚动是自动创建新的项。项目容器再循环和延迟滚动提高了滚动性能。
另外WPF绑定还有两个知识点:数据验证和数据转换,对于数据验证与Asp.net中验证类似,都是为了保证输入数据的合法性,而数据转换指的是在源数据绑定到目标依赖属性之前要做对应的转换,例如WPF显示人民币都需要显示一个¥符号,但是如果数据源的内容只是“120”这样的字符串怎么办呢?这时候就可以通过数据转换在绑定之前,把数据源的值转换成显示所需要的格式。对于这两个知识点,我觉得在遇到问题时再去学就好了,因为我们已经明白了解决问题的思路了。所以,在快速入门系列中不想太深入的介绍这两个知识点,以使大家可以快速掌握WPF要领。这里给出几个学习链接:
[数据绑定概述](http://msdn.microsoft.com/zh-cn/library/ms752347(v=vs.110).aspx) [WPF Data Binding - Part 1](http://www.codeproject.com/Articles/29054/WPF-Data-Binding-Part)
[WPF Simple Data Converter Example](http://www.codeproject.com/Tips/662209/WPF-Simple-Data-Converter-Example)
[如何:实现绑定验证](http://msdn.microsoft.com/zh-cn/library/ms753962(v=vs.110).aspx)
## 五、小结
到这里,这篇博文的内容就介绍结束了,时间不知不觉的已经2点多了。下面一篇博文将分享WPF命令的内容。
本文所有源码下载:[WPFBindingDemo.zip](http://files.cnblogs.com/zhili/WPFBindingDemo.zip)
- C# 基础知识系列
- C# 基础知识系列 专题一:深入解析委托——C#中为什么要引入委托
- C# 基础知识系列 专题二:委托的本质论
- C# 基础知识系列 专题三:如何用委托包装多个方法——委托链
- C# 基础知识系列 专题四:事件揭秘
- C# 基础知识系列 专题五:当点击按钮时触发Click事件背后发生的事情
- C# 基础知识系列 专题六:泛型基础篇——为什么引入泛型
- C# 基础知识系列 专题七: 泛型深入理解(一)
- C# 基础知识系列 专题八: 深入理解泛型(二)
- C# 基础知识系列 专题九: 深入理解泛型可变性
- C#基础知识系列 专题十:全面解析可空类型
- C# 基础知识系列 专题十一:匿名方法解析
- C#基础知识系列 专题十二:迭代器
- C#基础知识 专题十三:全面解析对象集合初始化器、匿名类型和隐式类型
- C# 基础知识系列 专题十四:深入理解Lambda表达式
- C# 基础知识系列 专题十五:全面解析扩展方法
- C# 基础知识系列 专题十六:Linq介绍
- C#基础知识系列 专题十七:深入理解动态类型
- 你必须知道的异步编程 C# 5.0 新特性——Async和Await使异步编程更简单
- 全面解析C#中参数传递
- C#基础知识系列 全面解析C#中静态与非静态
- C# 基础知识系列 C#中易混淆的知识点
- C#进阶系列
- C#进阶系列 专题一:深入解析深拷贝和浅拷贝
- C#进阶系列 专题二:你知道Dictionary查找速度为什么快吗?
- C# 开发技巧系列
- C# 开发技巧系列 使用C#操作Word和Excel程序
- C# 开发技巧系列 使用C#操作幻灯片
- C# 开发技巧系列 如何动态设置屏幕分辨率
- C# 开发技巧系列 C#如何实现图片查看器
- C# 开发技巧 如何防止程序多次运行
- C# 开发技巧 实现属于自己的截图工具
- C# 开发技巧 如何使不符合要求的元素等于离它最近的一个元素
- C# 线程处理系列
- C# 线程处理系列 专题一:线程基础
- C# 线程处理系列 专题二:线程池中的工作者线程
- C# 线程处理系列 专题三:线程池中的I/O线程
- C# 线程处理系列 专题四:线程同步
- C# 线程处理系列 专题五:线程同步——事件构造
- C# 线程处理系列 专题六:线程同步——信号量和互斥体
- C# 多线程处理系列专题七——对多线程的补充
- C#网络编程系列
- C# 网络编程系列 专题一:网络协议简介
- C# 网络编程系列 专题二:HTTP协议详解
- C# 网络编程系列 专题三:自定义Web服务器
- C# 网络编程系列 专题四:自定义Web浏览器
- C# 网络编程系列 专题五:TCP编程
- C# 网络编程系列 专题六:UDP编程
- C# 网络编程系列 专题七:UDP编程补充——UDP广播程序的实现
- C# 网络编程系列 专题八:P2P编程
- C# 网络编程系列 专题九:实现类似QQ的即时通信程序
- C# 网络编程系列 专题十:实现简单的邮件收发器
- C# 网络编程系列 专题十一:实现一个基于FTP协议的程序——文件上传下载器
- C# 网络编程系列 专题十二:实现一个简单的FTP服务器
- C# 互操作性入门系列
- C# 互操作性入门系列(一):C#中互操作性介绍
- C# 互操作性入门系列(二):使用平台调用调用Win32 函数
- C# 互操作性入门系列(三):平台调用中的数据封送处理
- C# 互操作性入门系列(四):在C# 中调用COM组件
- CLR
- 谈谈: String 和StringBuilder区别和选择
- 谈谈:程序集加载和反射
- 利用反射获得委托和事件以及创建委托实例和添加事件处理程序
- 谈谈:.Net中的序列化和反序列化
- C#设计模式
- UML类图符号 各种关系说明以及举例
- C#设计模式(1)——单例模式
- C#设计模式(2)——简单工厂模式
- C#设计模式(3)——工厂方法模式
- C#设计模式(4)——抽象工厂模式
- C#设计模式(5)——建造者模式(Builder Pattern)
- C#设计模式(6)——原型模式(Prototype Pattern)
- C#设计模式(7)——适配器模式(Adapter Pattern)
- C#设计模式(8)——桥接模式(Bridge Pattern)
- C#设计模式(9)——装饰者模式(Decorator Pattern)
- C#设计模式(10)——组合模式(Composite Pattern)
- C#设计模式(11)——外观模式(Facade Pattern)
- C#设计模式(12)——享元模式(Flyweight Pattern)
- C#设计模式(13)——代理模式(Proxy Pattern)
- C#设计模式(14)——模板方法模式(Template Method)
- C#设计模式(15)——命令模式(Command Pattern)
- C#设计模式(16)——迭代器模式(Iterator Pattern)
- C#设计模式(17)——观察者模式(Observer Pattern)
- C#设计模式(18)——中介者模式(Mediator Pattern)
- C#设计模式(19)——状态者模式(State Pattern)
- C#设计模式(20)——策略者模式(Stragety Pattern)
- C#设计模式(21)——责任链模式
- C#设计模式(22)——访问者模式(Vistor Pattern)
- C#设计模式(23)——备忘录模式(Memento Pattern)
- C#设计模式总结
- WPF快速入门系列
- WPF快速入门系列(1)——WPF布局概览
- WPF快速入门系列(2)——深入解析依赖属性
- WPF快速入门系列(3)——深入解析WPF事件机制
- WPF快速入门系列(4)——深入解析WPF绑定
- WPF快速入门系列(5)——深入解析WPF命令
- WPF快速入门系列(6)——WPF资源和样式
- WPF快速入门系列(7)——深入解析WPF模板
- WPF快速入门系列(8)——MVVM快速入门
- WPF快速入门系列(9)——WPF任务管理工具实现
- ASP.NET 开发
- ASP.NET 开发必备知识点(1):如何让Asp.net网站运行在自定义的Web服务器上
- ASP.NET 开发必备知识点(2):那些年追过的ASP.NET权限管理
- ASP.NET中实现回调
- 跟我一起学WCF
- 跟我一起学WCF(1)——MSMQ消息队列
- 跟我一起学WCF(2)——利用.NET Remoting技术开发分布式应用
- 跟我一起学WCF(3)——利用Web Services开发分布式应用
- 跟我一起学WCF(3)——利用Web Services开发分布式应用
- 跟我一起学WCF(4)——第一个WCF程序
- 跟我一起学WCF(5)——深入解析服务契约 上篇
- 跟我一起学WCF(6)——深入解析服务契约 下篇
- 跟我一起学WCF(7)——WCF数据契约与序列化详解
- 跟我一起学WCF(8)——WCF中Session、实例管理详解
- 跟我一起学WCF(9)——WCF回调操作的实现
- 跟我一起学WCF(10)——WCF中事务处理
- 跟我一起学WCF(11)——WCF中队列服务详解
- 跟我一起学WCF(12)——WCF中Rest服务入门
- 跟我一起学WCF(13)——WCF系列总结
- .NET领域驱动设计实战系列
- .NET领域驱动设计实战系列 专题一:前期准备之EF CodeFirst
- .NET领域驱动设计实战系列 专题二:结合领域驱动设计的面向服务架构来搭建网上书店
- .NET领域驱动设计实战系列 专题三:前期准备之规约模式(Specification Pattern)
- .NET领域驱动设计实战系列 专题四:前期准备之工作单元模式(Unit Of Work)
- .NET领域驱动设计实战系列 专题五:网上书店规约模式、工作单元模式的引入以及购物车的实现
- .NET领域驱动设计实战系列 专题六:DDD实践案例:网上书店订单功能的实现
- .NET领域驱动设计实战系列 专题七:DDD实践案例:引入事件驱动与中间件机制来实现后台管理功能
- .NET领域驱动设计实战系列 专题八:DDD案例:网上书店分布式消息队列和分布式缓存的实现
- .NET领域驱动设计实战系列 专题九:DDD案例:网上书店AOP和站点地图的实现
- .NET领域驱动设计实战系列 专题十:DDD扩展内容:全面剖析CQRS模式实现
- .NET领域驱动设计实战系列 专题十一:.NET 领域驱动设计实战系列总结