コントロールデザイナ

Last Updated 2011/09/21


コントロールデザイナとは Visual Studio などのビジュアルな開発環境において、フォームやコントロールをビジュアルに設計できるようにする仕組みです。

独自のコントロールを作成した場合でも標準のコントロールデザイナでたいていの場合は間に合うと思います。もし必要なら、独自のコントロールデザイナを作成し、コントロールに設定することも可能です。

このページでは、コントロールデザイナの概要について説明します。合わせて、スマートタグを設定する手順についても触れます。なお、コントロールデザイナの中心に位置するクラスは ControlDesigner クラスです。

私が公開している .Net Framework クラスライブラリリファレンスを見ながらサンプルコードを見ると、より分かりやすいと思います。


コントロールデザイナに関係する Visual Studio のヘルプ項目

コントロールデザイナに関する記事は次の項目の中にあります。

  開発ツールと言語ドキュメント
  . Visual Studio ドキュメント
  ... Windows ベースのアプリケーション、コンポーネント、サービス
  ..... Visual Studio のコンポーネント
  ....... デザイン時サポートの拡張

この中の「カスタムデザイナ」にコントロールデザイナに関係するクラスなどの説明があります。例によって、分かりやすいわけではありませんが、手がかりにはなるでしょう。

デザインモードでコントロールの外観と動作を拡張する

上記で説明した「カスタムデザイナ」の項目の中に、「方法:デザインモードでコントロールの外観と動作を拡張する」があります。この項目に収録されているサンプルコードがコントロールデザイナの基本的な作り方のほとんどをカバーしていますので、解説用として取り上げてみました。

以下は、サンプルコードを日本化したものです。基本的にはサンプルコードにならいましたが、コードを読みやすくするための変更はしています。

Note クラスのメンバ変数とローカル変数とを区別するために、私はクラスのメンバ変数にはフィールド "Field" をあらわす "F" を付けています。"m_" を使う人もいますが、2 文字より 1 文字のほうがいいのではないでしょうか。最初は "F" を付けることに抵抗があるかもしれませんが慣れです。私もそうでした。少なくとも、メンバ変数とローカル変数とは見ただけで区別できるようにすべきです。

ControlDesignerTest.lzh (4,722 bytes)

この LZH ファイルにはコントロールを定義するクラスだけを含みます。このコントロールをテストする手順を説明します。

  1. Windows フォームアプリケーション用プロジェクトを起こす
  2. clsControlDesignerTest.cs をプロジェクトに追加する
  3. [プロジェクト]-[参照の追加] で System.Design への参照を追加する
  4. [ビルド]-[ソリューションのビルド] を実行する
  5. ツールボックスに追加された DemoControl コントロールをフォームに貼り付ける

下図の左は DemoControl をフォームに貼り付けたときの状態を、中央は Margin プロパティを 8 に、Padding プロパティを 10 に設定したところ、右はスマートタグを開いたところです。スマートタグとは左図の右上隅にある小さな右向き矢印をクリックするとポップアップするダイアログボックスです。

ControlDesigner1 ControlDesigner3 ControlDesigner2

また、左図の境界矩形の 4 辺にある小さな矩形はコントロールの Anchor プロパティの設定状況を示します。矩形の内部を赤色で塗りつぶしたものは Anchor プロパティの設定があるもので、白抜きのものは Anchor プロパティの設定がないものです。この矩形内をマウスでダブルクリックするとその位置の Anchor プロパティを設定状態にすることができます。

中央の図の外側のフォーカス矩形(点線の矩形)と青色のコントロール端部とのスペースは Margin プロパティで設定、青色のコントロールの端部と内側のフォーカス矩形との間は Padding プロパティで設定します。ちなみに、Margin プロパティのデフォルト値は 4 辺とも 3 で、Padding プロパティのそれは 0 です。

スマートタグには Anchor プロパティと BackColor プロパティをリストアップするように設定してみました。

コントロールデザイナそのもののコードの説明は上記のクラスのコードの中に書いておきました。見たことがないクラスが多いので、読みにくいとは思いますが、こんなものかと見るだけでいいと思います。

コントロールデザイナの使い方

先にも触れましたが、コントロールデザイナをわざわざ作らなければならないケースはそう多くないと思います。しかし、コントロールを自作する場合はコントロールデザイナを使わなければならないケースもあります。

既存の Windows フォームコントロールから派生するコントロールを作成する場合、元のコントロールの public なプロパティはプロパティウインドウに表示されます。しかし、新しいコントロールでは使えないようにしたいプロパティがある場合は、そのプロパティを無効にするか、プロパティウインドウへの表示を抑制したいものです。

コンポーネントの作り方」のページで紹介する ColorComboBox クラスは ComboBox クラスから派生し、オーナー描画で 140 色の KnownColor 型を表示するものです。そこで、ComboBox クラスの DrawMode プロパティは DrawMode.OwnerDrawFixed にまた、DropDownStyle プロパティは  ComboBoxStyle.DropDownList に設定しています。これらのプロパティを無効にするわけにはいきませんが、プロパティウインドウには表示したくありません。また、いくつかのプロパティ(たとえば、AllowDrop プロパティや Sorted プロパティです)は使う可能性はありませんから無効にしてしまいたいところです。

まず、無効にはできないが、プロパティウインドウには表示したくないプロパティはどうすればいいか。ColorComboBoxEx クラスでは以下のようにしました。

  [Browsable(false)]
  public DrawMode DrawMode { get { return base.DrawMode; } set { base.DrawMode = value; } }

  [Browsable(false)]
  public ComboBoxStyle DropDownStyle { get { return base.DropDownStyle; } set { base.DropDownStyle = value; } }

つまり、あらためてプロパティを public 宣言して、BrowseableAttribute 属性で false を設定し、get および set アクセサは継承元のままとします。このテクニックは .Net Framework SDK のどこにも解説がないようですが、思いつきで試したところうまくいきました。

次に、プロパティそのものを無効にするテクニックですが、これはコントロールデザイナを使います。以下は、ColorComboBox クラスで使っているものです。

public class ColorComboBoxDesigner : System.Windows.Forms.Design.ControlDesigner
{
  public ColorComboBoxDesigner()
  {
  }

  protected override void PostFilterProperties(IDictionary properties)
  {
    // ColorComboBox クラスでは不要なプロパティを削除する
    properties.Remove("AllowDrop");
    properties.Remove("Sorted");

    base.PostFilterProperties(properties);
  }
} // end of ColorComboBoxDesigner class

ポイントは PostFilterProperties メソッドのオーバーライドです。このメソッドはデザイナ上のコントロール読み込んでプロパティウインドウにリストアップするときに .Net Framework が呼び出します。戻り値の properties はコントロールのプロパティを保持するディクショナリ(プロパティ名とその値の組み合わせのコレクション)ですから、不要なプロパティを Remove メソッドを使って削除します。この例では、AllowDrop プロパティと Sorted プロパティを削除しています。

ちなみに、 PreFilterProperties メソッドもありますが、これはコントロールにプロパティを追加するときに使います。しかし、使う機会はそう多くないと思います。

さて、コントロールデザイナクラスが出来たところで、コントロール本体に設定する手順を最後に説明します。

namespace emanual.Control
{
  [Designer(typeof(ColorComboBoxDesigner))]
  public class ColorComboBox : ComboBox
  {
    ....
  }
}

クラスの宣言部に DesignerAttribute 属性でコントロールデザイナクラスの型を指定します。

全体のコードは ColorComboBox クラスのソースコードを参照してください。

コンテナコントロール用コントロールデザイナ

コンテナコントロール、つまり、GroupBox コントロールや Panel コントロールのように、ほかのコントロールを内部に配置することを前提とするコントロールの場合は、ControlDesigner クラスから派生するコントロールデザイナでは役不足です。このような場合は ParentControlDesigner クラスから派生するコントロールデザイナを使わなければなりません。

ControlDesigner クラスと ParentControlDesigner クラスとの違いは、コントロールの親子関係を決めることができる点が決定的です。

以下は、UserControl から派生するコントロールに ParentControlDesigner クラスを関連付けたものです。ユーザーコントロール上に別のコントロールを配置することができるところを確認してください。

namespace emanual.Control
{
  [Designer(typeof(UserControlDesigner))]
  public partial class UserControl1 : UserControl
  {
    public UserControl1()
    {
      InitializeComponent();
    }

    private void UserControl1_Load(object sender, EventArgs e)
    {

    }

    //*************************************************************************************
    // UserControlDesigner class
    //*************************************************************************************
    public class UserControlDesigner : System.Windows.Forms.Design.ParentControlDesigner
    {
      public UserControlDesigner()
      {
      }
    } // end of UserControlDesigner class

  }  // end of UserControlDesigner1 class
} // end of namespace

スマートタグ

スマートタグについてはすでに「デザインモードでコントロールの外観と動作を拡張する」触れましたが、あらためて説明します。

コントロールデザイナにスマートタグを設定する方法は次の 2 種類があります。

「デザインモードでコントロールの外観と動作を拡張する」では最初の方法を使っていますので、ここでは 2 番目の方法について説明します。

さて、.Net Framework SDK の中にある「チュートリアル:Windows フォームコンポーネントへのスマートタグの追加」がありますが、これは最初の方法だけを使っています(正確にいうと、DesignerActionService オブジェクトをメンバ変数に取り込むところまではありますが、そのオブジェクトは使っていません)。そこで、DesignerActionService クラスの使い方を調べるため WEB サイトをあたったところ、参考になりそうなサンプルコードはないですね。

ともあれ、DesignerActionService クラスを使って、Label コントロールにスマートタグを付けるコードを以下に示します。

using System;
using System.Collections;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Drawing;
using System.Windows.Forms;
using System.Windows.Forms.Design;

namespace emanual.Control
{
  // これはテスト用のコントロールなので、Label コントロールのままとする
  [DesignerAttribute(typeof(SmartTagLabelDesigner))]
  class SmartTagLabel : Label
  {
    public SmartTagLabel()
    {
    }

    //*************************************************************************************
    // SmartTagLabelDesigner class
    //*************************************************************************************
    [System.Security.Permissions.PermissionSet(System.Security.Permissions.SecurityAction.Demand, Name = "FullTrust")]
    public class SmartTagLabelDesigner : ControlDesigner
    {
      private DesignerActionService FActionService = null;

      // コンストラクタ
      public SmartTagLabelDesigner()
      {
      }

      //-------------------------------------------------------------------------------------
      protected override void Dispose(bool disposing)
      {
        base.Dispose(disposing);

        if (FActionService != null)
          FActionService.Dispose();
      }

      //-------------------------------------------------------------------------------------
      // コントロールデザイナを初期化するときに呼び出されるメソッド
      public override void Initialize(IComponent component)
      {
        base.Initialize(component);

        // DesignerActionService オブジェクトを取得する
        FActionService = this.GetService(typeof(DesignerActionService)) as DesignerActionService;

        // SmartTagActionList オブジェクトを追加する
        FActionService.Add(this.Component, new SmartTagActionList(this.Component));
      }
    } // end of SmartTagDesigner class

    //***************************************************************************************
    // SmartTagActionList class
    //***************************************************************************************
    public class SmartTagActionList : System.ComponentModel.Design.DesignerActionList
    {
      private SmartTagLabel FRelatedControl;

      //-------------------------------------------------------------------------------------
      // コンストラクタ
      public SmartTagActionList(IComponent component) : base(component)
      {
        FRelatedControl = component as SmartTagLabel;
      }

      //-------------------------------------------------------------------------------------
      // スマートタグに追加する Anchor プロパティ
      public AnchorStyles Anchor
      {
        get { return FRelatedControl.Anchor; }
        set
        {
          PropertyDescriptor pd = TypeDescriptor.GetProperties(FRelatedControl)["Anchor"];
          pd.SetValue(FRelatedControl, value);
        }
      }

      //-------------------------------------------------------------------------------------
      // スマートタグに追加する BackColor プロパティ
      public Color BackColor
      {
        get { return FRelatedControl.BackColor; }
        set
        {
          PropertyDescriptor pd = TypeDescriptor.GetProperties(FRelatedControl)["BackColor"];
          pd.SetValue(FRelatedControl, value);
        }
      }

      //-------------------------------------------------------------------------------------
      // このメソッドは .Net Framework が呼び出すもので、スマートタグのリスト項目の
      // コレクションを返す
      public override DesignerActionItemCollection GetSortedActionItems()
      {
        DesignerActionItemCollection items = new DesignerActionItemCollection();

        // ヘッダー項目を追加する
        items.Add(new DesignerActionHeaderItem("配置", "Layout"));
        items.Add(new DesignerActionHeaderItem("表示", "Appearance"));

        // Anchor プロパティに対する DesignerActionPropertyItem オブジェクトを追加する
        items.Add(new DesignerActionPropertyItem("Anchor", "Anchor プロパティ", "Layout"));

        // BackColor プロパティに対する DesignerActionPropertyItem オブジェクトを追加する
        items.Add(new DesignerActionPropertyItem("BackColor", "BackColor プロパティ", "Appearance"));

        items.Add(new DesignerActionTextItem("配置カテゴリの説明文", "Layout"));

        return items;
      }
    } // end of SmartTagActionList class
  } // end of SmartTagLabel class
} // end of namespace

 このクラスをテストする手順は以下のとおり。

  1. 上記のコード部をファイル "clsSmartTagLabel.cs" として保存する
  2. Windows フォームアプリケーション用プロジェクトを起こす
  3. clsSmartTagLable.cs をプロジェクトに追加する
  4. [プロジェクト]-[参照の追加] で System.Design への参照を追加する
  5. [ビルド]-[ソリューションのビルド] を実行する
  6. ツールボックスに追加された SmartTagLabel コントロールをフォームに貼り付ける

これでフォーム上に SmartTagLabel コントロールが配置された状態になりますから、コントロールにフォーカスを与えるとスマートタグが表示されます。スマートタグをクリックするとスマートタグパネルが表示されます。

なお、この例のスマートタグの内容は「デザインモードでコントロールの外観と動作を拡張する」と同じです。

スマートタグパネル内にアクション(動詞)を設定、またコンテキストメニューにメニュー項目を追加する

下図は、PictureBox コントロールのスマートタグを開いたところです。

SmartTag

この中の [イメージの選択] をクリックすると、イメージを選択するダイアログボックスがオープンします。また、図はつけませんでしたが、PictureBox コントロール内をマウスで右クリックすると表示されるコンテキストメニュー内に [イメージの選択] のメニュー項目が追加されます。これはコントロールデザイナで DesignerVerb オブジェクトを設定することで実現できます。

以下のコードは、DesignerVerb オブジェクトを設定したコントロールデザイナを使う Label コントロールです。

using System;
using System.Collections;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Drawing;
using System.Windows.Forms;
using System.Windows.Forms.Design;

namespace emanual.Control
{
  // テスト用のコントロールなので、Label コントロールのままとする
  [DesignerAttribute(typeof(DesignerVerbLabelDesigner))]
  class DesignerVerbLabel : Label
  {
    public DesignerVerbLabel()
    {
    }

    //*************************************************************************************
    // DesignerVerbLabelDesigner class
    //*************************************************************************************
    [System.Security.Permissions.PermissionSet(System.Security.Permissions.SecurityAction.Demand, Name = "FullTrust")]
    public class DesignerVerbLabelDesigner : ControlDesigner
    {
      private DesignerVerbCollection FVerbs;

      // コンストラクタ
      public DesignerVerbLabelDesigner()
      {
      }

      //-------------------------------------------------------------------------------------
      // このプロパティのオーバーライドがあれば .Net Framework が読み込む
      public override DesignerVerbCollection Verbs
      {
        get
        {
          if (FVerbs == null)
          {
            FVerbs = new DesignerVerbCollection();

            FVerbs.Add(new DesignerVerb("アクション1", new EventHandler(OnFirstItemSelected)));
           FVerbs.Add(new DesignerVerb("アクション2", new EventHandler(OnSecondItemSelected)));
          }

          return FVerbs;
        }
      }

      //-------------------------------------------------------------------------------------
      private void OnFirstItemSelected(object sender, EventArgs args)
      {
        MessageBox.Show("アクション1を起動します。");
      }

      //-------------------------------------------------------------------------------------
      private void OnSecondItemSelected(object sender, EventArgs args)
      {
        MessageBox.Show("アクション2を起動します。");
      }

      //-------------------------------------------------------------------------------------
    } // end of DesignerVerbLabelDesigner class
  } // end of DesignerVerbLabel class
} // end of namespace

このクラスをテストする手順は「スマートタグ」の項で説明した "clsSmartTagLabel.cs" の場合と同じです。今回の場合は、上記のコード部を "clsDesignerVerbLabel.cs" として保存してください。

−以上−