# WPF快速入門系列(2)——深入解析依賴屬性
## 一、引言
感覺最近都頹廢了,好久沒有學習寫博文了,出于負罪感,今天強烈逼迫自己開始更新WPF系列。盡管最近看到一篇WPF技術是否老矣的文章,但是還是不能阻止我系統學習WPF。今天繼續分享WPF中一個最重要的知識點——依賴屬性。
## 二、依賴屬性的全面解析
聽到依賴屬性,自然聯想到C#中屬性的概念。C#中屬性是抽象模型的核心部分,而依賴屬性是專門基于WPF創建的。在WPF庫實現中,依賴屬性使用普通的C#屬性進行了包裝,使得我們可以通過和以前一樣的方式來使用依賴屬性,但我們必須明確,在WPF中我們大多數都在使用依賴屬性,而不是使用屬性。依賴屬性重要性在于,在WPF核心特性,如動畫、數據綁定以及樣式中都需要使用到依賴屬性。既然WPF引入了依賴屬性,也自然有其引入的道理。WPF中的依賴屬性主要有以下三個優點:
* 依賴屬性加入了屬性變化通知、限制、驗證等功能。這樣可以使我們更方便地實現應用,同時大大減少了代碼量。許多之前需要寫很多代碼才能實現的功能,在WPF中可以輕松實現。
* 節約內存:在WinForm中,每個UI控件的屬性都賦予了初始值,這樣每個相同的控件在內存中都會保存一份初始值。而WPF依賴屬性很好地解決了這個問題,它內部實現使用哈希表存儲機制,對多個相同控件的相同屬性的值都只保存一份。關于依賴屬性如何節約內存的更多內容參考:[WPF的依賴屬性是怎么節約內存的](http://www.cnblogs.com/zoupeiyang/archive/2011/07/22/2113778.html)
* 支持多種提供對象:可以通過多種方式來設置依賴屬性的值。可以配合表達式、樣式和綁定來對依賴屬性設置值。
## 2.1 依賴屬性的定義
上面介紹了依賴屬性所帶來的好處,這時候,問題又來了,怎樣自己定義一個依賴屬性呢?C#屬性的定義大家再熟悉不過了。下面通過把C#屬性進行改寫成依賴屬性的方式來介紹依賴屬性的定義。下面是一個屬性的定義:
在把上面屬性改寫為依賴屬性之前,下面總結下定義依賴屬性的步驟:
1. 讓依賴屬性的所在類型繼承自[DependencyObject](http://msdn.microsoft.com/zh-cn/library/system.windows.dependencyobject(v=vs.110).aspx)類。
2. 使用public static 聲明一個[DependencyProperty](http://msdn.microsoft.com/zh-cn/library/system.windows.dependencyproperty(v=vs.110).aspx)的變量,該變量就是真正的依賴屬性。
3. 在類型的靜態構造函數中通過[Register](http://msdn.microsoft.com/zh-cn/library/ms597501(v=vs.110).aspx)方法完成依賴屬性的元數據注冊。
4. 提供一個依賴屬性的包裝屬性,通過這個屬性來完成對依賴屬性的讀寫操作。
根據上面的四個步驟,下面來把Name屬性來改寫成一個依賴屬性,具體的實現代碼如下所示:
```
// 1\. 使類型繼承DependencyObject類
public class Person : DependencyObject
{
// 2\. 聲明一個靜態只讀的DependencyProperty 字段
public static readonly DependencyProperty nameProperty;
static Person()
{
// 3\. 注冊定義的依賴屬性
nameProperty = DependencyProperty.Register("Name", typeof(string), typeof(Person),
new PropertyMetadata("Learning Hard",OnValueChanged));
}
// 4\. 屬性包裝器,通過它來讀取和設置我們剛才注冊的依賴屬性
public string Name
{
get { return (string)GetValue(nameProperty); }
set { SetValue(nameProperty, value); }
}
private static void OnValueChanged(DependencyObject dpobj, DependencyPropertyChangedEventArgs e)
{
// 當只發生改變時回調的方法
}
}
```
從上面代碼可以看出,**依賴屬性是通過調用DependencyObject的GetValue和SetValue來對依賴屬性進行讀寫的**。它使用哈希表來進行存儲的,對應的Key就是屬性的HashCode值,而值(Value)則是注冊的DependencyPropery;而C#中的屬性是類私有字段的封裝,可以通過對該字段進行操作來對屬性進行讀寫。總結為:**屬性是字段的包裝,WPF中使用屬性對依賴屬性進行包裝。**
## 2.2 依賴屬性的優先級
WPF允許在多個地方設置依賴屬性的值,則自然就涉及到依賴屬性獲取值的優先級問題。例如下面XMAL代碼,我們在三個地方設置了按鈕的背景顏色,那最終按鈕會讀取那個設置的值呢?是Green、Yellow還是Red?
```
<Window x:Class="DPSample.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Button x:Name="myButton" **Background="Green"** Width="100" Height="30">
<Button.Style>
<Style TargetType="{x:Type Button}">
<Setter **Property="Background" Value="Yellow"**/>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter **Property="Background" Value="Red"** />
</Trigger>
</Style.Triggers>
</Style>
</Button.Style>
Click Me
</Button>
</Grid>
</Window>
```
上面按鈕的背景顏色是Green。之所以背景色是Green,是因為WPF每訪問一個依賴屬性,它都會按照下面的順序由高到底處理該值。具體優先級如下圖所示:

在上面XAML中,按鈕的本地值設置的是Green,自定義Style Trigger設置的為Red,自定義的Style Setter設置的為Yellow,由于這里的本地值的優先級最高,所以按鈕的背景色或者的是Green值。如果此時把本地值Green去掉的話,此時按鈕的背景顏色是Yellow而不是Red。這里盡管Style Trigger的優先級比Style Setter高,但是由于此時Style Trigger的IsMouseOver屬性為false,即鼠標沒有移到按鈕上,一旦鼠標移到按鈕上時,此時按鈕的顏色就為Red。此時才會體現出Style Trigger的優先級比Style Setter優先級高。所以上圖中優先級是比較理想情況下,很多時候還需要具體分析。
## 2.3 依賴屬性的繼承
依賴屬性是可以被繼承的,即父元素的相關設置會自動傳遞給所有的子元素。下面代碼演示了依賴屬性的繼承。
```
<Window x:Class="Custom_DPInherited.DPInherited"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300"
FontSize="18"
Title="依賴屬性的繼承">
<StackPanel >
<Label Content="繼承自Window的FontSize" />
<Label Content="顯式設置FontSize"
TextElement.FontSize="36"/>
<StatusBar>Statusbar沒有繼承自Window的FontSize</StatusBar>
</StackPanel>
</Window>
```
上面的代碼的運行效果如下圖所示:

在上面XAML代碼中。Window.FontSize設置會影響所有內部子元素字體大小,這就是依賴屬性的繼承。如第一個Label沒有定義FontSize,所以它繼承了Window.FontSize值。但一旦子元素提供了顯式設置,這種繼承就會被打斷,所以Window.FontSize值對于第二個Label不再起作用。
這時,你可能已經發現了問題:StatusBar沒有顯式設置FontSize值,但它的字體大小沒有繼承Window.FontSize的值,而是保持了系統的默認值。那這是什么原因呢?其實導致這樣的問題:并不是所有元素都支持屬性值繼承的,如StatusBar、Tooptip和Menu控件。另外,StatusBar等控件截獲了從父元素繼承來的屬性,并且該屬性也不會影響StatusBar控件的子元素。例如,如果我們在StatusBar中添加一個Button。那么這個Button的FontSize屬性也不會發生改變,其值為默認值。
前面介紹了依賴屬性的繼承,那我們如何把自定義的依賴屬性設置為可被其他控件繼承呢?通過[AddOwer](http://msdn.microsoft.com/zh-cn/library/ms597484(v=vs.110).aspx)方法可以依賴屬性的繼承。具體的實現代碼如下所示:
```
1 public class CustomStackPanel : StackPanel
2 {
3 public static readonly DependencyProperty MinDateProperty;
4
5 static CustomStackPanel()
6 {
7 MinDateProperty = DependencyProperty.Register("MinDate", typeof(DateTime), typeof(CustomStackPanel), new FrameworkPropertyMetadata(DateTime.MinValue, FrameworkPropertyMetadataOptions.Inherits));
8 }
9
10 public DateTime MinDate
11 {
12 get { return (DateTime)GetValue(MinDateProperty); }
13 set { SetValue(MinDateProperty, value); }
14 }
15 }
16
17 public class CustomButton :Button
18 {
19 private static readonly DependencyProperty MinDateProperty;
20
21 static CustomButton()
22 {
23 // AddOwner方法指定依賴屬性的所有者,從而實現依賴屬性的繼承,即CustomStackPanel的MinDate屬性被CustomButton控件繼承。
24 // 注意FrameworkPropertyMetadataOptions的值為Inherits
25 MinDateProperty = CustomStackPanel.MinDateProperty.AddOwner(typeof(CustomButton), new FrameworkPropertyMetadata(DateTime.MinValue, FrameworkPropertyMetadataOptions.Inherits));
26 }
27
28 public DateTime MinDate
29 {
30 get { return (DateTime)GetValue(MinDateProperty); }
31 set { SetValue(MinDateProperty, value); }
32 }
33 }
```
接下來,你可以在XAML中進行測試使用,具體的XAML代碼如下:
```
<Window x:Class="Custom_DPInherited.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Custom_DPInherited"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
Title="實現自定義依賴屬性的繼承" Height="350" Width="525">
<Grid>
<local:CustomStackPanel x:Name="customStackPanle" MinDate="{x:Static sys:DateTime.Now}">
<!--CustomStackPanel的依賴屬性-->
<ContentPresenter Content="{Binding Path=MinDate, ElementName=customStackPanle}"/>
<local:CustomButton Content="{Binding RelativeSource={x:Static RelativeSource.Self}, Path=MinDate}" Height="25"/>
</local:CustomStackPanel>
</Grid>
</Window>
```
上面XAML代碼中,顯示設置了CustomStackPanel的MinDate的值,而在CustomButton中卻沒有顯式設置其MinDate值。CustomButton的Content屬性的值是通過綁定MinDate屬性來進行獲取的,關于綁定的更多內容會在后面文章中分享。在這里CustomButton中并沒有設置MinDate的值,但是CustomButton的Content的值卻是當前的時間,從而可以看出,此時CustomButton的MinDate屬性繼承了CustomStackPanel的MinDate的值,從而設置了其Content屬性。最終的效果如下圖所示:

## 2.4 只讀依賴屬性
在C#屬性中,我們可以通過設置只讀屬性來防止外界惡意更改該屬性值,同樣,在WPF中也可以設置只讀依賴屬性。如[IsMouseOver](http://msdn.microsoft.com/zh-cn/library/system.windows.uielement.ismouseover(v=vs.110).aspx)就是一個只讀依賴屬性。那我們如何創建一個只讀依賴屬性呢?其實只讀的依賴屬性的定義方式與一般依賴屬性的定義方式基本一樣。只讀依賴屬性僅僅是用[DependencyProperty.RegisterReadonly](http://msdn.microsoft.com/zh-cn/library/system.windows.dependencyproperty.registerreadonly(v=vs.110).aspx)替換了DependencyProperty.Register而已。下面代碼實現了一個只讀依賴屬性。
```
1 public partial class MainWindow : Window
2 {
3 public MainWindow()
4 {
5 InitializeComponent();
6
7 // 內部使用SetValue來設置值
8 SetValue(counterKey, 8);
9 }
10
11 // 屬性包裝器,只提供GetValue,你也可以設置一個private的SetValue進行限制。
12 public int Counter
13 {
14 get { return (int)GetValue(counterKey.DependencyProperty); }
15 }
16
17 // 使用RegisterReadOnly來代替Register來注冊一個只讀的依賴屬性
18 private static readonly DependencyPropertyKey counterKey =
19 DependencyProperty.RegisterReadOnly("Counter",
20 typeof(int),
21 typeof(MainWindow),
22 new PropertyMetadata(0));
23 }
```
對應的XAML代碼為:
```
<Window x:Class="ReadOnlyDP.MainWindow"
Name="ThisWin"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="ReadOnly Dependency Property" Height="350" Width="525">
<Grid>
<Viewbox>
<TextBlock Text="{Binding ElementName=ThisWin, Path=Counter}"/>
</Viewbox>
</Grid>
</Window>
```
此時Counter包裝的counterKey就是一個只讀依賴屬性,因為其定義為private的,所以在類外也不能使用DependencyObject.SetValue方法來對其值,而包裝的Counter屬性又只提供了GetValue方法,所以類外部只能對該依賴屬性進行讀取,而不能對其賦值。此時運行效果如下圖所示。

## 2.5 附加屬性
WPF中還有一類特殊的屬性——附加屬性。附加是一種特殊的依賴屬性。它允許給一個對象添加一個值,而該對象可能對這個值一無所知。附加屬性最常見的例子就是布局容器中DockPanel類中的Dock附加屬性和Grid類中Row和Column附加屬性。那問題又來了,我們怎樣在自己的類中定義一個附加屬性呢?其實定義附加屬性和定義一般的依賴屬性一樣沒什么區別,只是用RegisterAttached方法代替了Register方法罷了。下面代碼演示了附加屬性的定義。
```
public class AttachedPropertyClass
{
// 通過使用RegisterAttached來注冊一個附加屬性
public static readonly DependencyProperty IsAttachedProperty =
DependencyProperty.RegisterAttached("IsAttached", typeof(bool), typeof(AttachedPropertyClass),
new FrameworkPropertyMetadata((bool)false));
// 通過靜態方法的形式暴露讀的操作
public static bool GetIsAttached(DependencyObject dpo)
{
return (bool)dpo.GetValue(IsAttachedProperty);
}
public static void SetIsAttached(DependencyObject dpo, bool value)
{
dpo.SetValue(IsAttachedProperty, value);
}
}
```
在上面代碼中,IsAttached就是一個附加屬性,附加屬性沒有采用CLR屬性進行封裝,而是使用靜態SetIsAttached方法和GetIsAttached方法來存取IsAttached值。這兩個靜態方法內部一樣是調用SetValue和GetValue來對附加屬性讀寫的。
## 2.6 依賴屬性驗證和強制
在定義任何類型的屬性時,都需要考慮錯誤設置屬性的可能性。對于傳統的CLR屬性,可以在屬性的設置器中進行屬性值的驗證,不滿足條件的值可以拋出異常。但對于依賴屬性來說,這種方法不合適,因為依賴屬性通過SetValue方法來直接設置其值的。然而WPF有其代替的方式,WPF中提供了兩種方法來用于驗證依賴屬性的值。
* ValidateValueCallback:該回調函數可以接受或拒絕新值。該值可作為[DependencyProperty.Register](http://msdn.microsoft.com/zh-cn/library/ms597501(v=vs.110).aspx)方法的一個參數。
* CoerceValueCallback:該回調函數可將新值強制修改為可被接受的值。例如某個依賴屬性Age的值范圍是0到120,在該回調函數中,可以對設置的值進行強制修改,對于不滿足條件的值,強制修改為滿足條件的值。如當設置為負值時,可強制修改為0。該回調函數可作為[PropertyMetadata](http://msdn.microsoft.com/zh-cn/library/ms557328(v=vs.110).aspx)構造函數參數進行傳遞。
當應用程序設置一個依賴屬性時,所涉及的驗證過程如下所示:
1. 首先,CoerceValueCallback方法可以修改提供的值或返回[DependencyProperty.UnsetValue](http://msdn.microsoft.com/zh-cn/library/system.windows.dependencyproperty.unsetvalue(v=vs.110).aspx)。
2. 如果CoerceValueCallback方法強制修改了提供的值,此時會激活ValidateValueCallback方法進行驗證,如果該方法返回為true,表示該值合法,被認為可被接受的,否則拒絕該值。不像CoerceValueCallback方法,ValidateValueCallback方法不能訪問設置屬性的實際對象,這意味著你不能檢查其他屬性值。即該方法中不能對類的其他屬性值進行訪問。
3. 如果上面兩個階段都成功的話,最后會觸發PropertyChangedCallback方法來觸發依賴屬性值的更改。
下面代碼演示了基本的流程。
```
1 class Program
2 {
3 static void Main(string[] args)
4 {
5 SimpleDPClass sDPClass = new SimpleDPClass();
6 sDPClass.SimpleDP = 2;
7 Console.ReadLine();
8 }
9 }
10
11 public class SimpleDPClass : DependencyObject
12 {
13 public static readonly DependencyProperty SimpleDPProperty =
14 DependencyProperty.Register("SimpleDP", typeof(double), typeof(SimpleDPClass),
15 new FrameworkPropertyMetadata((double)0.0,
16 FrameworkPropertyMetadataOptions.None,
17 new PropertyChangedCallback(OnValueChanged),
18 new CoerceValueCallback(CoerceValue)),
19 new ValidateValueCallback(IsValidValue));
20
21 public double SimpleDP
22 {
23 get { return (double)GetValue(SimpleDPProperty); }
24 set { SetValue(SimpleDPProperty, value); }
25 }
26
27 private static void OnValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
28 {
29 Console.WriteLine("當值改變時,我們可以做的一些操作,具體可以在這里定義: {0}", e.NewValue);
30 }
31
32 private static object CoerceValue(DependencyObject d, object value)
33 {
34 Console.WriteLine("對值進行限定,強制值: {0}", value);
35 return value;
36 }
37
38 private static bool IsValidValue(object value)
39 {
40 Console.WriteLine("驗證值是否通過,返回bool值,如果返回True表示驗證通過,否則會以異常的形式暴露: {0}", value);
41 return true;
42 }
43 }
```
其運行結果如下圖所示:

從運行結果可以看出,此時并沒有按照上面的流程先Coerce后Validate的順序執行,這可能是WPF內部做了一些特殊的處理。當屬性被改變時,首先會調用Validate來判斷傳入的value是否有效,如果無效就不繼續后續操作。并且CoerceValue后面并沒有運行ValidateValue,而是直接調用PropertyChanged。這是因為CoerceValue操作并沒有強制改變屬性的值,而前面對這個值已經驗證過了,所以也就沒有必要再運行Valudate方法來進行驗證了。但是如果在Coerce中改變了Value的值,那么還會再次調用Valudate操作來驗證值是否合法。
## 2.7 依賴屬性的監聽
我們可以用兩種方法對依賴屬性的改變進行監聽。這兩種方法是:
* 使用[DependencyPropertyDescriptor](http://msdn.microsoft.com/zh-cn/library/system.componentmodel.dependencypropertydescriptor(v=vs.110).aspx)類
* 使用[OverrideMetadata](http://msdn.microsoft.com/zh-cn/library/ms597491(v=vs.110).aspx)的方式。
下面分別使用這兩種方式來實現下對依賴屬性的監聽。
第一種方式:定義一個派生于依賴屬性所在的類,然后重寫依賴屬性的元數據并傳遞一個PropertyChangedCallback參數即可,具體的實現如下代碼所示:
```
1 public class MyTextBox : TextBox
2 {
3 public MyTextBox()
4 : base()
5 {
6 }
7
8 static MyTextBox()
9 {
10 //第一種方法,通過OverrideMetadata
11 TextProperty.OverrideMetadata(typeof(MyTextBox), new FrameworkPropertyMetadata(new PropertyChangedCallback(TextPropertyChanged)));
12 }
13
14 private static void TextPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args)
15 {
16 MessageBox.Show("", "Changed");
17 }
18 }
```
第二種方法:這個方法更加簡單,獲取DependencyPropertyDescriptor并調用AddValueChange方法為其綁定一個回調函數。具體實現代碼如下所示:
```
public MainWindow()
{
InitializeComponent();
//第二種方法,通過OverrideMetadata
DependencyPropertyDescriptor descriptor = DependencyPropertyDescriptor.FromProperty(TextBox.TextProperty, typeof(TextBox));
descriptor.AddValueChanged(tbxEditMe, tbxEditMe_TextChanged);
}
private void tbxEditMe_TextChanged(object sender, EventArgs e)
{
MessageBox.Show("", "Changed");
}
```
## 三、總結
到這里,依賴屬性的介紹就結束了。WPF中的依賴屬性通過一個靜態只讀字段進行定義,并且在靜態構造函數中進行注冊,最后通過.NET傳統屬性進行包裝,使其使用與傳統的.NET屬性并無兩樣。在后面一篇文章將分享WPF中新的事件機制——路由事件。
本文所有源碼下載:[DependencyPropertyDemo.zip](http://files.cnblogs.com/zhili/DependencyPropertyDemo.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 領域驅動設計實戰系列總結