web-dev-qa-db-ja.com

閉じた後にフォームを破棄します

C#でフォームを開いたり閉じたりする際に新しい問題が発生しました。

私の問題は、閉じた後にフォームをどのように破棄するかです。

これが私のコードです:

Program.cs:

static class Program
{
    public static Timer timer;

    [STAThread]
    static void Main()
    {
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);

        timer = new Timer { Interval = 1000};
        timer.Start();

        Application.Run(new Form1());
    }
}

Form1.cs:

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    private void button1_Click(object sender, EventArgs e)
    {
        Form2 form = new Form2();
        form.ShowDialog();
       /// I've tried Dispose() method . but didn't work
    }
}

Form2.cs:

public partial class Form2 : Form
{
    public Form2()
    {
        InitializeComponent();
    }

    private void Form2_Load(object sender, EventArgs e)
    {
        Program.timer.Tick += timer_Tick;    
        Close();
        // I've tried Dispose() method instead of Close() but didn't work
    }

    private int count = 0; 
    void timer_Tick(object sender, EventArgs e)
    {
        count++;
        if (count == 5) MessageBox.Show("");
    }
}

編集済み:私の質問は:form2が閉じた5秒後にメッセージボックスが表示される理由です!

13
Mironline

編集:この質問は、廃棄に関するものであることが判明しました。

まず、Disposeはガベージコレクションとは関係がありません。次のことが起こります。

  1. グローバルタイマーインスタンスがあります
  2. Form2を作成します
  3. Form2はタイマーをサブスクライブします
  4. Form2は閉じられているか、破棄されています
  5. タイマーイベントが発生し、カウンターがインクリメントされ、MessageBoxが表示されます
  6. タイマーイベントは、アプリが閉じるまで発生し続けます。

理解すべき主なポイントは、Close/Disposeはフォームのステータスを変更するだけであり、インスタンスを「削除」しない(できない)ということです。したがって、(閉じた)形式があり、カウンターフィールドがまだあり、イベントが発生します。


OK、パート1:

using () {}ブロックの方が良いでしょうが、これはうまくいくはずです:

    private void button1_Click(object sender, EventArgs e)
    {
        Form2 form = new Form2();
        form.ShowDialog();
       /// I've tried Dispose() method . but didn't work
        form.Dispose(); // should work
    }

そうでない場合は、「機能しない」と説明してください。


    private void Form2_Load(object sender, EventArgs e)
    {
        Program.timer.Tick += timer_Tick;    
        Close();
       /// I've tried Dispose() method instead of Close() . but didn't work
    }

これは奇妙なことですが、それは質問の人工的なコードだと思います。

グローバルProgram.Timerは、Form2インスタンスへの参照を保存し、収集されないようにします。破棄/クローズを妨げることはないので、タイマーはクローズドフォームに対して起動し続けます。これは通常失敗し、他の問題を引き起こします。

  1. これを行わないでください(Form2に独自のタイマーを指定してください)
  2. FormClosedイベントを使用して登録を解除します:Program.timer.Tick -= timer_Tick;
9
Henk Holterman

使用後にFormを破棄する最も簡単で信頼性の高い方法は、使用をusingブロック内に配置することです。

using (Form2 form = new Form2()) {
  form.ShowDialog();
}

C#のusingブロックは、基本的に上記を次のコードに拡張する構造です。

Form2 form;
try {
  form = new Form2(); 
  ...
} finally {
  if ( form != null ) {
    form.Dispose();
  }
}
4
JaredPar

これは古い質問ですが、オブジェクトがどのように機能するかについていくつかの興味深い点に触れています。フォームは、本質的にはオブジェクトです。同じクラスのすべてのオブジェクトは同じメソッドを共有しますが、それぞれに独自のデータがあります。これは何を意味するのでしょうか?これは、オブジェクトを閉じたり破棄したりしても、メモリからcodeが解放/削除/削除されないことを意味します。データのみ。それはすべて、言語に関係なく、オブジェクト全般に関するものでした。

さて、特にあなたのコードについて。行Program.timer.Tick += timer_Tick;が何をするかを調べてみましょう。これにより、フォームオブジェクト内の関数へのポインタがタイマーオブジェクトになります。したがって、これで、フォームオブジェクトに対して何をしても、タイマーオブジェクトはその関数を呼び出し続けます。タイマーオブジェクトフォームを気にせず、フォームオブジェクトの存在さえ認識しませんポインタを渡した関数のみを考慮します。タイマーオブジェクトに関する限り、この関数はスタンドアロン関数です。

Form.Close()は何をしますか? Form.Close()は、フォームで使用されるリソースを破棄します。別名、ガベージコレクション用のフォームのコントロールをマークしますnlessフォームはShowDialogを使用して表示されます。その場合、Dispose()を手動で呼び出す必要があります。 [〜#〜] msdn [〜#〜]

言うまでもなく(またはそれほど不必要ではないかもしれませんが)、フォームを閉じる/破棄すると、関数がメモリからクリアされ、タイマーオブジェクトに無効なポインタがあり、プログラムは5秒後にcrashになります。

3
ThunderGr

おそらく私は質問を間違って読んでいますが、紳士は、Form2.ShowDialog()として開かれたフォーム(たとえばform2)を閉じるには、Form2内でForm2.DialogResultを設定する必要があることを知っておく必要があると思います。そのメンバーを設定するだけで、フォームを閉じて結果を返すことができます。

1
Snowpaque

form.ShowDialog()は、フォームをモーダルダイアログとして表示します。これは、フォームが閉じられるまで通話が戻らないことを意味します。
モーダルダイアログで閉じるXをクリックしてもフォームは閉じず、非表示になるだけであることに注意してください。これがあなたを混乱させるものだと私は推測しています。 form1のコードをブロックするのではなく実行し続ける場合は、ShowDialog()ではなくShow()を呼び出す必要があります。 Xをクリックすると、非モーダルが閉じます。

ブロッキングモーダルダイアログが必要な場合は、他の回答で説明されているように、フォームをusingブロックで囲む必要があります。
モーダルダイアログを作成するときは、通常、「OK」ボタンなどを追加し、フォームのAcceptButtonプロパティをそのボタンに設定して、ユーザーがEnterキーを押してフォームを閉じることができるようにします。同様に、「キャンセル」ボタンを追加し、キャンセルボタンプロパティを設定してEscキーをキャプチャすることができます。
2つのボタンにクリックハンドラーを追加し、それに応じてフォームのDialogResultプロパティを設定して、Close()を呼び出します。

0
Holstebroe