「[[マイクロソフト系技術情報 Wiki>http://techinfoofmicrosofttech.osscons.jp/]]」は、「[[Open棟梁Project>https://github.com/OpenTouryoProject/]]」,「[[OSSコンソーシアム .NET開発基盤部会>https://www.osscons.jp/dotNetDevelopmentInfrastructure/]]」によって運営されています。

-戻る
--[[VSデザイナの問題]]
--[[.NETコントロールのカスタマイズ方法]]

* 目次 [#a4f725bb]
#contents

*概要 [#mb024c27]
-(子コントロールを持つFormやカスタム コントロールの)~
子コントロールを生成する処理を、コンストラクタに実装した場合、~
デザイン時と実行時の表示が乖離する現象に対する対処方法等を纏めている。

-この問題は、
--[[Windows Forms>#ec5b8d0b]]
--[[ASP.NET Web Forms>#i022b8ec]]

>など、デザイナを持つUIサブシステムの開発環境で発生する。

-Formやカスタム コントロールのコンストラクタは、実行時だけでなく、Visual Studioデザイナ上でも動作する。
-Formやカスタム コントロールのコンストラクタは、~
実行時だけでなく、デザイン時(Visual Studioデザイナ上で)も動作する。
--このため、子コントロールの二重登録のような問題が発生する。
--結果として、デザイン時と実行時の表示が異なることになる。

-この問題は、コンストラクタに実装していた処理を、ロード イベントに移動することで~
解決できるが、この場合、デザイナに子コントロールが表示がされなくなる。

*[[Windows Forms]] [#ec5b8d0b]

**原因 [#n8b590ec]
-VisualStudioデザイナにより生成されたコードは*.Designer.cs(vb)に出力される。
--当該コントロールの子コントロール毎にメンバ変数が定義され
--InitializeComponentに子コントロールの設定処理が実装される。

-コンストラクタは実行時だけでなく、デザイン時にも実行される。~
コンストラクタでは、上記のInitializeComponentを呼び出してる。

-InitializeComponentは子コントロール毎に定義されたメンバ変数に~
子コントロールをnewしてからプロパティ設定をしているため、~
子コントロールの二重登録のような問題は起きない。

-しかし、コンストラクタのコードから子コントロールを追加するような処理を実装すると
--メンバに直接子コントロールをAddしたりする場合
--DataSourceを設定してDataBindしたりする場合

>この結果が、*.Designer.cs(vb)に出力される~
VisualStudioデザイナの仕様であるため、~
子コントロールの二重登録のような問題が起きてしまう。

**対策 [#o02e82cf]
***InitLayoutイベント・ハンドラ [#q2700a11]
このため、対策としては、InitLayoutイベント・ハンドラをオーバ ライドして~
Formやカスタム コントロールの初期化処理を同梱するという方法が有用である。

-InitLayoutは、コントロール配置時にのみ発生するイベントのハンドラーである。~
-このため、デザイン時・実行時に処理が実行されて二重登録が起きる問題は発生しなくなる。

-ただし、コントロールのリサイズなどによってInitLayoutイベントが再発生することがある。~
この際、同様に結果が*.Designer.cs(vb)に出力され、~
二重登録のような問題が発生することがあるようなので、~
制御用のコードを別途、実装する必要がある。

-参考
--デザイナで配置できるコントロールを作ったらコンストラクタが2回呼ばれる  上野メモ帳~
http://uenomemo.sakura.ne.jp/pcmemo/54
--(vb.net)カスタムコントロール作成時に初期化処理を何でもかんでも~
コンストラクタに書いてはいけない : 3流プログラマのメモ書き~
http://jehupc.exblog.jp/7704408/
--全ては時の中に… : 【VB.NET】コントロール配置時に発生するイベント~
http://blog.livedoor.jp/akf0/archives/51271278.html

***DesignModeプロパティ [#cea7b275]
また、DesignModeプロパティを使用してデザイン時と実行時を判別できる。

-参考
--[[VSデザイナの問題]]

**考察 [#d28ca154]
[[Windows Forms]]では、デザイン専用コードを実装する必要が無いため、~
[[ASP.NET Web Formsのケース>#i022b8ec]]より簡単にデザイン時と実行時の表示を一致させることが出来るが、~
製品レベルの作り込みで、通常、ユーザ・プログラムでここまでの実装は行わない方が無難であると考える。

*[[ASP.NET Web Forms]] [#i022b8ec]
**対策の概要 [#j3eb857e]
***ロード イベントに処理を移動 [#n5d1492c]
-コンストラクタに実装した処理を、ロード イベントに移動する。
-この場合、この場合、デザイナに子コントロールが表示がされなくなるので、~
必要に応じて、以下の[[デザイン サポート コードの実装>#fdc5e48b]]を追加する。

***デザイン サポート コードを実装 [#fdc5e48b]
GetDesignTimeHtmlメソッドからVisual Studioデザイナに表示するHTMLを返すことで対応する。

**詳細 [#a43a8822]
***原因 [#u87784e7]
-WebサイトでなくWebアプリケーションであれば *.Designer.cs(vb)も出力されるが、ここは、~
--メンバ変数の定義のみが生成され、
--子コントロールの設定処理は、*.aspxにマークアップ側に実装される。

-通常、*.aspxのマークアップを使用して子コントロールの設定処理が行われる。
-コンストラクタでAdd やDataBindで子コントロールを追加すると、~
WindowsFormsと同じように、子コントロールにマークアップに追加され問題が発生する。

-例えば、~
下記のようなコンストラクタの実装によって、~
選択項目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>

>この実装の状態で、Visual Studioデザイナでコントロールのサイズを変更したところ、
~Visual Studioデザイナ上での表示が下記のような、意図せぬ表示となる問題が発生する。
 <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>

>これは、Visual Studioデザイナ上でコンストラクタが2回実行され、~
DataSourceにListItemが2回追加されたためである。

**対策 [#k957b28e]
-VisualStudioデザイン サポート コードは、[[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のVisual Studioデザインサポートを実装する</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>Visual Studioデザイナに表示する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

**考察 [#c5e49b42]
-このControlDesignerクラスのGetDesignTimeHtmlメソッドのオーバーライドのサンプルは、非常に簡素な実装であるため、~
文字のフォント、サイズ、色などの、スタイル関係のプロパティ設定、子コントロールのインスタンス数のプロパティ設定は反映されない。

-これらをHTMLタグに反映させる場合は、~
Visual Studioデザイン サポート コードを実装する必要がある。~
しかし、デザインタイム・プロパティ設定により可変となる
--「実際のWebアプリケーション上での外観」と
--「Visual Studioデザイナ上での外観」を

>一致させるには複雑な実装が必要になる。

-プロジェクト部品でのサポートは考えなくて良いと考える。~
また、製品レベルの作り込みであっても、~
これらを「完全に」一致させるようなコードを実装するのは、無駄を含むため、~
避けた方が良い(デザインを行なうのに必要となる範囲で一致させるレベルに留める)。

----
Tags: [[:.NET開発]], [[:UIサブシステム]], [[:Windows Forms]], [[:ASP.NET Web Forms]]

トップ   編集 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS