# WPF快速入门系列(7)——深入解析WPF模板
## 一、引言
模板从字面意思理解是“具有一定规格的样板"。在现实生活中,砖块都是方方正正的,那是因为制作砖块的模板是方方正正的,如果我们使模板为圆形的话,则制作出来的砖块就是圆形的,此时我们并不能说圆形的”砖块“不是砖块吧。因为形状只是它们的外观,其制作材料还是一样的。所以,模板可以理解为表现形式。WPF中的模板同样是表现形式的意思。
在WPF中包括三种模板:控件模板、数据模版和面板模板。它们都继承于[FrameworkTemplate](http://msdn.microsoft.com/zh-cn/library/system.windows.frameworktemplate(v=vs.110).aspx)基类,其继承层次结果如下图所示:
![](https://box.kancloud.cn/2016-01-23_56a2eb43891de.png)
从上图可以发现,FrameworkTemplate确实有三个子类,它们正是WPF中支持的三种模板。对于控件模板,即控件外观外衣,可以通过修改控件模板来自定义控件的外观表现,例如,可以通过修改按钮的控件模板使按钮表现为圆形;数据模板,即数据的外衣。用于从一个对象中提取数据,并在内容控件或列表控件的各个项中显示数据。面板模板即面板的外衣,而面板又用于进行布局的,所以面板的外衣也就是布局的外衣,通过修改面板模板可以自定义控件的布局。例如,ListBox默认是自从向下地显示每一项的,此时可以通过修改面板模板使其自左向右地显示每一项。
WPF模板其实都是外观的表现形式,不管是控件模板、数据模板还是面板模板,其都是改变控件的表现形式。只不过这三种控件的作用点不一样罢了。控件模板是针对于控件本身,修改它可以改变控件本身表现的样子;数据模板针对控件的数据,修改它可以改变控件绑定的数据表现样子。既然是决定数据的表现,从而决定其一般应用于数据绑定控件,如ListBox、ListView等控件。面板模板则针对于控件的布局,修改它可以影响控件的布局方式。
## 二、控件模板
在分别介绍这三种控件模板之前,我觉得你有必要先了解WPF的逻辑树和可视化树的内容,因为你要修改控件模板,则首先需要了解控件的组成。
## 2.1 WPF的逻辑树和可视化树
在许多技术中,元素和组件都是按树结构的形式进行组织的。使用这样的结构,**开发人员可以直接操作树中的对象节点来程序对象,从而通过操作该对象来修改程序的表现和行为(这是了解逻辑树和可视化树的主要原因)。**在WPF中,同样使用了树结构来组织元素之间的关系。WPF中支持逻辑树和可视化树的概念,并且WPF公开了两个提供树形视图帮助器类:[LogicalTreeHelper](http://msdn.microsoft.com/zh-cn/library/system.windows.logicaltreehelper(v=vs.110).aspx) 和 [VisualTreeHelper](http://msdn.microsoft.com/zh-cn/library/system.windows.media.visualtreehelper(v=vs.110).aspx)。逻辑树指的是UI界面的组成元素的结构。先看下面的XAML代码的例子:
```
<Window x:Class="TemplateDemo.VisualTree"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="200" Width="300">
<StackPanel Margin="5">
<Button Padding="5" Margin="10">First Button</Button>
<Button Padding="5" Margin="10">Second Button</Button>
</StackPanel>
</Window>
```
上面XAML的逻辑树如下图所示:
![](https://box.kancloud.cn/2016-01-23_56a2eb439a54b.png)
可视化树是逻辑树的扩展版本,它将元素分成更小的部分。上面XAML代码对应的可视化树如下图所示:
![](https://box.kancloud.cn/2016-01-23_56a2eb43ae359.png)
从上面可视化树可以看出,Button由多个可视化元素组成——使按钮具有阴影背景特征的边框(由[ButtonChrome](http://msdn.microsoft.com/zh-cn/library/microsoft.windows.themes.buttonchrome(v=vs.110).aspx)类表示)、内部的容器(一个ContentPresenter对象)以及存储按钮文本的文本块控件(由TextBlock表示)。上面的可视化树和逻辑树结构并不是我凭空想象出来的,而是有事实依据的,我们可以通过VisualTreeHelper类和LogicTreeHelper类提供的方法来查看窗口的可视化树和逻辑树,下面的例子实现了这个需求,具体的XAML实现如下所示:
```
<Window x:Class="TemplateDemo.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="380" Width="400">
<StackPanel Margin="5">
<Button Padding="5" Margin="5" Click="ShowLogicTree">Show Logic Tree Button</Button>
<Button Padding="5" Margin="5" Click="ShowVisualTree">Show Visual Tree Button</Button>
<!--TreeView控件用来显示窗口的逻辑树和可视化树-->
<TreeView Name="treeElements" Margin="5"></TreeView>
</StackPanel>
</Window>
```
对应的后台代码实现入下所示:
```
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
}
// 把公共代码抽象出一个方法,从而使代码重用
public void ProcessElement(object obj, TreeViewItem item, TreeViewItem previousItem)
{
item.Header = obj.GetType().Name;
item.IsExpanded = true;
// 如果当前元素是第一个元素就添加到树集合上
// 如果是内嵌元素,则添加到它的父节点上
if (previousItem == null)
{
treeElements.Items.Add(item);
}
else
{
previousItem.Items.Add(item);
}
}
private void PrintLogicTree(object obj, TreeViewItem previousItem)
{
TreeViewItem item = new TreeViewItem();
ProcessElement(obj, item, previousItem);
// 如果不是DependencyObject,则返回
if (!(obj is DependencyObject))
return;
// 递归打印逻辑树
foreach(object child in LogicalTreeHelper.GetChildren(obj as DependencyObject))
{
// 这里为了避免死循环,因为TreeView的子元素包含Window1、StackPanel等控件
// 如果不加这个条件,控件会一直反复循环
if (child is TreeView)
return;
PrintLogicTree(child, item);
}
}
private void PrintVisualTree(DependencyObject obj, TreeViewItem previousItem)
{
TreeViewItem item = new TreeViewItem();
ProcessElement(obj, item, previousItem);
// 递归输出视觉树
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
{
if (obj is TreeView)
return;
PrintVisualTree(VisualTreeHelper.GetChild(obj, i), item);
}
}
private void ShowLogicTree(object sender, RoutedEventArgs e)
{
treeElements.Items.Clear();
PrintLogicTree(this, null);
}
private void ShowVisualTree(object sender, RoutedEventArgs e)
{
treeElements.Items.Clear();
PrintVisualTree(this, null);
}
}
```
程序的运行效果如下图所示:
![](https://box.kancloud.cn/2016-01-23_56a2eb43bd6e2.png)
## 2.2 通过控件模板自定义控件外观
控件模板既然是控件的外衣,自然我们可以创建的新的控件模板,然后把新的控件模板应用到需要应用的控件中,这时候应用了新控件模板的控件,将会使用新的控件模板来渲染自身,从而改变控件的外观。这也是自定义控件外观的要旨。在WPF中按钮的默认控件是长方形的,我们可以通过创建一个新的控件模板来改变按钮的外观,下面的例子就实现了通过控件模板的方式自定义了一个圆形的按钮。具体的XAML代码如下所示:
```
<Window x:Class="TemplateDemo.ControlTemplate"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="ControlTemplate" Height="300" Width="300">
<Window.Resources>
<!--定义控件模板,并使用key标记-->
<ControlTemplate x:Key="roundButtonTemplate" TargetType="Button">
<Grid>
<Ellipse Name="ell" Fill="Orange" Width="100" Height="100"></Ellipse>
<!--使用模板绑定来绑定按钮的内容-->
<ContentPresenter Content="{TemplateBinding Button.Content}" VerticalAlignment="Center" HorizontalAlignment="Center"></ContentPresenter>
</Grid>
<!--定义模板触发器-->
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="ell" Property="Fill" Value="Yellow"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Window.Resources>
<StackPanel Margin="10">
<Button Content="Round Button" Template="{StaticResource roundButtonTemplate}"></Button>
</StackPanel>
</Window>
```
此时,你就可以看到按钮是一个圆形的了,并且当鼠标移动到按钮上时,会触发模板触发器来改变Ellipse的填充色,具体的运行效果如下图所示:
![](https://box.kancloud.cn/2016-01-23_56a2eb43d72c4.png)
从上面的控件模板的使用可知,它和创建自定义控件不同,在很多情况下,你不需要编写自己的控件,你只是希望更改控件的外观。使用控件面板非常简单:
* **首先在资源集合中创建一个ControlTemplate,并指定key标记**
* **然后赋值到控件的Template属性中。**
## 三、数据模板
数据模板是数据的外衣,数据模板是一段定义如何绑定数据对象的XAML标记,有两种类型的控件支持数据模板:
* 内容控件通过ContentTemplate属性支持数据模板。内容模板用于显示任何放在Content属性中的内容。
* 列表控件,即继承自ItemsControl类的控件,通过ItemPlate属性支持数据模板。该模板用于显示由ItemSource提供集合中的每一项。
基于列表的模板实际上是以内容控件模板为基础的,因为列表中的每一项由一个内容控件包装的。如ListBox控件的ListBoxItem元素。下面让我们具体看看如何去创建一个数据模板吧。
## 3.1 如何定义数据模板
具体的XAML代码如下所示:
```
<Window x:Class="TemplateDemo.DataTemplate"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local ="clr-namespace:TemplateDemo;assembly=TemplateDemo"
Title="DataTemplate" Height="300" Width="300">
<Window.Resources>
<!--创建数据模板-->
<DataTemplate x:Key="personDataTem">
<Border Name="blueBorder" Margin="3" BorderThickness="3" BorderBrush="Blue"
CornerRadius="5">
<Grid Margin="3">
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<TextBlock Name="nametxt" FontWeight="Bold" Text="{Binding Path=Name}"></TextBlock>
<TextBlock Grid.Row="1" Text="{Binding Path=Age}"></TextBlock>
</Grid>
</Border>
<!--定义数据模板触发器-->
<DataTemplate.Triggers>
<Trigger SourceName="blueBorder" Property="IsMouseOver" Value="True">
<Setter TargetName="blueBorder" Property="Background" Value="LightGray"/>
<Setter TargetName="nametxt" Property="FontSize" Value="20"/>
</Trigger>
</DataTemplate.Triggers>
</DataTemplate>
</Window.Resources>
<StackPanel Margin="5">
<ListBox Name="lstPerson" HorizontalContentAlignment="Stretch" ItemTemplate="{StaticResource personDataTem}"></ListBox>
</StackPanel>
</Window>
```
其对应的后台代码如下所示:
```
public partial class DataTemplate : Window
{
ObservableCollection<Student> persons = new ObservableCollection<Student>()
{
new Student() { Name ="LearningHard", Age=25},
new Student() { Name ="HelloWorld", Age=22}
};
public DataTemplate()
{
InitializeComponent();
lstPerson.ItemsSource = persons;
}
}
public class Student : INotifyPropertyChanged
{
public string ID { get { return Guid.NewGuid().ToString(); } }
public string Name { get; set; }
public int Age { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(PropertyChangedEventArgs e)
{
if (PropertyChanged != null)
PropertyChanged(this, e);
}
}
```
其运行效果如下图所示:
![](https://box.kancloud.cn/2016-01-23_56a2eb43e7321.png)
从上面数据模板的创建可知,使用DataTemplate很简单:
* **首先在资源集合中创建一个数据模板,并设置key标签。**
* **然后将key赋值到控件的CellTemplate或ContentTemplate或ItemTemplate属性上即可。**
## 3\. 2 数据模板与控件模板的关系
从上面的介绍可知,控件只是数据和行为的载体,至于它本身长什么样子和数据长什么样子都是靠Template决定的。决定控件外观的是ControlTemplate,决定数据外观的是DataTemplate,它们正是[Control](http://msdn.microsoft.com/zh-cn/library/system.windows.controls.control(v=vs.110).aspx)类的Template和ContentTemplate两个属性的值。
一般来说,ControlTemplate内都有一个ContentPresenter,这个ContentPresenter的ContentTemplate就是DataTemplate类型。所以数据模板和控件模板的关系如下图所示:
![](https://box.kancloud.cn/2016-01-23_56a2eb440208b.png)
## 四、创建面板模板
ItemsPanelTemplate用于指定项的布局。 [ItemsControl](http://msdn.microsoft.com/zh-cn/library/system.windows.controls.itemscontrol(v=vs.110).aspx) 类型具有一个类型为ItemsPanelTemplate 的 [ItemsPanel](http://msdn.microsoft.com/zh-cn/library/system.windows.controls.itemscontrol.itemspanel(v=vs.110).aspx) 属性。
每种ItemsControl都有其默认的ItemsPanelTemplate。ListBox, the default uses the VirtualizingStackPanel.">对于 [ListBox](http://msdn.microsoft.com/zh-cn/library/system.windows.controls.listbox(v=vs.110).aspx),默认值使用 [VirtualizingStackPanel](http://msdn.microsoft.com/zh-cn/library/system.windows.controls.virtualizingstackpanel(v=vs.110).aspx)。 MenuItem, the default uses WrapPanel.">对于 [MenuItem](http://msdn.microsoft.com/zh-cn/library/system.windows.controls.menuitem(v=vs.110).aspx),默认值使用 [WrapPanel](http://msdn.microsoft.com/zh-cn/library/system.windows.controls.wrappanel(v=vs.110).aspx)。 StatusBar, the default uses DockPanel.">对于 [StatusBar](http://msdn.microsoft.com/zh-cn/library/system.windows.controls.primitives.statusbar(v=vs.110).aspx),默认值使用 [DockPanel](http://msdn.microsoft.com/zh-cn/library/system.windows.controls.dockpanel(v=vs.110).aspx)。
ListBox, the default uses the VirtualizingStackPanel.">MenuItem, the default uses WrapPanel.">StatusBar, the default uses DockPanel.">自定义面板模板与自定义数据面板和数据面板一样简单,一样只需要首先定义一个面板模板在资源集合中,然后将其Key指定给ItemsPanel属性即可。具体的XAML实现如下所示:
```
<Window x:Class="TemplateDemo.ItemsPanelTemplate"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="ItemsPanelTemplate" Height="300" Width="300">
<Window.Resources>
<!--定义DataTemplate-->
<DataTemplate x:Key="personDataTem">
<Border Name="blueBorder" Margin="3" BorderThickness="3" BorderBrush="Blue"
CornerRadius="5">
<Grid Margin="3">
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<TextBlock Name="nametxt" FontWeight="Bold" Text="{Binding Path=Name}"></TextBlock>
<TextBlock Grid.Row="1" Text="{Binding Path=Age}"></TextBlock>
</Grid>
</Border>
</DataTemplate>
<!--定义ItemsPanelTemplate-->
<ItemsPanelTemplate x:Key="listItemsPanelTem">
<StackPanel Orientation="Horizontal"
VerticalAlignment="Center"
HorizontalAlignment="Left"/>
</ItemsPanelTemplate>
</Window.Resources>
<!--使用ItemsPanelTemplate只需要赋值给ItemsPanel属性即可-->
<ListBox Name="lstPerson" ItemsPanel="{StaticResource listItemsPanelTem}" ItemTemplate="{StaticResource personDataTem}" />
</Window>
```
其后台代码和数据模板的后台代码一样,其实现代码为:
```
public partial class ItemsPanelTemplate : Window
{
ObservableCollection<Student> persons = new ObservableCollection<Student>()
{
new Student() { Name ="LearningHard", Age=25},
new Student() { Name ="HelloWorld", Age=22}
};
public ItemsPanelTemplate()
{
InitializeComponent();
lstPerson.ItemsSource = persons;
}
}
```
此时程序运行的效果如下图所示,从下图结果可以看出,此时ListBox中的项不再是自上而下排列了,而是从左向右排列的。
![](https://box.kancloud.cn/2016-01-23_56a2eb44136f4.png)
## 五、总结
到这里,WPF模板的内容就介绍结束了,本文主要介绍了WPF中支持的三种模板:控件模板、数据模板和面板模板,然后各自定义并使用了自定义的模板,最后介绍了这三个模板之间的联系。使用这三个模板的方式都非常简单,**都是先定义一个模板,然后在把对应的key应用到控件对应的属性中,对于控件模板,应用的是控件的Template,对于数据模板,应用的是控件的ItemTemplate属性,对于面板模板,应用的是控件的ItemsPanel属性。**在下面的一篇博文将介绍如何实现一个MVVM的实例程序。
本文所有源码下载:[TemplateDemo.zip](http://files.cnblogs.com/zhili/TemplateDemo.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 领域驱动设计实战系列总结