-
Notifications
You must be signed in to change notification settings - Fork 0
MS_VSDesignerProblem
VS デザイナには
デザイン時にコードが実行されることにより、
幾つか問題が発生し得る。
補足(この 1 行が本ページの核心): 「デザイン時にコードが実行される」
——この事実を知らないと、以降の問題はどれも理解できない。【VS デザイナが何をしているか】 フォーム デザイナを開く → Visual Studio が【あなたのコードをコンパイルし】 → 【フォームのインスタンスを実際に生成する】★ → それを描画してデザイン画面に表示する つまり、 ・コンストラクタが【走る】 ・フィールド初期化子が【走る】 ・カスタム コントロールの OnPaint が【走る】 ・静的コンストラクタが【走る】【ただし、走らないもの】 ・Main メソッド(アプリの起動処理)★ ・Form_Load / Shown(デザイン時は発生しない) ・App.config を読む初期化処理(後述の通り一部は読める) ・DI コンテナの構築 → 【「アプリは起動していないのに、フォームだけ生きている」】 という特殊な状態であるこれが問題の根源である。
「アプリが起動していれば当然あるはずのもの」が、
デザイン時には存在しない。
VS デザイナは Visual Studio の 32bit プロセス中で動くので、
VS デザイナから実行されるコード中で、64bit の DLL をロードできない。
- 継承フォームを作成しようとするとデザイナーがエラーとなる。
https://social.msdn.microsoft.com/Forums/vstudio/ja-JP/b3914f86-7a4a-44e8-9abc-caa0a8dfeda1?forum=csharpgeneralja
移行メモ(Visual Studio 2022 で 64bit 化された): 本節の前提は
Visual Studio 2022 で変わった。【Visual Studio 2019 まで】 devenv.exe は【32bit プロセス】★ → デザイナも 32bit → 64bit 専用の DLL を P/Invoke するコントロールは デザイン時に読み込めず、エラーになる 【Visual Studio 2022 以降】 devenv.exe が【64bit 化】された → 逆に【32bit 専用の DLL が読めなくなった】★ → 古い 32bit の COM / ネイティブ DLL に依存する コントロールがデザイン時に落ちるようになった【つまり、問題が消えたのではなく「向きが逆になった」】 ・旧来: 64bit DLL が読めない ・現在: 32bit DLL が読めない → [DLL作成手順](MS_DLLCreationSteps) の 「32bit / 64bit の一致」(BadImageFormatException)と同じ問題.NET(Core 系)の Windows Forms デザイナはさらに構造が違う。
【.NET Framework のデザイナ】 Visual Studio の【プロセス内】で直接フォームを生成 【.NET(Core 系)のデザイナ】★ 【別プロセス(デザイナ サーバー)】でフォームを生成し、 VS とは IPC で通信する → VS 本体のビット数に縛られない → プロジェクトのターゲット(x86 / x64)に合わせて起動できる → 一方で【デザイナ プロセスが落ちる】という新しい症状が出る 「デザイナー サーバー プロセスとの通信に失敗しました」回避策:
・プラットフォーム ターゲットを AnyCPU にする ・DesignMode 判定でネイティブ呼び出しを避ける(後述) ・32bit / 64bit の両方を用意し、実行時に切り替える ([アンマネージドコードのクロスプラットフォーム化](MS_UnmanagedCodeCrossPlatform))
デザイン時に使用できる値は、実行時に使用できる値と異なる。
-
アプリケーションが実行されていないので、
共有メモリやグローバル変数などのデザイン時に読むことはできない。 -
デザインタイム・プロパティ(Open棟梁のカスタムコントロールのチェック属性のような)は利用可能。
-
app.config の値については、
以下のようにデザインタイムで利用可能であるもよう。- デザイン時に指定したDBのパス名を実行時に変更する方法 - Insider.NET - @IT
https://atmarkit.itmedia.co.jp/bbs/phpBB/viewtopic.php?topic=47969&forum=7 - 外部ファイルにコントロールのプロパティを格納する: .NET Tips: C#, VB.NET
https://dobon.net/vb/dotnet/programing/dynamicproperties.html
- デザイン時に指定したDBのパス名を実行時に変更する方法 - Insider.NET - @IT
補足(
app.configが「読める」ことの正体): 原文の「利用可能である
もよう」という慎重な書き方は妥当で、
読めるが、読んでいるのは想定と違うファイルである。【デザイン時に ConfigurationManager が読むもの】 アプリの App.config【ではなく】 → 【devenv.exe.config】(Visual Studio 自身の構成ファイル)★ または、.NET Core のデザイナでは → デザイナ サーバー プロセスの構成ファイル → 「値が取れない」「null になる」「既定値になる」 という症状の原因**参考リンクが示す「ダイナミック プロパティ」**は、
別の仕組みである。【ダイナミック プロパティ(Windows Forms)】 デザイナがプロパティ値を App.config に書き出し、 実行時に【生成されたコードが読み込む】 → InitializeComponent の中に ConfigurationManager.AppSettings[...] を埋める → デザイン時に config を読んでいるわけではない ★ 【現況】 この機能は現在のデザイナでは推奨されない → 設定は [.NET config](MS_DotNetConfig) / IOptions パターンで扱う
補足(デザイン時を判定する方法 ── 実務での必須知識): 本ページの
「対策」節が参考リンクのみのため、具体的な手段を補っておく。// ① Component.DesignMode(最も基本。ただし落とし穴あり) public partial class MyControl : UserControl { public MyControl() { InitializeComponent(); if (!DesignMode) // ← コンストラクタでは false になる ★ LoadDataFromDatabase(); } }【DesignMode の落とし穴】★ 最重要 ① 【コンストラクタの中では常に false】 → まだデザイナに関連付けられていないため → コンストラクタでの判定に使えない ② 【入れ子のコントロールでは false】 → 自分がデザインされている場合は true → 親がデザインされ、自分がその子の場合は false になる ③ WPF では別の API(後述)// ② より確実な判定(プロセス名を見る) static bool IsDesignTime => LicenseManager.UsageMode == LicenseUsageMode.Designtime || System.Diagnostics.Process.GetCurrentProcess().ProcessName is "devenv" or "DesignToolsServer"; // ← .NET Core のデザイナ ★// ③ LicenseManager(コンストラクタでも効く)★ public MyControl() { InitializeComponent(); if (LicenseManager.UsageMode == LicenseUsageMode.Runtime) LoadDataFromDatabase(); }WPF の場合:
// WPF は DesignerProperties を使う if (!System.ComponentModel.DesignerProperties.GetIsInDesignMode(this)) LoadData();<!-- XAML では d: 名前空間でデザイン時専用の値を与えられる ★ --> <UserControl xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" d:DataContext="{d:DesignInstance vm:MyViewModel, IsDesignTimeCreatable=True}"> <!-- ↑ デザイナ上でだけダミーのデータが表示される。実行時は無視される --> </UserControl>
d:名前空間はデザイン時の生産性を大きく上げる。
実データがなくてもレイアウトを確認できるためである
(XAMLの書き方(1) のバインディングと併用する)。
(子コントロールを持つ Form やカスタム コントロールの)
子コントロールを生成する処理を、コンストラクタに実装した場合、
デザイン時と実行時の表示が乖離する現象が発生する。
補足(なぜ二重に追加されるのか): 別ページで扱われる主題だが、
本ページの「デザイン時にコードが実行される」から自然に導けるので、
仕組みを補っておく。【二重追加が起きる流れ】 ① 【デザイン時】 デザイナがフォームのインスタンスを生成 → コンストラクタが走る → 子コントロールが 1 つ追加される ↓ デザイナが「このフォームには子コントロールが 1 つある」と認識 ↓ デザイナが【InitializeComponent に追加コードを書き出す】★ this.Controls.Add(this.myChild); ② 【実行時】 InitializeComponent が走る … 1 つ追加(デザイナが書いたコード) コンストラクタの続きが走る … もう 1 つ追加(自分で書いたコード) → 【合計 2 つ】★【対策】 ① コンストラクタで子コントロールを追加しない → OnLoad / Loaded イベントで行う ② DesignMode(または LicenseManager)で判定して、 デザイン時には追加しない ③ [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] を付け、デザイナに書き出させない ★ ④ そもそも【デザイナ ファイルを手で編集しない】 → [*.aspx.designer.cs(vb)を生成する方法](MS_ASPXDesignerGeneration) と 同じく、生成物は生成させる
下記のような対策の方法がある。
-
Windowsフォームのデザイン時に非実行のコードを書くには - Insider.NET - @IT
https://atmarkit.itmedia.co.jp/bbs/phpBB/viewtopic.php?topic=15316&forum=7 -
.NET:Tips > デザイン:デザイン時に処理を実行させない - YiaoWang
http://yiaowang.web.fc2.com/programing/vs_tips/design_04.html
補足(デザイナが壊れたときの切り分け手順): 本ページの内容を踏まえ、
実務で使える手順にまとめておく。【症状別の原因】 「デザイナーで例外が発生しました」 → コンストラクタ or カスタム コントロールの初期化で例外 → 【スタック トレースを必ず開いて読む】★ (デザイナのエラー画面に「詳細の表示」がある) 「型 'Xxx' を読み込めませんでした」 → ビルドが通っていない/DLL が見つからない → 【まずソリューションをビルドし直す】 → 32/64bit の不一致(前節) 「デザイナー サーバー プロセスとの通信に失敗しました」(.NET Core) → デザイナ プロセスが落ちた → VS を再起動、bin/obj を削除 「開くたびにコントロールの位置がずれる」 → コンストラクタでレイアウトを変更している → デザイナがその結果を .designer.cs に書き戻している ★ 「毎回 .designer.cs に不要な差分が出る」 → 同上。DesignerSerializationVisibility.Hidden を検討【設計上の原則】★ ① コンストラクタには【InitializeComponent 以外を書かない】 → 初期化は Load / Loaded、または明示的な Init メソッドへ ② 【外部リソースにデザイン時に触らない】 → DB、ファイル、ネットワーク、レジストリ、共有メモリ ③ 触る必要があるなら【必ず DesignMode 判定で囲む】 ④ カスタム コントロールは【デザイン時に落ちないこと】を 受け入れ条件に含める → 落ちると、そのコントロールを使う全画面が編集不能になる ★なお、根本的な回避策は「デザイナに依存しない」ことである。
・WPF / [XAMLの書き方(1)](MS_XAMLWriting1) は 【XAML を手で書く】のが普通 → デザイナはプレビューとして使う → デザイン時実行の問題に巻き込まれにくい ・[Blazor](MS_Blazor) / Web も同様(デザイナがない) ・Windows Forms でも、動的な画面はコードで組む方が安定する
Tags: 移行, .NET開発, UIサブシステム, Windows Forms, ASP.NET Web Forms
このWikiは「Open棟梁Project」,「OSSコンソーシアム 開発基盤部会」によって運営されています。