- PR -

WindowsFormの状態を保持するには?

投稿者投稿内容
Jitta
ぬし
会議室デビュー日: 2002/07/05
投稿数: 6267
お住まい・勤務地: 兵庫県・海手
投稿日時: 2004-01-09 11:34
 モーダルで表示すると、DialogResultプロパティを設定してもフォームは“消え”ます。MSDNによると、
引用:

ms-help://MS.VSCC/MS.MSDNQTR.2003FEB.1041/cpref/html/frlrfSystemWindowsFormsFormClassDialogResultTopic.htm
↑これって、どんな環境でも通じるの?

ユーザーがダイアログ ボックスの閉じるボタンをクリックした場合や、 DialogResult プロパティの値が設定された場合は、 Close メソッドが自動的に呼び出されることはありません。その場合はフォームが非表示になるだけで、ダイアログ ボックスの新しいインスタンスを作成しなくてもそのフォームを再表示できます。このため、フォームがアプリケーションで不要となった場合は、そのフォームの Dispose メソッドを呼び出す必要があります。



 CloseメソッドがDisposeメソッドと全く同等の処理を行う必要はなく、モーダルの場合は「Closeは「非表示」という意味である」と解釈すれば、Closeメソッド内でモーダルかモードレスかを判別し、処理を振り分けてもガイドラインに沿っていることになると思います。先に参照したページでも、「Close何タラではなく、Disposeだ」と書かれているとおりではないでしょうか。
 Disposeメソッドは複数回コールされることを前提にコーディングされるべきであり、なんど呼んでもそのことによる不都合はありません。

###
http://www.users.gr.jp/ml/archive/list.aspx?name=aspx&no=3301
この後、3320くらいからだったんだよなぁ
きくちゃん
ぬし
会議室デビュー日: 2003/08/01
投稿数: 854
お住まい・勤務地: 都内某所
投稿日時: 2004-01-09 12:28
Jittaさん、こんにちは。

引用:

ms-help://MS.VSCC/MS.MSDNQTR.2003FEB.1041/cpref/html/frlrfSystemWindowsFormsFormClassDialogResultTopic.htm
↑これって、どんな環境でも通じるの?


MSDN Library のリリースで微妙に違いますね。
2003年10月リリースでは
ms-help://MS.VSCC/MS.MSDNQTR.2003OCT. 〜
でした。

引用:

ンスタンスを作成しなくてもそのフォームを再表示できます。このため、フォームがアプリケーションで不要となった場合は、そのフォームの Dispose メソッドを呼び出す必要があります。


ShowDialog で 表示したフォームは、
1)DialogResult プロパティが DialogResult.None 以外のボタンをクリックした場合
2)Hide() または Close() を呼び出した場合
3)フォームのVisibleプロパティに False を指定した場合
4)フォームの DialogResult プロパティに DialogResult.None 以外を指定した場合
5)×ボタンやAlt + F4 等を使用した場合
の全てで、同一の挙動を示すように設計されていたんですね。
# 当然といえば当然か...。

しかし、という事は何も悩む必要は無いわけですよねぇ。
元質問の

引用:

しかし、WindowsFormsライブラリの設計上、同一インスタンスのFormに対してShowDialogとCloseを繰り返し使用することは想定外であると聞き、この方法は使えないことがわかりました。


というのは、どこから聞いた話なんでしょうか...。
Hiro
会議室デビュー日: 2004/01/08
投稿数: 12
投稿日時: 2004-01-09 20:20
外出先から戻ってきて、ようやくここを見ることが出来ました。
みなさんたくさんの返信ありがとうございます。

引用:
で、やってるうちに気付いたんですが、ShowDialog で表示されたフォーム側で Hide() した場合も、呼出元に戻りますねぇ...。



私も始めて知りました。てっきり処理が戻ってこないものだと・・・
しかし、ShowDialogした後にHideして、再びShowDialogするのはあまり良くないかもしれません。
再びShowDialogした後ですと、ToolTipやMouseHoverイベントが無効になってしまいます。(これも想定外の使い方??)


引用:
元質問の

引用:


しかし、WindowsFormsライブラリの設計上、同一インスタンスのFormに対してShowDialogとCloseを繰り返し使用することは想定外であると聞き、この方法は使えないことがわかりました。




というのは、どこから聞いた話なんでしょうか...。



これはMicrosoftの方に聞きました。
このことを知らずにアプリケーションを作ってしまったばっかりにアプリケーションの制御構造を変更する必要に迫られてしまい、今回のスレッドを立てた次第です。
これって、Windowsプログラマの中では常識なのでしょうか?
Closeした後にShowDialogしてはならないなら、Closeした後にはShowDialog出来ないようにするとか、ShowDialogすると例外を出すとか、そういう構造になっていてしかるべきだと思うのですが・・・。
きくちゃん
ぬし
会議室デビュー日: 2003/08/01
投稿数: 854
お住まい・勤務地: 都内某所
投稿日時: 2004-01-09 21:51
Hiroさん、こんばんは。

引用:

再びShowDialogした後ですと、ToolTipやMouseHoverイベントが無効になってしまいます。(これも想定外の使い方??)


なんと!
そんな制限(?)があったとは...。

引用:

これって、Windowsプログラマの中では常識なのでしょうか?


どうなんでしょう。私は初めて聞きました。
というか、モーダルなフォームを Close しても破棄されないという事自体、知りませんでしたし。

という事で、

【方法1】
ShowDialog()から制御が戻った際に、データを受け取ってからDispose()する。再度表示する場合は新しいインスタンスを生成して、保持しているデータを元に内容を復元する。

【方法2】
以前の投稿で提案したように、ShowDialog()は使わずにイベントとかデリゲートとかで制御クラスに通知して画面の遷移を行う。

とかじゃないですかねぇ。
Jubei
ぬし
会議室デビュー日: 2002/03/02
投稿数: 830
お住まい・勤務地: 関西
投稿日時: 2004-01-10 00:46
諸農です。

スレッドとは直接関係ありません。すみません。

引用:

Jittaさんの書き込み (2004-01-09 11:34) より:

MSDNによると、
引用:

ms-help://MS.VSCC/MS.MSDNQTR.2003FEB.1041/cpref/html/frlrfSystemWindowsFormsFormClassDialogResultTopic.htm
↑これって、どんな環境でも通じるの?






ですが、ウチはMSDN(最新の10月リリース分を入れています)と統合しているせいか、

VS2002の場合は
ms-help://MS.VSCC/MS.MSDNQTR.2003OCT.1041/cpref/html/frlrfSystemWindowsFormsFormClassDialogResultTopic.htm

VS2003の場合は
ms-help://MS.VSCC.2003/MS.MSDNQTR.2003OCT.1041/cpref/html/frlrfSystemWindowsFormsFormClassDialogResultTopic.htm

と言う感じで、OCTに変わっています。
あと、ドキュメントアップデートも行っています。

HELPからの引用は、FCL SDKのアドレスの方がいいのかもしれないですね。

ではでは(^^)/
_________________
諸農和岳
Powered by Turbo Delphi & Microsoft Visual Studio 2005

十兵衛@わんくま同盟
http://blogs.wankuma.com/jubei/
iStation
大ベテラン
会議室デビュー日: 2003/12/08
投稿数: 158
投稿日時: 2004-01-10 01:02
こんな感じではいかがですか?

// Form1
 ...
 [STAThread]
 static void Main()
 {
  Application.Run(new Form1());
 }

 private Form2 _frmForm2 = null;

 private void btnNext_Click(object sender, System.EventArgs e)
 {
  if(_frmForm2 == null || _frmForm2.IsDisposed)
  {
   _frmForm2 = new Form2();
   _frmForm2.Owner = this;
  }
  _frmForm2.Show();
  this.Hide();
 }
 ...

// Form2
 ...
 private Form3 _frmForm3 = null;

 private void btnNext_Click(object sender, System.EventArgs e)
 {
  if(_frmForm3 == null || _frmForm3.IsDisposed)
  {
   _frmForm3 = new Form3();
   _frmForm3.Owner = this;
  }
  _frmForm3.Show();
  this.Hide();
 }

 private void btnBack_Click(object sender, System.EventArgs e)
 {
  this.Owner.Show();
  this.Close();
 }
 ...

// Form3
 ...
 private void btnBack_Click(object sender, System.EventArgs e)
 {
  this.Owner.Show();
  this.Close();
 }
 ...

[ メッセージ編集済み 編集者: iStation 編集日時 2004-01-10 08:41 ]
架空兎
ベテラン
会議室デビュー日: 2003/08/18
投稿数: 78
お住まい・勤務地: さいたま氏
投稿日時: 2004-01-12 18:29
引用:

Hiroさんの書き込み (2004-01-08 10:12) より:

1→2→3と画面が進んで行くのですが、3→2→1と戻ることも出来るようになっています。つまり、画面の遷移は前後に自由に行えます。
そして、画面を戻った時には最後に表示した画面と同じ状態(画面への入力内容やフォーカス位置など)で表示させたいと思っています。


引用:

きくちゃんさんの書き込み (2004-01-09 21:51) より:

【方法1】
ShowDialog()から制御が戻った際に、データを受け取ってからDispose()する。再度表示する場合は新しいインスタンスを生成して、保持しているデータを元に内容を復元する。

【方法2】
以前の投稿で提案したように、ShowDialog()は使わずにイベントとかデリゲートとかで制御クラスに通知して画面の遷移を行う。


このようなケースの場合は、私は方法1より方法2の方がいいと思うのですが、
ただ、デリゲートではなくクラスを使った"委譲"の方がいいのかな?と思いました。

一応、サンプルを。

コード:

// 画面遷移を行うフォームの基本クラスです。
public class FormBase : System.Windows.Forms.Form {
    private FormTransferable formTrans = null;

    public void SetFormTransferable(FormTransferable formTrans) {
        this.formTrans = formTrans;
    }
    protected void GoNextForm(object arg) {
        if (this.formTrans != null) {
            this.formTrans.GoNext(this, arg);
        }
    }
    protected void GoPreviousForm(object arg) {
        if (this.formTrans != null) {
            this.formTrans.GoPrevious(this, arg);
        }
    }
}

// このクラスのサブクラスで画面遷移の実装を行います。
public abstract class FormTransferable {
    private Form source = null;

    public Form Source {
        get {
            return this.source;
        }
        set {
            this.source = value;
        }
    }

    public virtual void GoNext(Form current, object arg) {
    }
    public virtual void GoPrevious(Form current, object arg) {
    }
}

// フォーム A
public class FormA : FormBase {
    private void btnNext_Click(object sender, System.EventArgs e) {
        base.GoNextForm(パラメータ);
    }
}

// フォーム B
public class FormB : FormBase {
    private void btnPrevious_Click(object sender, System.EventArgs e) {
        base.GoPreviousForm(null);
    }
}

// フォーム A からフォーム B へ画面を遷移します。
public class FormTransferableA : FormTransferable {
    public override void GoNext(Form current, object arg) {
        FormTransferable formTrans = new FormTransferableB();
        FormBase form = new FormB();

        formTrans.Source = current;
        form.SetFormTransferable(formTrans);
        current.Hide();
        form.Show();
    }
}

// フォーム B から遷移元 ( フォーム A ) へ画面を戻します。
public class FormTransferableB : FormTransferable {
    public override void GoPrevious(Form current, object arg) {
        current.Close();
        base.Source.Show();
    }
}

// エントリポイントです。
public class Sample {
    [STAThread]
    static void Main() {
        FormBase form = new FormA();

        form.SetFormTransferable(new FormTransferableA());
        Application.Run(form);
    }
}


このサンプルではパラメータを object 型で受け渡していますが、
できればパラメータ用のクラスを作ってそれを受け渡した方が良いかも知れません。
あと、パラメータを受け取ったら適切な型にキャストして使います。

ふと思ったのですが、方法2を使う場合、制御クラスを作るメリットが
あまり感じられないのは私だけでしょうか?
#直接フォームを表示してもいいような・・・。
きくちゃん
ぬし
会議室デビュー日: 2003/08/01
投稿数: 854
お住まい・勤務地: 都内某所
投稿日時: 2004-01-13 13:17
架空兎さん、こんにちは。

引用:

ふと思ったのですが、方法2を使う場合、制御クラスを作るメリットが
あまり感じられないのは私だけでしょうか?
#直接フォームを表示してもいいような・・・。


うーん、どうなんでしょう。
ビジネスロジックとかを制御クラスに集約して、各フォーム側は自身のUI制御のみに徹する作りにすれば、「見通しの良さ」が確保出来るかなぁ、とか、(汎用的なフォームなら)部品として外に出せるかなぁ、とか思いますけど。
ま、ケース・バイ・ケースでしょうか...。

スキルアップ/キャリアアップ(JOB@IT)