# 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);
}
}
```
上面程序的運行結果如下圖所示:

## 五、自定義命令
在開發過程中,自然少不了自定義命令來完成內置命令所沒有提供的任務。下面通過一個例子來演示如何創建一個自定義命令。
首先,定義一個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>
```
接下來,看看程序的運行效果,具體的運行結果如下圖所示:

## 六、實現可撤銷的命令程序
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>
```
上面程序的運行效果如下圖所示:

## 七、小結
到這里,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 領域驅動設計實戰系列總結