アプリケーション開発において、各画面で意匠に統一性を持たせることは要求仕様なることが多いと思います。例えばラベルの色を統一することもその一つでしょう。多くの画面で使用するすべてのラベルについて、プロパティウィンドウで色を設定していく作業は結構な手間になります。また、別の色に変えたいときにも同じ作業が発生してしまいます。Webサイトの意匠設計ではスタイルシートを使って色などを一カ所で設定することが可能になります。このようなことができないでしょうか。

NET Frameworkでは出来合いのコントロールから継承して新たなカスタムコントロールを作ることができます。そこでカスタムコントロールのコンストラクタでBackColorプロパティに望みの色を設定するようにすれば良さそうです。しかしそれだけではコンストラクタとInitializeComponentメソッドで2回設定され無駄が生じますしIDE操作の上でも望ましいと言えません。これを解決するためにはBackColorプロパティにDefaultValue属性を設定します。

属性による既定値設定

Public Class MyLabelDesigner
  Inherits System.Windows.Forms.Design.ControlDesigner

  Public Overrides Sub InitializeNewComponent(ByVal defaultValues As IDictionary)
    MyBase.InitializeNewComponent(defaultValues)
    With Cast(Of MyLabel)(Control)
      .AutoSize = False
      .Text = Nothing
    End With
  End Sub

  Private Sub SetDefaultValue(Of componentType, propertyType) _
    (ByVal properties As IDictionary, ByVal propertyName As String, ByVal value As String)
    properties(propertyName) = TypeDescriptor.CreateProperty(GetType(componentType), _
      Cast(Of PropertyDescriptor)(properties(propertyName)), _
      New DefaultValueAttribute(GetType(propertyType), value))
  End Sub

  Protected Overrides Sub PreFilterProperties(ByVal properties As IDictionary)
    MyBase.PreFilterProperties(properties)
    SetDefaultValue(Of MyLabel, Color)(properties, "BackColor", "FloralWhite")
    SetDefaultValue(Of MyLabel, BorderStyle)(properties, "BorderStyle", "Fixed3D")
  End Sub
End Class

<Designer(GetType(MyLabelDesigner))> _
Public Class MyLabel
  Inherits Label

  Public Sub New()
    MyBase.New()
    BackColor = Color.FloralWhite
    BorderStyle = BorderStyle.Fixed3D
  End Sub
End Class

素直にDefaultValue属性を設定する例はMSDNを参照してください。この方法をそのまま当てはめた場合の問題は属性を設定するためにわざわざプロパティをオーバーライドまたはシャドウしなければならないことです。属性が必要なのはデザイン時だけですから別の方法で属性を設定する方法を考えてみました。

MyLabelのBackColorプロパティとBorderStyleプロパティにDefaultValue属性を設定するのにMyLabelDesignerクラスを使用します。MyLabel本体はコンストラクタのオーバーライドだけになっています。SetDefaultValueはMyLabelDesignerのメンバーとしていますが汎用的に作ってあるので他のModuleのPublicメソッドにしても問題有りません。Castメソッドはジェネリックのページで例示した物ですが一般的にはCTypeを使えばよいでしょう。上記程度のサンプルにはジェネリックを使う必要はありませんがそのまま残してあります。InitializeNewComponentはなくてもかまいませんが、Textプロパティにコントロール名が設定されるのは便利とは言い難いので空白になるようにしているのとAutoSizeがTrueになることを防いでいます。サンプルコードを試される方はSystem.Design.dllへの参照をプロジェクトに追加してください。

MSDNを見るとメモ : このメソッドは、.NET Framework version 2.0 で新しく追加されたものです。と書いてあるのを見かけます。カスタムコンポーネントを作っていると新しく追加されたメソッドをよく使っています。version 1.Xを使っていたら結構不便に思ったかもしれないです。