Formクラス(System.Windows.Forms.Form)において、
Loadイベントの処理内にCloseメソッドを呼び出してフォームを閉じようとしています。
-----
class Form2 {
// 他のメンバは略
private void Form2_Load(object sender, System.EventArgs e) {
Close();
}
}
-----
ところが、このForm2を、ShowDialogで表示したとき、
Closeを無視して普通に表示されてしまいます。
-----
Form2 form = new Form2();
form.ShowDialog();
// Form2はちゃんと表示されてしまう!
-----
上記コードは簡単に問題を再現するためのものです。
実際にはForm2_Loadにおいてある条件が満たされたとき(エラー発生など)にClose()を
呼んでいます。
ですので、閉じてくれないと困るのです。
【質問】
ShowDialogでフォームを表示するとき、
Loadイベント処理中にフォームを閉じるにはどうしたら良いのでしょうか。
Closeメソッドを呼んだだけでは上記の通り閉じません。
フォーム表示前に閉じることができるのであれば、Loadイベント中で無くても良いです。
環境はWindowsXP Professinal SP1 + Visual Studio.NET + C# です。
Googleでざっと検索もしてみました。(ShowDialog Load Closeあたりのキーワードで)
同じ問題で悩んでいる人は何人かいるようですが、
解決方法は見つかりませんでした。
Load イベントで Close が効かないのが仕様なら
アプローチを変えればよいかと。
条件次第(初期化に失敗したとか)で Close したいんですよね?
そもそも ShowDialog() しなければ済みそうですよね。
フォームの初期化メソッドを実装して、
ShowDialog する前に それを呼ぶ。
初期化メソッドが失敗したなら ShowDialog しない。
どうですか?
こんにちは。C#をやって半月ほどの者です。
ShowDialog()では無理だと思われます。
ShowDialog()はモーダルダイアログにするときに使うはずだったと…。
ちなみにShowDialog()を呼んだ場合、リソースを開放するDispose()が必要です。
Show()を呼んだ場合は、自動的にDisposeされます。
DisposeはClose()するときに呼ばれます。
レスありがとうございます。
To: suzuka様
> 条件次第(初期化に失敗したとか)で Close したいんですよね?
その通りです。
> そもそも ShowDialog() しなければ済みそうですよね。
> フォームの初期化メソッドを実装して、
> ShowDialog する前に それを呼ぶ。
> 初期化メソッドが失敗したなら ShowDialog しない。
> どうですか?
その方法は「最後の手段」にしたいと思います。
初期化失敗してFormをCloseするのは、Form側の役目だと思います。
FormをShowする側に対し、何らかの役割(この場合初期化メソッドを呼ぶこと)を
強要させたくありません。
普通、Form派生クラスであれば、以下のコードでモーダルダイアログとして
表示できます。
----- Formを表示するコード(1) ------
HogeForm form = new HogeForm();
if (form.ShowDialog() == DialogResult.OK) {
// ...以下色々
-----
一般的なForm派生クラスでできるのに、今、私の作成したForm派生クラスで
できないのはやはり不自然です。
よって、可能な限り回避したいです。
さらに理想を言うと、ただのFormとしても扱えるようにしておきたいです。
----- Formを表示するコード(2) -----
Form form = new HogeForm();
if (form.ShowDialog() == DialogResult.OK) {
// ...以下色々
-----
(2)で表示できるようにしておけば、Template Methodパターンに使えたりと
色々応用ができるからです。
ただし、本当に(2)で表示できるようにしておいたほうが実用的かと言われると、
結構疑わしいです。理想が過ぎる気がします。
でも(1)で表示できるようにはしておきたいです。
レスありがとうございます。
To: 杏様
> ShowDialog()では無理だと思われます。
> ShowDialog()はモーダルダイアログにするときに使うはずだったと…。
モーダルダイアログにしたいのでShowDialogを呼んでおります。
> ちなみにShowDialog()を呼んだ場合、リソースを開放するDispose()が必要です。
> Show()を呼んだ場合は、自動的にDisposeされます。
> DisposeはClose()するときに呼ばれます。
CloseするときにDisposeが呼ばれるならば、
Disposeを明示的に呼ぶ必要があるのはどのような場合でしょうか?
CloseってShowDialogから戻る前に必ず(除く例外)呼ばれますよね?
とりあえず解決策を一つ見つけました。
Form派生クラスでShowDialogを再定義し、FormクラスのShowDialogを隠蔽してしまう。
Form派生クラスで再定義したShowDialogは以下のようになります。
-----
public class HogeForm : System.Windows.Forms.Form {
public new DialogResult ShowDialog() {
if (ある条件) {
return DialogResult.Calcel;
}else {
return super.ShowDialog();
}
}
}
-----
(※ShowDialogはオーバーロードされているので、正確には2つ定義する必要がありま
す)
これで、呼び出し側は、上記(1)のコードでモーダルダイアログを表示できるように
なりました。
しかし、(2)のコードではダメです。
ShowDialogがvirtualメソッドであれば完璧だったんですが、
残念ながらそうではありません。
これでは片手落ちです。
(2)のコードでもダイアログを表示せずに、
ShowDialogから戻ってきてくれるような方法をどなたかご存知でしたらお教え願います。
(誤)
return super.ShowDialog();
(正)
return base.ShowDialog();
>その方法は「最後の手段」にしたいと思います。
>初期化失敗してFormをCloseするのは、Form側の役目だと思います。
必ずしも そんなことないと思いますよ。
議論する気はありませんけど。
コンストラクタで初期化を行うようにして、
失敗時には
throw new ApplicationException();
するようにする。
で、
Form2 f2 = null;
try
{
f2 = new Form2();
f2.ShowDialog();
}
catch (ApplicationException ae)
{
}
finally
{
//f2.Dispose(); 必要なのかな?
}
ってのが妥当な気がしてきました。
検証してませんけど。
どうしても Load イベントでの Close にこだわりたい場合
お役に立てません。がんばってください。
郷に従うのが楽でいいと思う…
返事が遅れてすみません。
Dispose()は、
「コントロールによって使用されている全てのリソースを開放するためのメソッド」
です。
まだC#をやって1ヶ月くらいしかたたないので、
詳しくは分かりませんが、
試しに
Form2 form2 = new Form2();
form2.ShowDialog();
でやってみましたが、今のところ何ら問題はなかったんですよね。
なぜShow()は自動的に呼ばれるか、
もう少し勉強しないといけないですね…。
どなたか分かる方がいらっしゃったらお願いします。
余談ですが、
私はここを見てDispose()がかかれていたので発言したのですが…。
どうでしょうか。
http://homepage3.nifty.com/midori_no_bike/CS/
>試しに
> Form2 form2 = new Form2();
> form2.ShowDialog();
>でやってみましたが、今のところ何ら問題はなかったんですよね。
この場合、ガベージコレクタが form2 を回収するまで
フォームに関するリソース(ウィンドウハンドル)とかが
解放されないのではないかと思います。
(特に)9x 系で繰り返すと
まずいことになるかもしれないです。
.NET の情報は集めてるんですが
実質でほとんど使ったことないので
確信は持てないのですが…
To: suzuka様
To: 杏様
まず、Disposeについてです。
MSDNのサンプルソースには大概ShowDialogの後Disposeが入っていますので、
入れておいたほうが無難でしょう。
ガベージコレクション時のにフォームのインスタンスが回収されると、
Disposeが呼ばれるので、
C#がシステムリソースが減ったときにガベージコレクタを動かしてくれれば
Disposeがなくてもさほど問題がないと思います。
(システムリソースが減ったときにガベージコレクタが動くかどうかは不明)
わざわざDisposeを呼ばないとならない理由ですが、
おそらく以下のようなFormの使い方を想定しているのでしょう。
フォームのインスタンスは再利用できます。
以下のコードでは4回HogeFormが表示されます。
-----
Form form = new HogeForm();
form.ShowDialog();
form.ShowDialog();
form.ShowDialog();
form.ShowDialog();
form.Dispose();
-----
1つのShowDialogから返ってきたとき、
formはHideされた状態にあるようです。
何度も使うフォームはインスタンスフィールドとしておくと良いのかもしれません。
また、ShowDialog用フォームクラスを設計するときは、
こうした使い方もあることを念頭においておく必要があります。
本題であるShowDialog表示時のLoadイベント処理中にフォームを閉じる方法についてで
す。
まず、ShowDialogではなく、Showで表示したときは
Loadイベント処理中にCloseを呼び出すことにより問題なくフォームが閉じられます。
また、上記の通り、ShowDialogは、システムリソースを解放することなく
フォームをHide状態にしただけで戻ってきます。
ここで一つの仮説を立ててみます。
ShowDialog表示時は、Show表示時と異なり、Closeを呼び出してもHideしかしない。
(※本来、ShowDialog表示時はDialogResultプロパティへの代入で
Hide状態になるべきであり、Closeを呼ぶのはあまり好ましくないでしょう。)
もう一つ仮説を立ててみます。
ShowDialogでは、Loadイベントを処理してから、フォームをShow状態にする。
この2つの仮説をあわせると、
Loadイベント処理中にCloseを呼び出してHideしたところで、
すぐ後にShow状態になってしまい、フォームはちゃんと表示されてます。
仮説のまま進めたくは無いのですが、確認する方法が分かりませんし
仮説が間違えている証拠は見つからなかったのでこれを前提に考えてみますと、
Loadイベント処理中にHideやCloseを使わずにフォームを見えなくしておき(A)、
Show状態になった後、どこかのイベント処理でDialogResultに
値を代入すれば良いでしょう(B)。
(A)を実現するための方法として2つ見つけました。
コードを記述すると以下のようになります。
-----(方法1)
ShowInTaskbar = false;
FormBorderStyle = FormBorderStyle.None;
Size = new Size(0, 0);
-----(方法2)
Opacity = 0;
ShowInTaskbar = false;
-----
方法2は一瞬白い四角っぽいものが表示されることがありますので
どちらかというと方法1の方が良いでしょう。
(B)のDialogResultに値を代入するイベントは、
Paintです。
この方法を使えば、私の環境では「Formを表示するコード(2)」でも
「フォームが表示されずにShowDialogが終了する」ことを確認しました。
とりあえず問題を解決する方法が1つは見つかりましたので
解決にチェックは入れておきます。
しかし、この方法は、他の環境でもフォームが表示されずに済むか分かりませんし、
無理やり目的を達成してしまっているような不自然な処理だとも思いますので
(同じ問題を抱えている人には)お勧めはしません。