关闭窗体后处理

16

我是一个有用的助手,可以为您翻译文本。

我遇到了在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("");
    }
}

编辑: 我的问题是:为什么在关闭表单2后5秒钟才显示消息框!


1
为什么你想在垃圾回收器处理之前就丢弃表单(假设没有对表单的引用)? - Lazarus
2
“它没起作用”是什么意思?是否引发了异常?窗口是否没有消失?@Lazarus:好问题。 - DHN
关闭窗体后,我需要释放该窗体吗? - Reza Aghaei
5个回答

11
这个问题实际上与Dispose有关。首先,Dispose与垃圾回收没有任何关系。具体情况如下:
1. 你有一个全局的计时器实例。 2. 你创建form2。 3. Form2订阅计时器。 4. Form2被关闭和/或Disposed。 5. 计时器事件触发,增加计数器并显示一个MessageBox。 6. 计时器事件持续触发,直到应用程序关闭。
需要理解的主要点是Close / Dispose只改变了Form的状态,它们不能“删除”实例。因此,(已关闭的)表单仍然存在,计数器字段仍然存在,并且事件触发。
好的,第一部分:使用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实例的引用,并防止其被收回。但它并不能防止Form2被Disposed/Close,所以你的计时器会持续为已关闭的表单触发,这通常会失败并导致其他问题。
  1. 不要这样做(给Form2自己的定时器)
  2. 使用FormClosed事件取消订阅:Program.timer.Tick -= timer_Tick;

2
+1 建议使用 using 语句,也许可以给他一个例子。 - jsmith
亲爱的Henk Holterman,移除计时器事件是一个好的解决方案,但我的问题是,为什么当窗体已经被释放时,消息框还会显示出来? - Mironline
1
@Mironline:为什么不行呢?无论是计时器还是消息框都不需要那个窗体。试着在timer_Tick中设置控件属性,你会得到一个异常。 - H H

4

在使用完Form后,最简单可靠的处理方法是将其用法放在using块中。

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

C#中的using块是一种构造,它本质上将以上内容扩展为以下代码。

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

在“秒”块中,访问form2之前必须进行初始化。如果已经初始化,则不会等于null。我尝试过了,但消息框会在5秒后显示。 - Mironline

3
这是一个老问题,但它涉及到对象如何工作的一些有趣的点。表单本质上是一个对象。同一类的所有对象共享相同的方法,但每个对象都有自己的数据。这是什么意思?这意味着关闭或处理对象并不会从内存中释放/删除/移除任何代码,只有数据。那些关于对象的内容,无论使用哪种语言都是一样的。
现在,具体来说你的代码。让我们看看Program.timer.Tick += timer_Tick;这一行的作用。它为Form object中的函数提供了一个指针给timer object。那么,现在,无论您对Form object做什么,timer object都将继续调用该函数。计时器对象不关心您的窗体甚至不知道窗体对象的存在。它只关心您传递指针的函数。就计时器对象而言,这个函数是一个独立的函数。
Form.Close()做了什么?Form.Close()处置了表单使用的资源,即标记了表单的控件进行垃圾回收除非使用ShowDialog显示表单。在这种情况下,必须手动调用Dispose()。MSDN 不用说(或者也许不是那么不用说),如果关闭/处理表单会清除内存中的函数,那么计时器对象将具有无效的指针,并且您的程序将在5秒后崩溃

1
也许我理解问题有误,但我认为这位先生需要知道的是,要关闭一个以Form2.ShowDialog()打开的窗体(比如说form2),你需要在Form2中设置Form2.DialogResult。只需设置该成员即可关闭窗体并返回结果。

0

form.ShowDialog()会将窗体显示为模态对话框。这意味着调用不会返回,直到窗体关闭。
请注意,单击模态对话框上的关闭X并不会关闭窗体,它只是隐藏了它。我猜这就是让你感到困惑的原因。 如果你希望form1中的代码继续执行而不是阻塞,你应该调用Show()而不是ShowDialog()。非模态对话框将在单击X时关闭。

如果您确实需要一个阻塞的模态对话框,您应该像其他答案中所述那样使用using块来包装窗体。
构建模态对话框时,通常会添加一个“确定”按钮或类似的按钮,并将表单的AcceptButton属性设置为该按钮,以允许用户通过按Enter键关闭窗体。同样,您可以添加一个“取消”按钮,并将CancelButton属性设置为捕获Esc键。
为两个按钮添加单击处理程序,相应地设置窗体的DialogResult属性并调用Close()。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接