# WPF快速入门系列(5)——深入解析WPF命令
## 一、引言
WPF命令相对来说是一个崭新的概念,因为命令对于之前的WinForm根本没有实现这个概念,但是这并不影响我们学习WPF命令,因为设计模式中有命令模式,关于命令模式可以参考我设计模式的博文:[http://www.cnblogs.com/zhili/p/CommandPattern.html](http://www.cnblogs.com/zhili/p/CommandPattern.html)。命令模式的要旨在于把命令的发送者与命令的执行者之间的依赖关系分割开了。对此,WPF中的命令也是一样的,**WPF命令使得命令源(即命令发送者,也称调用程序)和命令目标(即命令执行者,也称处理程序)分离**。现在是不是感觉命令是不是亲切了点了呢?下面就详细分享下我对WPF命令的理解。
## 二、命令是什么呢?
上面通过命令模式引出了WPF命令的要旨,那在WPF中,命令是什么呢?对于程序来说,命令就是一个个任务,例如保存,复制,剪切这些操作都可以理解为一个个命令。即当我们点击一个复杂按钮时,此时就相当于发出了一个复制的命令,即告诉文本框执行一个复杂选中内容的操作,然后由文本框控件去完成复制的操作。在这里,复杂按钮就相当于一个命令发送者,而文本框就是命令的执行者。它们之间通过命令对象分割开了。如果采用事件处理机制的话,此时调用程序与处理程序就相互引用了。
所以对于命令只是从不同角度理解问题的一个词汇,之前理解点击一个按钮,触发了一个点击事件,在WPF编程中也可以理解为触发了一个命令。说到这里,问题又来了,WPF中既然有了命令了?那为什么还需要路由事件呢?对于这个问题,我的理解是,事件和命令是处理问题的两种方式,它们之间根本不存在冲突的,并且WPF命令中使用了路由事件。所以准确地说WPF命令应该是路由命令。那为什么说WPF命令是路由的呢?这个疑惑将会在WPF命令模型介绍中为大家解答。
另外,WPF命令除了使命令源和命令目标分割的优点外,它还具有另一个优点:
* 使得控件的启用状态和相应的命令状态保持同步,即命令被禁用时,此时绑定命令的控件也会被禁用。
## 三、WPF命令模型
经过前面的介绍,大家应该已经命令了WPF命令吧。即命令就是一个操作,任务。接下来就要详细介绍了WPF命令模型了。 WPF命令模型具有4个重要元素:
* **命令**——命令表示一个程序任务,并且可跟踪该任务是否能被执行。然而,命令实际上不包含执行应用程序的代码,真正处理程序在命令目标中。
* **命令源**——命令源触发命令,即命令的发送者。例如Button、MenuItem等控件都是命令源,单击它们都会执行绑定的命令。
* **命令目标**——命令目标是在其中执行命令的元素。如Copy命令可以在TextBox控件中复制文本。
* **命令绑定**——前面说过,命令是不包含执行程序的代码的,真正处理程序存在于命令目标中。那命令是怎样映射到处理程序中的呢?这个过程就是通过命令绑定来完成的,命令绑定完成的就是红娘牵线的作用。
WPF命令模型的核心就在于[ICommand](http://msdn.microsoft.com/zh-cn/library/system.windows.input.icommand(v=vs.110).aspx)接口了,该接口定义命令的工作原理。该接口的定义如下所示:
```
public interface ICommand
{
// Events
event EventHandler CanExecuteChanged;
// Methods
bool CanExecute(object parameter);
void Execute(object parameter);
}
```
该接口包括2个方法和一个事件。CanExecute方法返回命令的状态——指示命令是否可执行,例如,文本框中没有选择任何文本,此时Copy命令是不用的,CanExecute则返回为false。
Execute方法就是命令执行的方法,即处理程序。当命令状态改变时,会触发CanExecuteChanged事件。
当自定义命令时,不会直接去实现ICommand接口。而是使用[RoutedCommand](http://msdn.microsoft.com/zh-cn/library/system.windows.input.routedcommand(v=vs.110).aspx)类,该类实是WPF中唯一现了ICommand接口的类。所有WPF命令都是RoutedCommand类或其派生类的实例。然而程序中处理的大部分命令不是RoutedCommand对象,而是RoutedUICommand对象。[RoutedUICommand](http://msdn.microsoft.com/zh-cn/library/system.windows.input.routeduicommand(v=vs.110).aspx)类派生与RoutedCommand类。
接下来介绍下为什么说WPF命令是路由的呢?实际上,RoutedCommand上Execute和CanExecute方法并没有包含命令的处理逻辑,而是将触发遍历元素树的事件来查找具有CommandBinding的对象。而真正命令的处理程序包含在CommandBinding的事件处理程序中。所以说WPF命令是路由命令。该事件会在元素树上查找CommandBinding对象,然后去调用CommandBinding的CanExecute和Execute来判断是否可执行命令和如何执行命令。那这个查找方向是怎样的呢?对于位于工具栏、菜单栏或元素的[FocusManager.IsFocusScope](http://msdn.microsoft.com/zh-cn/library/system.windows.input.commandbinding(v=vs.110).aspx)设置为”true“是从元素树上根元素(一般指窗口元素)向元素方向向下查找,对于其他元素是验证元素树根方向向上查找。
WPF中提供了一组已定义命令,命令包括以下类:ApplicationCommands, NavigationCommands, MediaCommands, EditingCommands, and the ComponentCommands.">[ApplicationCommands](http://msdn.microsoft.com/zh-cn/library/system.windows.input.applicationcommands(v=vs.110).aspx)、[NavigationCommands](http://msdn.microsoft.com/zh-cn/library/system.windows.input.navigationcommands(v=vs.110).aspx)、[MediaCommands](http://msdn.microsoft.com/zh-cn/library/system.windows.input.mediacommands(v=vs.110).aspx)、[EditingCommands](http://msdn.microsoft.com/zh-cn/library/system.windows.documents.editingcommands(v=vs.110).aspx) 以及[ComponentCommands](http://msdn.microsoft.com/zh-cn/library/system.windows.input.componentcommands(v=vs.110).aspx)。 Cut, BrowseBack and BrowseForward, Play, Stop, and Pause.">这些类提供诸如 [Cut](http://msdn.microsoft.com/zh-cn/library/system.windows.input.applicationcommands.cut(v=vs.110).aspx)、[BrowseBack](http://msdn.microsoft.com/zh-cn/library/system.windows.input.navigationcommands.browseback(v=vs.110).aspx)、[BrowseForward](http://msdn.microsoft.com/zh-cn/library/system.windows.input.navigationcommands.browseforward(v=vs.110).aspx)、[Play](http://msdn.microsoft.com/zh-cn/library/system.windows.input.mediacommands.play(v=vs.110).aspx)、[Stop](http://msdn.microsoft.com/zh-cn/library/system.windows.input.mediacommands.stop(v=vs.110).aspx) 和 [Pause](http://msdn.microsoft.com/zh-cn/library/system.windows.input.mediacommands.pause(v=vs.110).aspx) 等命令。
## 四、使用命令
前面都是介绍了一些命令的理论知识,下面介绍了如何使用WPF命令来完成任务。XAML具体实现代码如下所示:
```
1 <Window x:Class="WPFCommand.MainWindow"
2 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
3 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
4 Title="MainWindow" Height="200" Width="300">
5 <!--定义窗口命令绑定,绑定的命令是New命令,处理程序是NewCommand-->
6 <Window.CommandBindings>
7 <CommandBinding Command="ApplicationCommands.New" Executed="NewCommand"/>
8 </Window.CommandBindings>
9
10 <StackPanel>
11 <Menu>
12 <MenuItem Header="File">
13 <!--WPF内置命令都可以采用其缩写形式-->
14 <MenuItem Command="New"></MenuItem>
15 </MenuItem>
16 </Menu>
17
18 <!--获得命令文本的两种方式-->
19 <!--直接从静态的命令对象中提取文本-->
20 <Button Margin="5" Padding="5" Command="ApplicationCommands.New" ToolTip="{x:Static ApplicationCommands.New}">New</Button>
21
22 <!--使用数据绑定,获得正在使用的Command对象,并提取其Text属性-->
23 <Button Margin="5" Padding="5" Command="ApplicationCommands.New" Content="{Binding RelativeSource={RelativeSource Self},Path=Command.Text}"/>
24 <Button Margin="5" Padding="5" Visibility="Visible" Click="cmdDoCommand_Click" >DoCommand</Button>
25 </StackPanel>
26 </Window>
```
其对应的后台代码实现如下所示:
```
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
//// 后台代码创建命令绑定
//CommandBinding bindingNew = new CommandBinding(ApplicationCommands.New);
//bindingNew.Executed += NewCommand;
//// 将创建的命令绑定添加到窗口的CommandBindings集合中
//this.CommandBindings.Add(bindingNew);
}
private void NewCommand(object sender, ExecutedRoutedEventArgs e)
{
MessageBox.Show("New 命令被触发了,命令源是:" + e.Source.ToString());
}
private void cmdDoCommand_Click(object sender, RoutedEventArgs e)
{
// 直接调用命令的两种方式
ApplicationCommands.New.Execute(null, (Button)sender);
//this.CommandBindings[0].Command.Execute(null);
}
}
```
上面程序的运行结果如下图所示:
![](https://box.kancloud.cn/2016-01-23_56a2eb42d33ee.png)
## 五、自定义命令
在开发过程中,自然少不了自定义命令来完成内置命令所没有提供的任务。下面通过一个例子来演示如何创建一个自定义命令。
首先,定义一个Requery命令,具体的实现如下所示:
```
public class DataCommands
{
private static RoutedUICommand requery;
static DataCommands()
{
InputGestureCollection inputs = new InputGestureCollection();
inputs.Add(new KeyGesture(Key.R, ModifierKeys.Control, "Ctrl+R"));
requery = new RoutedUICommand(
"Requery", "Requery", typeof(DataCommands), inputs);
}
public static RoutedUICommand Requery
{
get { return requery; }
}
}
```
上面代码实现了一个Requery命令,为了演示效果,我们需要把该命令应用到XAML标签上,具体的XAML代码如下所示:
```
<!--要使用自定义命令,首先需要将.NET命名空间映射为XAML名称空间,这里映射的命名空间为local-->
<Window x:Class="WPFCommand.CustomCommand"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WPFCommand"
Title="CustomCommand" Height="300" Width="300" >
<Window.CommandBindings>
<!--定义命令绑定-->
<CommandBinding Command="local:CustomCommands.Requery" Executed="RequeryCommand_Execute"/>
</Window.CommandBindings>
<StackPanel>
<!--应用命令-->
<Button Margin="5" Command="local:CustomCommands.Requery" Content="{Binding RelativeSource={RelativeSource Self}, Path=Command.Text}"></Button>
</StackPanel>
</Window>
```
接下来,看看程序的运行效果,具体的运行结果如下图所示:
![](https://box.kancloud.cn/2016-01-23_56a2eb42e460c.png)
## 六、实现可撤销的命令程序
WPF命令模型缺少的一个特征就是Undo命令,尽管提供了一个ApplicationCommands.Undo命令,但是该命令通常被用于编辑控件,如TextBox控件。如果希望支持应用程序范围内的Undo操作,就需要在内部跟踪以前的命令,并且触发Undo操作时还原该命令。这个实现原理就是保持用一个集合对象保存之前所有执行过的命令,当触发Undo操作时,还要上一个命令的状态。这里除了需要保存执行过的命令外,还需要保存触发命令的控件以及状态,所以我们需要抽象出一个类来保存这些属性,我们取名这个类为CommandHistoryItem。为了保存命令和命令的状态,自然就需要在完成命令之前进行保存,所以自然联想到是否有Preview之类的事件呢?实际上确实有,这个事件就是PreviewExecutedEvent,所以我们需要在窗口加载完成后把这个事件注册到窗口上,这里在触发这个事件的时候就可以保存即将要执行的命令、命令源和命令源的内容。另外,之前的命令自然需要保存到一个列表中,这里使用ListBox控件作为这个列表,如果不希望用户在界面上看到之前的命令列表的话,也可以使用List等集合容器。
上面讲解完了主要实现思路之后,下面我们梳理下实现思路:
1. **抽象一个CommandHistoryItem来保存命令相关的属性。**
2. **注册PreviewExecutedEvent事件,为了在命令执行完之前保存命令、命令源以及命令源当前的状态。**
3. **在PreviewExecutedEvent事件处理程序中,把命令相关属性添加到ListBox列表中。**
4. **当执行撤销操作时,可以从ListBox.Items列表中取出上一个执行的命令进行恢复之前命令的状态。**
有了上面的实现思路之后,实现这个可撤销的命令程序也就是码代码的过程了。具体的后台代码实现如下所示:
```
1 public partial class CommandsMonitor : Window
2 {
3 private static RoutedUICommand undo;
4 public static RoutedUICommand Undo
5 {
6 get { return CommandsMonitor.undo; }
7 }
8
9 static CommandsMonitor()
10 {
11 undo = new RoutedUICommand("Undo", "Undo", typeof(CommandsMonitor));
12 }
13
14 public CommandsMonitor()
15 {
16 InitializeComponent();
17 // 按下菜单栏按钮时,PreviewExecutedEvent事件会被触发2次,即CommandExecuted事件处理程序被触发了2次
18 // 一次是菜单栏按钮本身,一次是目标源触发命令的执行,所以在CommandExecuted要过滤掉不关心的命令源
19 this.AddHandler(CommandManager.PreviewExecutedEvent, new ExecutedRoutedEventHandler(CommandExecuted));
20 }
21
22 public void CommandExecuted(object sender, ExecutedRoutedEventArgs e)
23 {
24 // 过滤掉命令源是菜单按钮的,因为我们只关心Textbox触发的命令
25 if (e.Source is ICommandSource)
26 return;
27 // 过滤掉Undo命令
28 if (e.Command == CommandsMonitor.Undo)
29 return;
30
31 TextBox txt = e.Source as TextBox;
32 if (txt != null)
33 {
34 RoutedCommand cmd = e.Command as RoutedCommand;
35 if (cmd != null)
36 {
37 CommandHistoryItem historyItem = new CommandHistoryItem()
38 {
39 CommandName = cmd.Name,
40 ElementActedOn = txt,
41 PropertyActedOn = "Text",
42 PreviousState = txt.Text
43 };
44
45 ListBoxItem item = new ListBoxItem();
46 item.Content = historyItem;
47 lstHistory.Items.Add(item);
48 }
49
50 }
51 }
52
53 private void window_Unloaded(object sender, RoutedEventArgs e)
54 {
55 this.RemoveHandler(CommandManager.PreviewExecutedEvent, new ExecutedRoutedEventHandler(CommandExecuted));
56 }
57
58 private void UndoCommand_Executed(object sender, RoutedEventArgs e)
59 {
60 ListBoxItem item = lstHistory.Items[lstHistory.Items.Count - 1] as ListBoxItem;
61
62 CommandHistoryItem historyItem = item.Content as CommandHistoryItem;
63 if (historyItem == null)
64 {
65 return;
66 }
67
68 if (historyItem.CanUndo)
69 {
70 historyItem.Undo();
71 }
72 lstHistory.Items.Remove(item);
73 }
74
75 private void UndoCommand_CanExecuted(object sender, CanExecuteRoutedEventArgs e)
76 {
77 if (lstHistory == null || lstHistory.Items.Count == 0)
78 {
79 e.CanExecute = false;
80 }
81 else
82 {
83 e.CanExecute = true;
84 }
85 }
86 }
87
88 public class CommandHistoryItem
89 {
90 public String CommandName { get; set; }
91 public UIElement ElementActedOn { get; set; }
92
93 public string PropertyActedOn { get; set; }
94
95 public object PreviousState { get; set; }
96
97 public bool CanUndo
98 {
99 get { return (ElementActedOn != null && PropertyActedOn != ""); }
100 }
101
102 public void Undo()
103 {
104 Type elementType = ElementActedOn.GetType();
105 PropertyInfo property = elementType.GetProperty(PropertyActedOn);
106 property.SetValue(ElementActedOn, PreviousState, null);
107 }
108 }
109 }
```
其对应的XAML界面设计代码如下所示:
```
<Window x:Class="WPFCommand.CommandsMonitor"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="CommandsMonitor" Height="300" Width="350"
xmlns:local="clr-namespace:WPFCommand"
Unloaded="window_Unloaded">
<Window.CommandBindings>
<CommandBinding Command="local:CommandsMonitor.Undo"
Executed="UndoCommand_Executed"
CanExecute="UndoCommand_CanExecuted"/>
</Window.CommandBindings>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<ToolBarTray Grid.Row="0">
<ToolBar>
<Button Command="ApplicationCommands.Cut">Cut</Button>
<Button Command="ApplicationCommands.Copy">Copy</Button>
<Button Command="ApplicationCommands.Paste">Paste</Button>
</ToolBar>
<ToolBar>
<Button Command="local:CommandsMonitor.Undo">Reverse Last Command</Button>
</ToolBar>
</ToolBarTray>
<TextBox Margin="5" Grid.Row="1"
TextWrapping="Wrap" AcceptsReturn="True">
</TextBox>
<TextBox Margin="5" Grid.Row="2"
TextWrapping="Wrap" AcceptsReturn="True">
</TextBox>
<ListBox Grid.Row="3" Name="lstHistory" Margin="5" DisplayMemberPath="CommandName"></ListBox>
</Grid>
</Window>
```
上面程序的运行效果如下图所示:
![](https://box.kancloud.cn/2016-01-23_56a2eb4302760.gif)
## 七、小结
到这里,WPF命令的内容就介绍结束了,关于命令主要记住命令模型四要素——命令、命令绑定、命令源和命令目标。后面继续为大家分享WPF的资源和样式的内容。
本文所有源码:[WPFCommandDemo.zip](http://files.cnblogs.com/zhili/WPFCommandDemo.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 领域驱动设计实战系列总结