為什麼要設置默認值?
- 屬性網格視覺提示:非默認值以**粗體**顯示,開發者一眼識別已修改項。
- 右鍵重置功能:屬性網格右鍵菜單提供「重置」,一鍵恢復默認值。
- 序列化優化:設計器生成代碼時,**僅序列化非默認值屬性**,大幅減少生成代碼體積,提升載入效能。
兩種實現機制
| 機制 | 適用類型 | 實現方式 | 優點 | 缺點 |
| DefaultValueAttribute | 基本類型、枚舉、字串、typeof 等編譯期常量 | 屬性聲明前加 [DefaultValue("Hello")] | 極簡單、聲明式、編譯器可識別 | 無法用於引用類型實例 (如 Color、Font、陣列、自定義類) |
| Reset/ShouldSerialize 模式 | **任意類型** (引用類型、結構體、複雜對象) | 提供 ResetPropertyName() 和 ShouldSerializePropertyName() 方法 | 通用、運行時動態判斷、支援複雜邏輯 | 需手寫方法、依賴「魔術命名約定」 |
代碼實戰:混合使用兩種模式
using System;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;
namespace CustomControlSample {
public class FirstControl : Control {
// 1. 簡單類型:字串 - 用 DefaultValueAttribute
private string _displayText = "Hello World!"; // 必須初始化為默認值!
[Browsable(true)]
[DefaultValue("Hello World!")] // 編譯期常量
public string DisplayText {
get => _displayText;
set { _displayText = value; Invalidate(); }
}
// 2. 複雜類型:Color - 用 Reset/ShouldSerialize 模式
private Color _textColor = Color.Red; // 初始化默認值
[Browsable(true)]
// 無 DefaultValueAttribute!
public Color TextColor {
get => _textColor;
set { _textColor = value; Invalidate(); }
}
// 魔術方法:重置為默認值
public void ResetTextColor() => TextColor = Color.Red;
// 魔術方法:是否需要序列化 (非默認值才序列化)
public bool ShouldSerializeTextColor() => TextColor != Color.Red;
// 3. 枚舉類型:ContentAlignment - DefaultValueAttribute 完美支援
private ContentAlignment _alignment = ContentAlignment.MiddleLeft;
[Category("Alignment")]
[Description("Specifies the alignment of text.")]
[DefaultValue(ContentAlignment.MiddleLeft)]
public ContentAlignment TextAlignment {
get => _alignment;
set { _alignment = value; Invalidate(); }
}
protected override void OnPaint(PaintEventArgs e) {
base.OnPaint(e);
var format = new StringFormat();
switch (_alignment) {
case ContentAlignment.MiddleLeft: format.Alignment = StringAlignment.Near; break;
case ContentAlignment.MiddleRight: format.Alignment = StringAlignment.Far; break;
case ContentAlignment.MiddleCenter: format.Alignment = StringAlignment.Center; break;
}
e.Graphics.DrawString(DisplayText, Font, new SolidBrush(TextColor), ClientRectangle, format);
}
}
}
關鍵細節總結
- 字段初始化必須與默認值一致:
private string _displayText = "Hello World!";、private Color _textColor = Color.Red;。否則運行時初始狀態與設計時默認值不符,導致序列化邏輯混亂。
- DefaultValueAttribute 參數必須是編譯期常量:字面量、
const、typeof、enum 值。不能傳 Color.Red、new Font(...)、new int[] {1,2}。
- Reset/ShouldSerialize 方法必須為 public、無參、命名精確:
Reset + PropertyName、ShouldSerialize + PropertyName。大小寫敏感,屬性名稱需完全匹配。
- ShouldSerialize 返回 true = 需序列化 = 非默認值;返回 false = 默認值 = 不序列化。邏輯勿反。
- 兩種機制可混用:簡單類型用 Attribute,複雜類型用模式方法,互不衝突。
進階:自定義類型的默認值
public class Scope { public int Min { get; set; } public int Max { get; set; } }
public class MyControl : Control {
private Scope _scope = new Scope { Min = 0, Max = 100 }; // 默認實例
[Browsable(true)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)] // 內容序列化
public Scope Scope {
get => _scope;
set { _scope = value ?? new Scope { Min = 0, Max = 100 }; Invalidate(); }
}
public void ResetScope() => Scope = new Scope { Min = 0, Max = 100 };
public bool ShouldSerializeScope() => Scope.Min != 0 || Scope.Max != 100;
}
DesignerSerializationVisibility.Content 讓設計器序列化對象的**屬性內容**而非對象引用。
- Reset 方法創建新的默認實例;ShouldSerialize 比較關鍵屬性。
驗證效果
- 編譯控件庫,新建 WinForms 專案引用,拖入控件。
- 屬性網格中:
DisplayText、TextAlignment 顯示正常字體 (默認值);TextColor 同理。
- 修改
TextColor 為 Blue → 變**粗體**,右鍵有「重置」選項,點擊恢復 Red 且變正常字體。
- 切換到代碼視圖 (
Form1.Designer.cs):
- 未修改屬性 → **無任何代碼生成**。
- 修改 TextColor → 生成 this.firstControl1.TextColor = System.Drawing.Color.Blue;。
- 重置後 → 該行代碼**自動刪除**。