「[[マイクロソフト系技術情報 Wiki>http://techinfoofmicrosofttech.osscons.jp/]]」は、「[[Open棟梁Project>https://github.com/OpenTouryoProject/]]」,「[[OSSコンソーシアム .NET開発基盤部会>https://www.osscons.jp/dotNetDevelopmentInfrastructure/]]」によって運営されています。 -[[戻る>.NET開発]] * 目次 [#a4f725bb] #contents *概要 [#mb024c27] 子コントロールを持つFormやカスタム コントロールの子コントロールを生成する処理を、~ コンストラクタに実装した場合、実行時の表示が「不正になる」現象に対する対処方法等を纏めている。 この問題は、 -[[Windows Forms>#ec5b8d0b]] -[[ASP.NET Web Forms>#i022b8ec]] で発生する。 *共通的な問題 [#n8b1e837] **処理の移動 [#baf4b5f9] -Formやカスタム コントロールのコンストラクタは、実行時だけでなく、デザイナ上で動作する。 -このため、子コントロールの二重登録のような問題が発生する。 -コンストラクタに実装した処理を、ロード イベントに移動することで解決できるが、 -この場合、デザイナに子コントロールが表示がされなくなる。 **デザイン時に使用できる値 [#saf04f73] デザイン時に使用できる値は、実行時に使用できる値と異なる。 -アプリケーションが実行されていないので、~ 共有メモリやグローバル変数などのデザイン時に読むことはできない。 -デザインタイム・プロパティ([[Open棟梁のカスタムコントロールのチェック属性のような>https://opentouryo.osscons.jp/index.php?%E3%83%90%E3%83%AA%E3%83%87%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E6%A9%9F%E8%83%BD%E4%BB%98%E3%81%8D%E3%82%AB%E3%82%B9%E3%82%BF%E3%83%A0%20%E3%82%B3%E3%83%B3%E3%83%88%E3%83%AD%E3%83%BC%E3%83%AB]])は利用可能。 -app.configの値については、以下のようにデザインタイムで利用可能であるもよう。 --デザイン時に指定したDBのパス名を実行時に変更する方法 - Insider.NET - @IT~ http://www.atmarkit.co.jp/bbs/phpBB/viewtopic.php?topic=47969&forum=7 --外部ファイルにコントロールのプロパティを格納する: .NET Tips: C#, VB.NET~ http://dobon.net/vb/dotnet/programing/dynamicproperties.html *[[Windows Forms]] [#ec5b8d0b] -参考 --Windowsフォームのデザイン時に非実行のコードを書くには - Insider.NET - @IT~ http://www.atmarkit.co.jp/bbs/phpBB/viewtopic.php?topic=15316&forum=7 **対策の概要 [#kcae74c6] ***InitLayoutイベント・ハンドラ [#q2700a11] 対策としては、InitLayoutイベント・ハンドラをオーバ ライドして~ カスタム コントロールの初期化処理を同梱するという方法が有用である。 -デザイナで配置できるコントロールを作ったらコンストラクタが2回呼ばれる 上野メモ帳~ http://uenomemo.sakura.ne.jp/pcmemo/54 -(vb.net)カスタムコントロール作成時に初期化処理を何でもかんでも~ コンストラクタに書いてはいけない : 3流プログラマのメモ書き~ http://jehupc.exblog.jp/7704408/ ***Designプロパティ [#cea7b275] また、Designプロパティを使用してデザイン時と実行時を判別できる。 -.NET:Tips > デザイン:デザイン時に処理を実行させない - YiaoWang~ http://yiaowang.web.fc2.com/programing/vs_tips/design_04.html -Windowsフォームのデザイン時に非実行のコードを書くには - Insider.NET - @IT~ http://www.atmarkit.co.jp/bbs/phpBB/viewtopic.php?topic=15316&forum=7 **詳細 [#e74cb6cc] ***原因 [#n8b590ec] -VisualStudioデザイナにより生成されたコードは*.Designer.cs(vb)に出力される。 --当該コントロールの子コントロール毎にメンバ変数が定義され --InitializeComponentに子コントロールの設定処理が実装される。 -コンストラクタは実行時だけでなく、デザイン時にも実行される。~ コンストラクタでは、上記のInitializeComponentを呼び出してる。 -InitializeComponentは子コントロール毎に定義されたメンバ変数に~ 子コントロールをnewしてからプロパティ設定をしているため、~ 子コントロールの二重登録のような問題は起きない。 -しかし、コンストラクタのコードから子コントロールを追加するような処理を実装すると --メンバに直接子コントロールをAddしたりする場合 --DataSourceを設定してDataBindしたりする場合 >この結果が、*.Designer.cs(vb)に出力される~ VisualStudioデザイナの仕様であるため、~ 子コントロールの二重登録のような問題が起きてしまう。 ***対策 [#o02e82cf] -このため、デザイン表示時のみ実行したい処理は、InitLayoutに実装するようにする。 --InitLayoutは、コントロール配置時にのみ発生するイベントのハンドラーである。~ --このため、デザイン時・実行時に処理が実行されて二重登録が起きる問題は発生しなくなる。 -ただし、コントロールのリサイズなどによってInitLayoutイベントが再発生することがある。 --この際、同様に結果が*.Designer.cs(vb)に出力され、~ 二重登録のような問題が発生することがあるようなので、~ 制御用のコードを別途、実装する必要がある。 **考察 [#d28ca154] [[Windows Forms]]では、デザイン専用コードを実装する必要が無いため、~ [[ASP.NET Web Formsのケース>#i022b8ec]]より簡単にデザイン時と実行時の表示を一致させることが出来るが、~ 製品レベルの作り込みで、通常、ユーザ・プログラムでここまでの実装は行わない方が無難であると考える。 *[[ASP.NET Web Forms]] [#i022b8ec] **対策の概要 [#j3eb857e] ***処理の移動 [#n5d1492c] コンストラクタに実装した処理を、ロード イベントに移動する。 ***デザイン サポート コードを実装 [#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]]