« 独自のプロパティエディタ作成(ドロップダウン形式) | トップページ | 「フォルダの参照」ダイアログのコントロールID »

2010年9月28日 (火)

自作コントロールを使い易くする(アイコン指定)

汎用的な自作コントロールを作成したら、ツールボックスに登録して使いたい筈だ。アイコンを指定せずに、自作コントロールを使い易くする(説明表示系)で使用した、CurrencyTextBox コントロールを登録してみる。

ツールボックスへの登録を行うと、VS2003なら、 VS2005なら、 と登録される筈だ。この様に、特にアイコンを指定せずとも、デフォルトのアイコンが表示される。

ではまず、既存のコントロールと同じアイコンを表示させる方法を示します。

登録するコントロールのクラス定義の直前で以下の行を加えます。
[ToolboxBitmap(typeof(指定したい既存のコントロールクラス名))]
例えば、TextBox コントロールのアイコンを CurrencyTextBox コントロールのアイコンとして使う場合は、以下のようにします。

/// <summary>
/// 金額入力用の TextBox クラスです。
/// </summary>
[ToolboxBitmap(typeof(TextBox))]        // TextBox のアイコンと同じにする場合の指定
public class CurrencyTextBox : TextBox
{
    ...
}

これでアイコン指定は終了です。ツールボックスの表示も、 となります。何とも簡単ですね。

では、独自のアイコンを指定するにはどうすればいいでしょうか。

例えば、msdn Online = 10 行シリーズ(番外編) …(中略)…Day 1 - ボタンコントロールのカスタマイズでは、後半部分にツールボックスへ独自アイコンの表示の仕方を解説しています。最初から全て指示通りにしてれば、問題無くアイコン登録はうまくいくでしょう。ですが、プロジェクト名を指示通りの MyCustomControl でなく、別の名前で1から作成したりすると、アイコン登録がうまくいかなくなります。

ここに落とし穴(しかも結構深い)があるのです。

まずは、このボタンコントロール GradientButton を例にとる事とし、ソリューション エクスプローラを見てみましょう。
GradientButton.cs がこのコントロール定義本体のコードを記述したファイルです。ここでは、namespace に MyCustomControl が指定されているので、GradientButton は、名前空間 MyCustomControl に所属している事が判ります。では、GradientButton.bmp は、どの名前空間に所属しているのでしょうか?
答えは、「規定の名前空間」に所属しています。この場合、プロジェクトのプロパティ(下図参照)から、規定の名前空間が MyCustomControl である事が判ります。もちろん、名前の変更は可能ですが、新規作成時点ではプロジェクト名と同じ名前になる様です。

さて、もしプロジェクト名を Acha_yaControl として新規作成して、これ以外は GradientButton のコントロール作成の指示通りにしていたとしたら、どうなるでしょうか。規定の名前空間は、プロジェクト名と同名になっているので、Acha_yaControl となります。しかし、GradientButton.cs 内の名前空間は、MyCustomControl のままです。

サンプル通りの指定では、アイコン登録が行われず、また何かエラーを出す訳でもないので情報が得られず、途方に暮れる事になります。

ここで、アイコン登録の指定部分は、

[System.Drawing.ToolboxBitmap(typeof(GradientButton), "GradientButton.bmp")]

となっていました。ToolboxBitmapAttribute のコンストラクタの説明では、「namespace.name という名前の埋め込みリソースを検索する」とあります。namespaceは、typeof を指定したクラスの名前空間 MyCustomControl を表わし、name は、第2パラメータである"GradientButton.bmp"を表わします。すなわち、MyCustomControl.GradientButton.bmp という埋め込みリソースを探す事になります。ところが、埋め込みリソース自体は、Acha_yaControl.GradientButton.bmp の名前で登録されているので、見つからない事になってしまいます。
つまり、この指定ではアイコン登録しようとするコントロールの名前空間と、規定の名前空間は一致していないと、アイコンの登録が出来ない事になります。

ところで、ToolboxBitmapAttribute は、パラメータを省略して、

[System.Drawing.ToolboxBitmap(typeof(GradientButton)]    // type のみの指定

または、

[System.Drawing.ToolboxBitmap("GradientButton.bmp")]    // string のみの指定

といった指定も可能です。これらの指定では、名前空間が一致していない場合に有効でしょうか?

ToolboxBitmapAttribute のコンストラクタの説明では、「type 指定のみの場合、namespace.classname.bmp を検索する」とあります。すなわちこの例では、MyCustomControl.GradientButton.bmp を検索する事になり、既定の名前空間が、Acha_yaControl である限り、この指定でアイコン登録は無理そうです。
では、string のみの指定ではどうでしょう。説明では、「string 指定のみの場合、埋め込みリソースのビットマップでなく、外部ファイルとして存在するビットマップファイルを検索する」とあります。そしてそれは、アセンブリと同じディレクトリにビットマップがあるものとして検索する様です。また、相対パスや絶対パスによる指定も可能です。
[System.Drawing.ToolboxBitmap(@"..\..\GradientButton.bmp")]   // 一般的な開発環境では、ソースの存在するディレクトリにある GradientButton.bmp ファイルをアイコンとして使用
[System.Drawing.ToolboxBitmap(@"C:\GradientButton.bmp")]    // Cドライブ直下のGradientButton.bmp をアイコンとして使用

従って、この指定ではとりあえず名前空間が一致していない場合でも指定が可能となります。但し、ツールボックスに登録しようとする時点で、アイコンとなるべきビットマップファイルが存在していないと、今度は右図の様なエラーが出て、登録すら出来ません。それに DLLファイルとセットで配布する必要も出て来ます。埋め込みリソースが使えない欠点が出て、ちょっと使いにくそうです。

結論として、ToolboxBitmapAttribute の指定は、アイコン登録しようとするコントロールの名前空間と、規定の名前空間は一致しなくても良いが、一致していないと使いにくい事が導き出される。

従って、ここまでの説明で、アイコン登録しようとするコントロールの名前空間と、規定の名前空間は一致させた上で、

[System.Drawing.ToolboxBitmap(typeof(GradientButton)]

または、

[System.Drawing.ToolboxBitmap(typeof(GradientButton), "GradientButton.bmp")]

という指定を行えば、アイコン登録は問題無くできそうである。実際、GradientButton の例ではこの指定で問題無く登録出来た。これで、説明終わりといいたいところなのだが、実はまだこれだけでは済まないのである。

私の自作コントロールの中には、これでもアイコン登録がうまくいかず、デフォルトのアイコンが表示されたり、デバッグ時点ではうまく登録出来ていたのに、リリース用に別プロジェクトを作成して再コンパイルした環境ではうまく登録できないといった現象も起きているのである。もちろん、上記のポイントは踏まえてあり、問題ない筈である。この調査を行っていたとき、VSUGフォーラムのカスタムコントロール:ツールボックス上のアイコン変更の反映 に、以下の様な記述を行なう様に書いてあった。

[System.Drawing.ToolboxBitmap(typeof(GradientButton), "MyCustomControl.GradientButton.bmp")]

プロジェクトに追加するビットマップファイル自体は、前と変わらず GradientButton.bmp のままである。これでは少なくとも、私自身が最初に説明してきた内容と矛盾する事になる。この指定では、MyCustomControl.MyCustomControl.GradientButton.bmp の埋め込みリソースを検索する事になってしまうからだ。しかし、現実にはこの指定で、先程のうまく登録出来なかった自作コントロールは、うその様にアイコンが登録されていった。

なぜかは判らない。しかし上記指定でも、実際には MyCustomControl.GradientButton.bmp の埋め込みリソースを検索する様で、元々アイコン登録がうまく出来ていた GradientButton コントロールは、この指定でも問題無くアイコン登録が出来た。

従って、アイコン登録が出来なかった場合は、この様な記述で対処出来る事は間違いない様だ。

« 独自のプロパティエディタ作成(ドロップダウン形式) | トップページ | 「フォルダの参照」ダイアログのコントロールID »

C# Tips」カテゴリの記事

コメント

コメントを書く

コメントは記事投稿者が公開するまで表示されません。

(ウェブ上には掲載しません)

トラックバック


この記事へのトラックバック一覧です: 自作コントロールを使い易くする(アイコン指定):

« 独自のプロパティエディタ作成(ドロップダウン形式) | トップページ | 「フォルダの参照」ダイアログのコントロールID »