Open棟梁Project - マイクロソフト系技術情報 Wiki
[[Open棟梁Project>http://opentouryo.osscons.jp/]] - [[マイクロソフト系技術情報 Wiki>http://techinfoofmicrosofttech.osscons.jp/]]

* 目次 [#xe7a23df]
#contents

*概要 [#a6c161e7]
.NET Frameworkでは、画面の開発に必要となるコントロールが用意されている。

.NETでは、これらのコントロールの動作をカスタマイズすることで、特定プロジェクト向けにカスタマイズされた~
(例えば、自動編集処理、自動入力チェック処理、サニタイジング処理、などの機能を有した)コントロール共通部品開発が可能である。

ここでは、この.NETコントロール([[ASP.NET Web Forms]], [[Windows Forms]])のカスタマイズ方法について説明する。

*[[ASP.NET Web Forms]] [#s5af500d]
[[ASP.NET Web Forms]]で使用するコントロールの特徴は、Webアプリケーションであるために、

-表示はHTMLにより行われるのでカスタマイズしやすい。
-ただし、状態の持ち回りについて考慮する必要がある。

という点である。

**クラス定義 [#t4162e0b]
以下は、ラベル コントロール(System.Web.UI.WebControls.Label)を継承したカスタム ラベル コントロール(Ctrl.WebCustLabel)のクラス定義。

 [assembly: TagPrefix("Ctrl", "my_wcc")]
 namespace Ctrl {
   /// <summary>System.Web.UI.Labelのカスタム・コントロール</summary>
   [DefaultProperty("Text"),
   ToolboxData("<{0}:WebCustLabel runat=server></{0}:WebCustLabel>")]
   public class WebCustLabel : Label

このクラス定義に含まれる「[[ToolboxData>http://msdn.microsoft.com/ja-jp/library/system.web.ui.toolboxdataattribute.aspx]]」属性は、カスタム コントロールに必要な属性で、Visual Studio(以下、VSと略す)のツールボックスからドラッグ&ドロップされるときに生成される、カスタム タグを表す文字列を指定する。また、「[[DefaultProperty>http://msdn.microsoft.com/ja-jp/library/system.componentmodel.defaultpropertyattribute.aspx]]」属性は既定のプロパティを指定するオプション属性である。これ以外に、(クラス定義には含まれないが)名前空間定義に含まれる「[[TagPrefix>http://msdn.microsoft.com/ja-jp/library/system.web.ui.tagprefixattribute.tagprefix.aspx]]」 属性により「ToolboxData」属性の「{0}」に設定されるデフォルトの文字列(タグ プレフィックス)を指定できる。この属性もオプション属性である。

**コンストラクタ [#r24d7631]
コンストラクタについては自由な利用が可能である。

ここではスタイル関係のプロパティに初期値を設定。

 /// <summary>コンストラクタ</summary>
 /// <summary>コンストラクタでプロジェクトなどでの標準スタイルを適用する。</summary>
 public WebCustLabel()
 {
   // 初期設定のプロパティ値を設定する。
   // ※ デザインタイム・プロパティのほうが優先される。
   this.Font.Size = 12;
   this.ForeColor = System.Drawing.Color.Red;
   this.Font.Name = "MS ゴシック";
 }

***VSデザイナとコンストラクタ [#lc492ffc]
コントロールのコンストラクタは、VSデザイナ上で機能するアーキテクチャとなっている。

このため、下記のようなコンストラクタの実装によって、選択項目1~3を生成するカスタムのRadioButtonListコントロールを作成した場合、

 /// <summary>コンストラクタ</summary>
 /// <summary>コンストラクタでプロジェクトなどでの標準スタイルを適用する。</summary>
 public WebCustRadioButtonList() {
   // 初期設定のプロパティ値を設定する。
   // ※デザインタイム・プロパティのほうが優先される。
 
   // WebCustRadioButtonListの初期化(データバインド)
   string[] itemlist = { "選択項目1", "選択項目2", "選択項目3" };
   this.DataSource = itemlist;
   this.DataBind();
 
   // 横方向にオプションボタンを並べて表示する。
   this.RepeatDirection = RepeatDirection.Horizontal;
 }

カスタムのRadioButtonListコントロールのタグ内に、子コントロール(ListItem)のコレクションのタグが出力される。

 <my_wcc:WebCustRadioButtonList ID="WebCustRadioButtonList1" runat="server" Width="340px">
   <asp:ListItem Value="選択項目1">選択項目1</asp:ListItem>
   <asp:ListItem Value="選択項目2">選択項目2</asp:ListItem>
   <asp:ListItem Value="選択項目3">選択項目3</asp:ListItem>
 </my_wcc:WebCustRadioButtonList>

この実装の状態で、VSデザイナでコントロールのサイズを変更したところ、VSデザイナ上での表示が下記のような、意図せぬ表示となる問題が発生する。

 <my_wcc:WebCustRadioButtonList ID="WebCustRadioButtonList1" runat="server" Width="340px">
   <asp:ListItem Value="選択項目1">選択項目1</asp:ListItem>
   <asp:ListItem Value="選択項目2">選択項目2</asp:ListItem>
   <asp:ListItem Value="選択項目3">選択項目3</asp:ListItem>
   <asp:ListItem Value="選択項目1">選択項目1</asp:ListItem>
   <asp:ListItem Value="選択項目2">選択項目2</asp:ListItem>
   <asp:ListItem Value="選択項目3">選択項目3</asp:ListItem>
 </my_wcc:WebCustRadioButtonList>

***上記問題への対策 [#r3d2e027]
-処理の移動
--コンストラクタに実装したデータバインド処理を、ロード イベントに移動する。
--ロード イベントにデータバインド処理を移動すると、~
今度は、デザイン時に限り表示が「バインドなし」という状態になってしまう。

-VSデザイン サポートによる処置~
VSデザイン サポート コードを、[[ControlDesignerクラス>http://msdn.microsoft.com/ja-jp/library/system.web.ui.design.controldesigner.aspx]]を継承するデザイナ クラスの[[GetDesignTimeHtmlメソッド>http://msdn.microsoft.com/ja-jp/library/system.web.ui.design.controldesigner.getdesigntimehtml.aspx]]のオーバーライドに実装する。

 /// <summary>WebCustRadioButtonListのVSデザインサポートを実装する</summary>
 internal class WebCustRadioButtonListDesigner : ControlDesigner {
   /// <summary>メンバ変数にWebCustRadioButtonListを保持</summary>
   protected WebCustRadioButtonList wcrbl;
 
   /// <summary>初期化</summary>
   public override void Initialize(IComponent component) {
     if (component is WebCustRadioButtonList) {
       base.Initialize(component);
       this.wcrbl = (WebCustRadioButtonList)component;
     }
   }
 
   /// <summary>VSデザイナに表示するHTMLを返す。</summary>
   public override string GetDesignTimeHtml() {
     try {
       // 非常に単純なコードであるため、
       // スタイル関係のプロパティ設定、子コントロールのインスタンス数のプロパティ設定を反映しない。
       string ret = ""
       + "<table>"
       + "  <tr>"
       + "    <td>"
       + "      <input type=\"radio\" value=\"選択項目1\" />"
       + "      <label >選択項目1</label>"
       + "    </td>"
       + "    <td>"
       + "      <input type=\"radio\" value=\"選択項目2\" />"
       + "      <label>選択項目2</label>"
       + "    </td>"
       + "    <td>"
       + "      <input type=\"radio\" value=\"選択項目3\" />"
       + "      <label >選択項目3</label>"
       + "    </td>"
       + "  </tr>"
       + "</table>";
 
       return ret;
     }
     catch (Exception ex) {
       // エラーの場合
       return String
         .Concat("<h3>Error</h3>Stack Trace:<br>", ex.StackTrace);
     }
   }
 }

最後に、上記のデザイナ クラスを使用するように、カスタム ラベル コントロール(Ctrl.WebCustLabel)のクラス定義に「[[Designer>http://msdn.microsoft.com/ja-jp/library/system.componentmodel.designerattribute.aspx]]」 属性を使用してデザイナ クラスを指定する。

 /// <summary>System.Web.UI.RadioButtonListのカスタム・コントロール</summary>
 [Designer("Ctrl.WebCustRadioButtonListDesigner"),
 ToolboxData("<{0}:WebCustRadioButtonList runat=server></{0}:WebCustRadioButtonList>")]
 public class WebCustRadioButtonList : RadioButtonList

このControlDesignerクラスのGetDesignTimeHtmlメソッドのオーバーライドのサンプルは、非常に簡素な実装であるため、文字のフォント、サイズ、色などの、スタイル関係のプロパティ設定、子コントロールのインスタンス数のプロパティ設定は反映されない。これらをHTMLタグに反映させる場合は、スタイルのサポート コードを実装する必要がある。

しかし、これらのプロパティ設定により可変となる「実際のWebアプリケーション上での外観」と「VSデザイナ上での外観」を一致させるには複雑な実装が必要になるため、プロジェクト部品でのサポートは考えなくて良いと考える。また、製品レベルの作り込みであっても、これらを「完全に」一致させるようなコードを実装するのは、無駄を含むため、避けた方が良い。

**プロパティ [#cf8f9890]
***カスタム プロパティの追加 [#w0e6cbab]
本項ではカスタム プロパティの追加方法について説明する。~
カスタム プロパティを追加する際は、通常通りにプロパティ プロシージャを実装すれば良い。

 /// <summary>コントロール内のテキストを反転するかどうかを示す。</summary>
 [DefaultValue("false"),
 Category("動作"),
 Description("コントロール内のテキストを反転するかどうかを示します。")]
 public bool Reverse {
   set { this.ViewState["Reverse"] = value; }
   get {
     return (this.ViewState["Reverse"] == null) ?
       false : (bool)this.ViewState["Reverse"];
   }
 }

-ただし、[[ASP.NET Web Forms]]アプリケーションにおいてコントロールの状態を持ち回る(状態を複数ポストバック間において保存する)場合は、インスタンス変数は用いず、Control.[[ViewState]]を使用する必要がある。これはPage.[[ViewState]]とは異なるため、同じキーを使用しても衝突しない。詳しくは、「[[ASP.NETの状態管理方式]]」を参照。

-また、ここでも同様にオプションとして、「[[DefaultValue>http://msdn.microsoft.com/ja-jp/library/system.componentmodel.defaultvalueattribute.aspx]]」・「[[Category>http://msdn.microsoft.com/ja-jp/library/system.componentmodel.categoryattribute.aspx]]」・「[[Description>http://msdn.microsoft.com/ja-jp/library/system.componentmodel.descriptionattribute.aspx]]」などの属性を定義できる。これらの属性は共に、PropertyGrid(コントロールのデザインタイム プロパティの設定を行うためのUIで、Visual Studioデザイナでコントロール選択時、右下のペインに表示されるグリッド)への表示方法を制御するためのものである。PropertyGridへの表示方法を制御するための属性について、以下にまとめる。

|メンバ名|説明|デフォルト値|h
|DefaultValue|デフォルト値を表す。|-|
|Browsable|プロパティをPropertyGridに表示するかどうかを指定する。|True|
|Category|プロパティをまとめるカテゴリ。&br;カテゴリには、次の共通カテゴリがある。&br;・Appearance:表示&br;・Behavior:動作&br;・Data:データ|-|
|Description|プロパティの設定を行う際の参考としてPropertyGridに表示される説明。|-|

***既存プロパティのカスタマイズ [#s2db5d89]
-本項では既存プロパティのカスタマイズ(オーバーライド)方法について説明する。既存プロパティのカスタマイズの際は、既存プロパティをオーバーライドする必要がある。カスタマイズ対象の既存のプロパティは、派生クラスでオーバーライドが可能なように、virtual / abstractキーワード(VBのキーワードでは、Overridable / MustOverrideに該当)が設定されている必要がある。

-また、何からの処理を追加した後、「base.オーバーライドしたプロパティ」から値を取得し、その後にカスタム動作となる処理を追加する(に値を設定し、その前にカスタム動作となる処理を追加する)ようにする。

***Label.Textのカスタマイズ [#u4099ba9]
以下は、Textプロパティをオーバーライドしカスタム動作を実装するサンプル(テンプレート)である。

 /// <summary>Textプロパティに機能を追加実装する。</summary>
 public override string Text {
 
   // ベースのプロパティを使用する。
   
   set {
     string temp = value;
     // カスタム動作の処理を実装する。
     base.Text = temp;
   }
   
   get {
     string temp = base.Text;
     // カスタム動作の処理を実装する。
     return temp;
   }
 }

***TextBox.Enabled, ReadOnlyのカスタマイズ [#lcd25c63]
-なお、以下は、テキストボックス コントロール(System.Web.UI.WebControls.TextBox)を継承したカスタム テキストボックス コントロール(Ctrl.WebCustTextBox)のEnabledプロパティのカスタマイズ例である。

-ここでは、Enabledプロパティのsetterメソッド内部で、実際にメンバ変数に値をセットする処理をReadOnlyプロパティに変更している。これは、例えばEnabled = falseで実装したものの、後工程での指摘により、ReadOnly = true的な動作(表示)に変更する必要に迫られた場合など、最小限の工数で対応するために、このような修正を施すことがある。また、併せて、ReadOnlyプロパティをオーバーライドしカスタム動作として、背景色の変更処理を実装する。

-このカスタマイズにより、Enabledプロパティの挙動をReadOnlyに一括変更でき、またReadOnly = trueに設定された場合、背景色をグレーに変更するので、「読み取り専用項目」であることが、ユーザ(オペレータ)に伝わり易くなる。

 /// <summary>活性・非活性を、Enabledから、ReadOnlyに変更したい場合。</summary>
 public override bool Enabled {
   set {
     this.ReadOnly = !value;
   }
   // getは親のEnabledのgetを使用
 }

また、背景色をグレーに変更するにあたって、元の背景色のバックアップを取っておく必要があるが、これについても、持ち回る(状態を複数ポストバック間において保存する)必要があるので、同様にインスタンス変数は用いず、Control.ViewStateを使用する必要がある。

 /// <summary>ReadOnlyプロパティに背景変更処理を追加する。</summary>
 public override bool ReadOnly {
   // ベースのプロパティを使用する。
 
   set {
     base.ReadOnly = value;
 
     // 背景変更
     if (base.ReadOnly) {
       this.ViewState["bkBgColor"] = this.BackColor;
       this.BackColor = System.Drawing.Color.LightGray;
     }
     else {
       this.BackColor =
         (this.ViewState["bkBgColor"] == null)
           ? this.BackColor : (System.Drawing.Color)this.ViewState["bkBgColor"];
     }
   }
 
   get {
     return base.ReadOnly;
   }
 }

**UIのカスタマイズ [#jc51310c]
[[ASP.NET Web Forms]]で使用するコントロールは、~
表示の制御がHTMLにより行われるのでカスタマイズしやすい。

***Render系メソッド [#y4342c87]
ここで出力されるHTMLのカスタマイズは、Render系メソッドのカスタマイズにより対応する。

カスタム コントロールをControlクラスから派生させて[[スクラッチ開発>#rcc49996]]する場合は、Render系メソッドも[[スクラッチ開発>#rcc49996]]するが、~
それ以外の場合は、必要に応じて動作変更するようにして、動作変更の必要がない場合は、処理を基本クラスの「base.xxxx」メソッドに委譲するような方法を採る。

***[[HtmlTextWriterクラス>http://msdn.microsoft.com/ja-jp/library/system.web.ui.htmltextwriter.aspx]] [#t34f01ae]
Render系メソッドにおいてHTMLを描画するには、引数に渡された[[HtmlTextWriterクラス>http://msdn.microsoft.com/ja-jp/library/system.web.ui.htmltextwriter.aspx]]を使用する。

-[[RenderBeginTagメソッド>http://msdn.microsoft.com/ja-jp/library/system.web.ui.htmltextwriter.renderbegintag.aspx]]
-[[WriteFullBeginTagメソッド>http://msdn.microsoft.com/ja-jp/library/system.web.ui.htmltextwriter.writefullbegintag.aspx]]
-[[Writeメソッド>http://msdn.microsoft.com/ja-jp/library/system.web.ui.htmltextwriter.write.aspx]]
-[[RenderEndTagメソッド>http://msdn.microsoft.com/ja-jp/library/system.web.ui.htmltextwriter.renderendtag.aspx]]
-[[WriteEndTagメソッド>http://msdn.microsoft.com/ja-jp/library/system.web.ui.htmltextwriter.writeendtag.aspx]]

などがある。

余談となるが、HtmlTextWriterは TextWriterのDecoratorとして設計されている。

***[[System.Web.UI.Control.Renderメソッド>http://msdn.microsoft.com/ja-jp/library/system.web.ui.control.render.aspx]] [#ic119b74]
Renderメソッドには、必要に応じてHTML描画コードをオーバーライドして実装する。

RenderメソッドのHTML描画処理は、更に、以下の3つのメソッドから構成される設計となっているので、必要であれば、こちらのメソッドをオーバーライドすることで、開始タグ、タグ内テキスト、終了タグと局所的にHTML描画処理をカスタマイズできる。

-[[System.Web.UI.WebControls.WebControl.RenderBeginTagメソッド>http://msdn.microsoft.com/ja-jp/library/system.web.ui.webcontrols.webcontrol.renderbegintag.aspx]]
-[[System.Web.UI.WebControls.WebControl.RenderContentsメソッド>http://msdn.microsoft.com/ja-jp/library/system.web.ui.webcontrols.webcontrol.rendercontents.aspx]]
-[[System.Web.UI.WebControls.WebControl.RenderEndTagメソッド>http://msdn.microsoft.com/ja-jp/library/system.web.ui.webcontrols.webcontrol.renderendtag.aspx]]

例えば、次のような実装を行うと、コントロールのHTML出力として、 "<test> test </test>" が出力される。RenderContentsメソッドのみオーバーライドすれば、タグ内に出力される文字列の出力のみカスタマイズできる。必要であれば、スタイルの属性・属性値をHTMLタグに反映させること。また、inputタグの場合は、id、nameなどの属性・属性値を出力し、次のポストバックでコントロールが正しく復元されるように実装する必要がある。

 /// <summary>RenderBeginTagのテスト</summary>
 public override void RenderBeginTag(HtmlTextWriter output) {
   output.Write("<test>");
 }
 
 /// <summary>RenderContentsのテスト</summary>
 protected override void RenderContents(HtmlTextWriter output) {
   output.Write("test");
 }
 
 /// <summary>RenderEndTagのテスト</summary>
 public override void RenderEndTag(HtmlTextWriter output) {
   output.Write("</test>");
 }

***[[System.Web.UI.Control.RenderControlメソッド>http://msdn.microsoft.com/ja-jp/library/system.web.ui.control.rendercontrol.aspx]] [#acf11ad1]
RenderControlメソッドは、上記のRenderメソッドの制御コード(前・後処理など)を実装する(Renderメソッドの呼び出し自体も実装する)。必要に応じてこれをオーバーライドする。RenderControlメソッドには、Renderメソッドで描画されたHTMLを取得し、それを変換する処理を実装することも可能である。

***DisplayRawTextカスタム プロパティの実装例(Render) [#ea94138b]
以下は、カスタム プロパティによって、HTMLの描画処理を変更するRenderメソッドの実装例である(変更の必要がない場合は、「base.Render」に処理を委譲する)。興味深いのは、(追加したカスタム プロパティである)DisplayRawText、Hideプロパティをtrueに設定した場合である。この場合、HTTPレスポンスにコントロールのHTMLが出力されなくなるが、以降DisplayRawText、Hideプロパティをfalseに戻した場合、コントロールが正しく復元される。これは、このコントロールの復元処理は、ViewStateにより実現されており、ViewStateの情報は、コントロールが出力するinputタグから分離されたViewState専用のHiddenタグに保存されるためである。

 /// <summary>
 /// Renderメソッドは、
 /// ・RenderBeginTag(開始のタグ)
 /// ・RenderContents(中間の部分)
 /// ・RenderEndTag(終了のタグ)
 /// の各メソッドをこの順に呼び出して、コントロールをクライアントに送信する。
 /// </summary>
 /// <remarks>
 /// このメソッドは、表示中にページによって自動的に呼び出される。
 /// また、このメソッドは、主にコントロールの開発者によって使用される。
 /// </remarks>
 protected override void Render(HtmlTextWriter output) {
   // Render処理を作り込む。
 
   if (this.DisplayRawText) {
     // 生テキストの表示
     output.Write(this.Text);
   }
   else if (this.Hide) {
     // 出力時に隠す(データ自体は保持)
     output.Write("");
   }
   else {
     // 通常通りの出力
     base.Render(output);
   }
 }

***ToUpper, ToLowerカスタム プロパティの実装例(RenderControl) [#l0a21914]
以下は、カスタム プロパティによって、Renderメソッドにより描画されたHTML出力を編集するRenderControlメソッドの実装例である(編集の必要がない場合は、「base.RenderControl」に処理を委譲する)。Renderメソッドにより描画されたHTML出力を取得するには、Renderメソッド内部で使用するHtmlTextWriterを変更する。変更内容は、HtmlTextWriterが内部で使用するTextWriterクラス を既定の型から、StringWriterクラス 型に変更する。これにより、Renderメソッドにより描画されたHTML出力を、RenderControlメソッド中で取得できる 。

 /// <summary>
 /// Visibleプロパティ、ページのトレースなどの
 /// 制御を行い、ページにコントロールを表示する。
 /// </summary>
 /// <remarks>
 /// このメソッドは、表示中にページによって自動的に呼び出される。
 /// カスタム コントロールの開発者はこのメソッドをオーバーライドできる。
 /// </remarks>
 public override void RenderControl(HtmlTextWriter output) {
 
   if (this.ToUpper || this.ToLower) {
 
     // RenderControlにRenderの制御処理を作り込む。
 
     // StringWriterの書き出し先のStringBuilderを生成
     StringBuilder sb = new StringBuilder();
     // StringWriterを使用するStringWriterを生成
     StringWriter sw = new StringWriter(sb);
 
     // 上記StringWriterを使用するHtmlTextWriterを生成
     HtmlTextWriter htw = new HtmlTextWriter(sw);
 
     // StringWriterを使用するHtmlTextWriterを指定し、Renderメソッドを実行する。
     this.Render(htw);
 
     // RenderメソッドでRenderされた、コントロールのHTMLを
     // StringWriterに指定したStringBuilderから取得する。
     string html = sb.ToString();
 
     // HTMLの編集処理
     if (this.ToUpper) {
       html = html.ToUpper();
     }
     else if (this.ToLower)
     {
       html = html.ToLower();
     }
     else
     { 
       // ここは通らない。
     }
 
     // ページにコントロールを表示
     output.Write(html);
   }
   else {
     // ベースのRenderControlを使用する。
     base.RenderControl(output);
   }
 }

**カスタム サーバ コントロールのメタデータ属性 [#y50238c5]
http://msdn.microsoft.com/ja-jp/library/ms178658.aspx

*[[Windows Forms]] [#y005203a]

**クラス定義 [#c3ba9bb7]
以下は、ラベル コントロール(System.Windows.Forms.Label )を継承したカスタム ラベル コントロール(Ctrl.WinCustLabel)のクラス定義。

 namespace Ctrl
 {
   /// <summary>System.Windows.Forms.Labelのカスタム・コントロール</summary>
   [DefaultProperty("Text")]
   public class WinCustLabel : Label


**コンストラクタ [#d7f269e0]
コンストラクタについては自由な利用が可能である。

***VSデザイナとコンストラクタ [#zc8d85c0]
[[ASP.NET Web Forms]]と[[同様に>#lc492ffc]]、子コントロールを持つカスタム コントロールのコンストラクタに子コントロールを生成する処理を実装した場合、表示が不正になる現象は[[Windows Forms]]アプリケーションでも同様に確認できる。

対策としては、VSデザイナに正しく表示されない問題を除けば、[[Control.InitLayoutメソッド>https://msdn.microsoft.com/ja-jp/library/system.windows.forms.control.initlayout.aspx]]をオーバ ライドしてカスタム コントロールの初期化処理を同梱するという方法が有用である。

-デザイナで配置できるコントロールを作ったらコンストラクタが2回呼ばれる  上野メモ帳~
http://uenomemo.sakura.ne.jp/pcmemo/54

**プロパティ [#p5ebf934]
[[Windows Forms]]アプリケーションのカスタム コントロールでは、カスタム プロパティのうち、表示に影響を与えるものがあったら、カスタム プロパティのsetterメソッドでRefreshメソッド を実行する必要がある。また、[[ASP.NET Web Forms]]では、状態保持のためにViewStateを使用したが、[[Windows Forms]]アプリケーションでは通常通りメンバ変数を使用する。

 /// <summary>文字列反転</summary>
 private bool _Reverse = false;
 
 /// <summary>テキストを反転するかどうかを示す。</summary>
 [DefaultValue("false"),
 Category("動作"),
 Description("テキストを反転するかどうかを示します。")]
 public bool Reverse {
   set {
     this._Reverse = value;
 
     // 表示に影響を与えるプロパティの
     // setterにはRefreshメソッドを仕込む。
     this.Refresh();
   }
   get { return this._Reverse; }
 }

***表示に影響を与えるプロパティのgetter [#naed4706]
[[Windows Forms]]アプリケーションのカスタム コントロールのプロパティのカスタマイズは、基本的に[[ASP.NET Web Forms]]のコントロールのカスタマイズ方法と変わらないが、表示に影響を与えるプロパティのgetterメソッドのみ変更するカスタマイズを施すと、コントロールが認識している描画領域・位置と合わなくなり、すべての文字が描画されないなどの問題が発生する。

故に、[[Windows Forms]]アプリケーションのカスタム コントロールでは、描画にかかわるプロパティのgetterメソッドのみをカスタマイズすることは不可能である。例えば、getterメソッド内でsetterメソッドを呼び出し、描画領域・位置の再計算・再描画させるという方法も、このRefreshメソッドによる描画領域・位置の再計算・再描画にて、getterメソッド呼び出すため、getter → setter → 再描画(Refresh) → getterの無限再起(スタック オーバーフロー)に陥る。このため、このアプローチは上手くいかない。対処としては、setterメソッドに実装を移す必要がある。

**イベント [#y4e45a5d]
***イベントハンドラの追加 [#e7f9ba82]
リッチクライアントである[[Windows Forms]]では[[ウィンドウ メッセージ]]を使用したイベントドリブンの制御を行うため、共通的な処理をカスタム コントロール内に同梱することも可能である。以下は、フォーカスが当たった際に入力されたテキストを全選択するサンプル(プログラムやタブで遷移した時だけテキストを全選択し、マウスクリックで遷移した時は選択しないように制御している)。

 namespace Ctrl {
   /// <summary>System.Windows.Forms.TextBoxのカスタム・コントロール</summary>
   [DefaultProperty("Text")]
   public class WinCustTextBox : TextBox {
   /// <summary>コンストラクタ</summary>
     public WinCustTextBox() {
       this.InitializeComponent();
     }
     /// <summary>初期化</summary>
     private void InitializeComponent() {
       this.SuspendLayout();
       this.Enter += new System.EventHandler(this.WinCustomTextBox_Enter); 
       this.Leave += new System.EventHandler(this.WinCustomTextBox_Leave);
       this.MouseDown += new System.Windows.Forms.MouseEventHandler(this.WinCustomTextBox_MouseDown);
       this.ResumeLayout(false);
     }
 
     /// <summary>MouseDown状態の確認用フラグ</summary>
     private bool IsMouseDown = false;
 
     /// <summary>マウスが入った</summary>
     private void WinCustomTextBox_MouseDown(object sender, MouseEventArgs e) {
       this.IsMouseDown = true;
     }
 
     /// <summary>フォーカス</summary>
     private void WinCustomTextBox_Enter(object sender, EventArgs e) {
       if (!this.IsMouseDown) {
         // MouseDown状態で無ければ全選択
         this.SelectAll();
       }
     }
 
     /// <summary>ロスト フォーカス</summary>
     private void WinCustomTextBox_Leave(object sender, EventArgs e) {
       this.IsMouseDown = false;
     }
   }
 }

***カスタムイベント [#o0d0de2b]
カスタムイベントの作成方法

-方法 : カスタムのダブルクリック イベントを作成する~
http://msdn.microsoft.com/ja-jp/library/ms172533.aspx

-カスタムコントロール作成入門講座初級編  みかみんのプログラミング道場~
http://tech.junax.jp/custom-control-beginner/
--新規イベントの実装~
http://tech.junax.jp/custom-control-beginner/wfcb-new-event/

**UIのカスタマイズ [#n4f5f75f]
オーナードロー(オーナー描画)
-コントロールの描画を独自で行う方法。
-OnPaintメソッドをオーバーライドする。

***OnPaintメソッド [#ofb09ba5]
ここで出力されるHTMLのカスタマイズは、Render系メソッドのカスタマイズにより対応する。

カスタム コントロールをControlクラスから派生させて[[スクラッチ開発>#rcc49996]]する場合は、OnPaintメソッドも[[スクラッチ開発>#rcc49996]]するが、~
それ以外の場合は、必要に応じて動作変更するようにして、動作変更の必要がない場合は、処理を基本クラスの「base.xxxx」メソッドに委譲するような方法を採る。

-参考
--方法 : カスタム イメージのボタン コントロールを作成する~
http://msdn.microsoft.com/ja-jp/library/ms172532.aspx
 // Override the OnPaint method to draw the background image and the text.
 protected override void OnPaint(PaintEventArgs e)
 {
     if(this.pressed && this.pressedImage != null)
         e.Graphics.DrawImage(this.pressedImage, 0, 0);
     else
         e.Graphics.DrawImage(this.backgroundImage, 0, 0);
 
     // Draw the text if there is any.if(this.Text.Length > 0)
     {
         SizeF size = e.Graphics.MeasureString(this.Text, this.Font);
 
         // Center the text inside the client area of the PictureButton.
         e.Graphics.DrawString(this.Text,
             this.Font,
             new SolidBrush(this.ForeColor),
             (this.ClientSize.Width - size.Width) / 2,
             (this.ClientSize.Height - size.Height) / 2);
             }
 
     // Draw a border around the outside of the // control to look like Pocket PC buttons.
     e.Graphics.DrawRectangle(new Pen(Color.Black), 0, 0, 
         this.ClientSize.Width - 1, this.ClientSize.Height - 1);
 
     base.OnPaint(e);
 }

--方法 : オーナー描画リスト ボックスを作成する~
http://msdn.microsoft.com/ja-jp/library/ms229679.aspx
 ・・・

***クリッピング [#z51ef61e]
部分描画により、性能を向上させることができる。

-C#ゲームプログラミング-2.12 クリッピング - WisdomSoft~
http://www.wisdomsoft.jp/496.html

***オーナー描画をサポートするコントロール [#j1d99d2c]
-組み込みのオーナー描画サポートを備えたコントロール~
https://msdn.microsoft.com/ja-jp/library/yyfab68k.aspx
>オーナー描画によるカスタマイズを含んだ再使用可能なコントロールを作成するには、オーナー描画をサポートするコントロール クラスから派生した新しいクラスを作成します。その後、描画イベントを処理する代わりに、新しいクラス内の適切な OnEventName メソッドのオーバーライドにオーナー描画コードを記述します。このとき、必ず基本クラスの OnEventName メソッドを呼び出すようにしてください。これは、このコントロールのユーザーがオーナー描画イベントを処理してさらなるカスタマイズを実行できるようにするための配慮です。

*メソッド・プロパティをオーバーライドできない場合 [#aaba22bf]
派生クラスでオーバーライドが可能なように、virtual / abstractキーワード(VBのキーワードでは、Overridable / MustOverrideに該当)が設定されておらず、カスタマイズができない場合、この場合、newキーワード により隠蔽・置換が可能であるが、.NETフレームワークによりコントロールがベースの型によってハンドルされると、カスタマイズにより追加した機能が動作しないなどの問題が発生する。

例えば、newキーワードによりTextプロパティを上書きし、Renderメソッドのオーバーライド中で「base.Render」を呼び出した場合、「base.Render」中では「base.Text」が呼ばれてしまい、newキーワードにより上書きした「this.Text」が呼ばれなくなる。このため、思い通りに動作しなくなる。

このようなコントロールは、当該プロパティのカスタマイズをサポートした再使用可能なコントロールとして作成されておらず、また、newキーワード(VBのキーワードでは、Shadowsに該当)によるメソッド・プロパティの上書きは、問題の発見を遅れさせ、かつ原因特定などを困難にする可能性があるので、避けるようにする。

*スクラッチ開発 [#rcc49996]
下記のコントロールの基本クラスから継承し、カスタム コントロールをスクラッチ開発する方法がある。

-[[ASP.NET Web Forms]]~
System.Web.UI.WebControls.WebControl~
https://msdn.microsoft.com/ja-jp/library/system.web.ui.webcontrols.webcontrol.aspx
-[[Windows Forms]]~
System.Windows.Forms.Control~
https://msdn.microsoft.com/ja-jp/library/system.windows.forms.control.aspx

*参考 [#d6212364]
**Webカスタム コントロールの開発 [#u1cb43bd]
Webカスタム コントロール開発に関する参考資料

-MSDN > Visual Web Developer > ASP.NETカスタム サーバ コントロールの開発~
http://msdn.microsoft.com/ja-jp/library/zt27tfhy.aspx

--チュートリアル : カスタム サーバ コントロールの開発と使用~
http://msdn.microsoft.com/ja-jp/library/yhzc935f.aspx
--方法 : Visual StudioでカスタムASP.NETサーバ コントロールを使用する~
http://msdn.microsoft.com/ja-jp/library/ms366537.aspx

--カスタム サーバ コントロールのメタデータ属性~
http://msdn.microsoft.com/ja-jp/library/ms178658.aspx
--サーバ コントロールの単純なプロパティとサブプロパティ~
http://msdn.microsoft.com/ja-jp/library/ms178648.aspx
--Webコントロールのレンダリングの例~
http://msdn.microsoft.com/ja-jp/library/f820d25y.aspx
--カスタム サーバ コントロールの例のビルド~
http://msdn.microsoft.com/ja-jp/library/az5kdaz0.aspx

--(状態管理)
---コントロールの状態とビューステートの例~
http://msdn.microsoft.com/ja-jp/library/1whwt1k7.aspx
---サーバ コントロールのカスタム状態管理~
http://msdn.microsoft.com/ja-jp/library/ms178650.aspx
---カスタム型の状態管理の例~
http://msdn.microsoft.com/ja-jp/library/ms178652.aspx

--(コレクション プロパティ、子コントロール)
---サーバ コントロールのコレクション プロパティ~
http://msdn.microsoft.com/ja-jp/library/ms178654.aspx
---複合Webコントロールの例~
http://msdn.microsoft.com/ja-jp/library/3257x3ea.aspx
---子コントロールの型指定されたスタイルの例~
http://msdn.microsoft.com/ja-jp/library/ms178656.aspx

--(テンプレート、データ バインド)
--テンプレート サーバ コントロールの例~
http://msdn.microsoft.com/ja-jp/library/ms178657.aspx
--ASP.NET1.1用カスタム データ バインドWebサーバ コントロールの開発~
http://msdn.microsoft.com/ja-jp/library/ms366538.aspx
--ASP.NET2.0用カスタム データ バインドWebサーバ コントロールの開発~
http://msdn.microsoft.com/ja-jp/library/ms366539.aspx

--カスタム サーバ コントロールのセキュリティ保護~
http://msdn.microsoft.com/ja-jp/library/ms366542.aspx

-CodeZine > 改行やリンクをタグに自動置換するコントロール作成~
http://codezine.jp/article/detail/402

-japan.internet.comデベロッパー > Webカスタム コントロールの作成~
http://japan.internet.com/developer/20051108/26.html

**[[Windows Forms]]カスタム コントロールの開発 [#zb628548]
[[Windows Forms]]カスタム コントロール開発に関する参考資料

-MSDN > アプリケーション開発 > Windowsフォーム コントロール > カスタム コントロールの開発~
http://msdn.microsoft.com/ja-jp/library/4yf3whkx.aspx

--方法 : 数値テキスト ボックスを作成する~
http://msdn.microsoft.com/ja-jp/library/ms229644.aspx
--方法 : カスタム イメージのボタン コントロールを作成する~
http://msdn.microsoft.com/ja-jp/library/ms172532.aspx
--方法 : カスタムのダブルクリック イベントを作成する~
http://msdn.microsoft.com/ja-jp/library/ms172533.aspx
--方法 : フォームの内容をスクロールする~
http://msdn.microsoft.com/ja-jp/library/ms172530.aspx

--(スマート デバイスPocket PCプロジェクト)
---方法 : グラデーションの塗りつぶしを表示する(スマート デバイスPocket PCプロジェクト)~
http://msdn.microsoft.com/ja-jp/library/ms229655.aspx
---方法 : ネイティブのコールバックを使用してボタンをサブクラス化する~
http://msdn.microsoft.com/ja-jp/library/ms229661.aspx

--(.NET Compact Framework)
---方法 : ListViewのアイテムを並べ替える~
http://msdn.microsoft.com/ja-jp/library/ms229643.aspx
---方法 : ネイティブのコールバックを使用してTreeViewをサブクラス化する~
http://msdn.microsoft.com/ja-jp/library/ms229669.aspx
---方法 : オーナー描画リスト ボックスを作成する~
http://msdn.microsoft.com/ja-jp/library/ms229679.aspx
---方法 : OnEnter機能を作成する~
http://msdn.microsoft.com/ja-jp/library/ms229686.aspx

-カスタムコントロール作成入門講座初級編  みかみんのプログラミング道場~
http://tech.junax.jp/custom-control-beginner/
--フォームと継承~
http://tech.junax.jp/custom-control-beginner/wfcb-form-inheritance/
--作成環境の準備~
http://tech.junax.jp/custom-control-beginner/vs-prepare/
--初めてのカスタムコントロール~
http://tech.junax.jp/custom-control-beginner/wfcb-first-control/
--新規プロパティの実装~
http://tech.junax.jp/custom-control-beginner/wfcb-new-property/
--新規イベントの実装~
http://tech.junax.jp/custom-control-beginner/wfcb-new-event/

**PropertyGridの活用 [#ae470ca6]
PropertyGridコントロールに関する参考資料

-DOBON.NET > プログラミング道 > .NET Tips > コントロール > PropertyGridコントロールの使い方~
http://dobon.net/vb/dotnet/control/propertygrid.html

-MSDN > 技術資料 >
--.NET FrameworkのPropertyGridコントロールの高度な活用~
http://msdn.microsoft.com/ja-jp/library/aa302326.aspx
--Visual Studio .NETプロパティ ブラウザによるコンポーネントの本格的なRAD化~
http://msdn.microsoft.com/ja-jp/library/aa302334.aspx

**Open棟梁のカスタムコントロール [#pb2c889d]
-[[ASP.NET Web Forms]]のカスタムコントロール~
https://github.com/OpenTouryoProject/OpenTouryo/tree/develop/root/programs/C%23/Frameworks/Infrastructure/CustomControl
-[[Windows Forms]]のカスタムコントロール~
https://github.com/OpenTouryoProject/OpenTouryo/tree/develop/root/programs/C%23/Frameworks/Infrastructure/CustomControl/RichClient


トップ   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS