none
showDialog一个窗口,然后使用close关闭,接着提示一个消息(messagbox.show)。为什么窗口没有关闭就提示了消息? RRS feed

  • 问题

  • showDialog一个窗口,然后使用close关闭,接着提示一个消息(messagbox.show)。为什么窗口没有关闭就提示了消息?请指点?

    测试的脚本如下:
    新建一个窗口,增加一个按钮,和一个timer控件,增加timer1_Tick事件。

     Form f1 = new Form();

    //按钮脚本
            private void buttonShow_Click(object sender, EventArgs e)
            {

                timer1.Start();
                f1 = new Form();
                f1.ShowDialog();

            }

    //timer的事件      
      private void timer1_Tick(object sender, EventArgs e)
            {
                timer1.Stop();
                f1.Close();
                MessageBox.Show("看看响应窗口是否关闭了?");
                f1.Dispose();
            }


     如果f1.Close(); 后面增加f1.Dispose();那么主程序界面不显示了,只剩下了消息提示了。

    2009年7月23日 7:07

答案

  • 我想了一下,原因应该是这样的:
    你使用ShowDialog来显示窗体,会将窗体显示为模式对话框,并将当前活动窗口设置为它的所有者。而子窗体需要处理的系统消息应该是他的所有者转发的,理解这一点很重要!
    你使用的这个记时器(System.Windows.Forms.Timer),处理任务的时候,主窗体是不能响应消息的:

    1,如果你使用ShowDialog方法的话,会将窗体显示为模式对话框,并将当前活动窗口(主窗体)设置为它的所有者。这时主窗体不能响应消息,也就不能把对f1的关闭的消息传递给f1,所以f1也不能关闭,只能执行后面的代码,这时显示了你定义的消息框,当你把消息框点掉,当记时器任务结束了,这是主窗体有可以响应消息了,所以这是才能把f1的关闭消息传给f1,f1这时才可以关闭

    2,如果你使用Show方法的话,主窗体并不是f1的所有者,虽然执行记时器任务的时候主窗体仍然不能响应消息,但是这时f1自己可以响应消息的,所以马上就可以关闭掉f1了!

    希望这个解释对你有帮助啊!
    周雪峰
    2009年7月23日 9:29
    版主
  • using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Linq;
    using System.Text;
    using System.Windows.Forms;
    using System.Threading;
    
    namespace X.WinFormsApp
    {
        /// <summary>
        /// 标题:这只是一个简单测试,要用于实际还需要自己丰富代码
        /// </summary>
        public partial class X200905211105 : Form
        {
            private Panel fDisablePanel;
    
            public X200905211105()
            {
                InitializeComponent();
                this.InitDisableMask();
            }
    
            private void InitDisableMask()
            {
                this.fDisablePanel = new Panel();
                this.fDisablePanel.Visible = false;
                this.fDisablePanel.Size = this.ClientSize;
                this.fDisablePanel.BackColor = Color.Blue;
                Label loading = new Label();
                loading.Text = "Loading...";
                this.fDisablePanel.Controls.Add(loading);
                this.Controls.Add(this.fDisablePanel);
    
                Button btnStart = new Button();
                btnStart.Text = "开始异步";
                btnStart.Click += new EventHandler(btnStart_Click);
                this.Controls.Add(btnStart);
            }
    
            private void btnStart_Click(object sender, EventArgs e)
            {
                this.RunWorkerAsync();
            }
    
            public void RunWorkerAsync()
            {
                this.fDisablePanel.Visible = true;
                this.fDisablePanel.BringToFront();
    
                using (BackgroundWorker backgroundWorker = new BackgroundWorker())
                {
                    backgroundWorker.DoWork += new DoWorkEventHandler(delegate(object o, DoWorkEventArgs workerEventArgs)
                    {
                        // 你的异步代码
                        Thread.Sleep(5000);
                    });
    
                    backgroundWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(delegate(object o, RunWorkerCompletedEventArgs e)
                    {
                        MessageBox.Show("加载成功");
                        this.fDisablePanel.Visible = false;
                    });
    
                    backgroundWorker.RunWorkerAsync();
                }
            }
        }
    }
    


    知识改变命运,奋斗成就人生!
    2009年7月24日 7:01
    版主

全部回复

  • 主程序界面不是不显示了,而是被VS盖住了,在后面
    2009年7月23日 7:35
  • 我从编译的程序运行,也是这样的。

    2009年7月23日 8:00
  • 我重现了你的问题!
    你使用f1.Show()就可以先关闭窗体然后显示消息框了!
    周雪峰
    2009年7月23日 8:29
    版主
  • 谢谢。
    不过使用show窗口不能满足要求,因为其他的窗口都可以获得焦点操作。所以需要使用showdialog,等用户响应后或完成某个任务后,再关闭。
    这个是我显示等待窗口中使用的。

    2009年7月23日 9:26
  • 我想了一下,原因应该是这样的:
    你使用ShowDialog来显示窗体,会将窗体显示为模式对话框,并将当前活动窗口设置为它的所有者。而子窗体需要处理的系统消息应该是他的所有者转发的,理解这一点很重要!
    你使用的这个记时器(System.Windows.Forms.Timer),处理任务的时候,主窗体是不能响应消息的:

    1,如果你使用ShowDialog方法的话,会将窗体显示为模式对话框,并将当前活动窗口(主窗体)设置为它的所有者。这时主窗体不能响应消息,也就不能把对f1的关闭的消息传递给f1,所以f1也不能关闭,只能执行后面的代码,这时显示了你定义的消息框,当你把消息框点掉,当记时器任务结束了,这是主窗体有可以响应消息了,所以这是才能把f1的关闭消息传给f1,f1这时才可以关闭

    2,如果你使用Show方法的话,主窗体并不是f1的所有者,虽然执行记时器任务的时候主窗体仍然不能响应消息,但是这时f1自己可以响应消息的,所以马上就可以关闭掉f1了!

    希望这个解释对你有帮助啊!
    周雪峰
    2009年7月23日 9:29
    版主
  • 喜欢你的分析,不过还是有些糊涂。模式对话框的close是所有者窗口转发的,这个可以理解。
    但是所有者窗口转发了close了,应该可以关闭模式对话框啊。脚本顺序执行,先关闭再显示消息。不过你说得很有启发,似乎可以从其他的途径实现这种操作,但是我还没有想出来。

    2009年7月23日 9:57
  • 是这样的,你执行这段代码的时候:
      private void timer1_Tick(object sender, EventArgs e)
            {
                timer1.Stop();
                f1.Close();  //这行会执行,但是需要f1收到关闭的消息才会关闭窗体
                MessageBox.Show("看看响应窗口是否关闭了?");
                f1.Dispose();
            }
    主窗体是不能转发消息的(这是你使用的这个记时器的特性),这时WM_CLOSE消息在消息队列中是待处理的!这段代码执行完毕,主窗体才可以转发消息给f1,然后f1会关闭!

    周雪峰
    2009年7月23日 11:06
    版主
  •  根据你的提示,我做了另一个测试:
    在响应窗口上加了一个timer,让这个窗口3秒钟以后自动关闭this.Close();

    在主窗口中,
    f2 = new dialogshow();
    f2.FormClosed += new FormClosedEventHandler(f2_FormClosed);

    在响应窗口关闭时,调用主窗口的一个事件
            void f2_FormClosed(object sender, FormClosedEventArgs e)
            {
                MessageBox.Show("看看响应窗口是否关闭了?");
            }

    发现效果是一样的。

    那么如果主窗口的提示信息放到响应窗口的close中,
            private void timer1_Tick(object sender, EventArgs e)
            {
                this.Close();
                MessageBox.Show("这是响应窗口的消息!");
            }

    结果还是一样!

    我实在是有些搞不懂了。如果使用f2.show(),确实可以先关闭窗口,然后显示信息。但是看到界面明显的有些卡。难道是因为响应窗口close时没有dispose?
    2009年7月24日 3:11
  • 对啊!
    你是在子窗体上加的Timer吧!当你执行这个的时候:
            private void timer1_Tick(object sender, EventArgs e)
            {
                this.Close();
                MessageBox.Show("这是响应窗口的消息!");
            }
    会使拥有这个记时器的窗体(子窗体)无法响应关闭消息,所以不能马上关闭子窗体,需要这个记时器任务结束后才能关闭!

    那个FormClosed时间并不是窗体消失后才响应的,而是窗体消失前!

    我没有太理解你现在的疑问是什么?
    周雪峰
    2009年7月24日 5:13
    版主
  • 实际上,我是要显示一个 诸如 “正在处理,请稍候...”这样的窗口,这个窗口是主线程showdialog,使用backGroundWorker启动另一个线程检索或保存,处理完成后,也就是backgroudWorker_Completed事件中,关闭响应窗口,然后提示一个信息,告诉用户保存成功。

    实际上这里面没有用到timer。不知道如何实现提示消息的时候,看不到 等待窗口了呢?

    这个倒是不影响操作,就是看起来不爽,如果实在解决不了就算了。
    2009年7月24日 6:20
  • using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Linq;
    using System.Text;
    using System.Windows.Forms;
    using System.Threading;
    
    namespace X.WinFormsApp
    {
        /// <summary>
        /// 标题:这只是一个简单测试,要用于实际还需要自己丰富代码
        /// </summary>
        public partial class X200905211105 : Form
        {
            private Panel fDisablePanel;
    
            public X200905211105()
            {
                InitializeComponent();
                this.InitDisableMask();
            }
    
            private void InitDisableMask()
            {
                this.fDisablePanel = new Panel();
                this.fDisablePanel.Visible = false;
                this.fDisablePanel.Size = this.ClientSize;
                this.fDisablePanel.BackColor = Color.Blue;
                Label loading = new Label();
                loading.Text = "Loading...";
                this.fDisablePanel.Controls.Add(loading);
                this.Controls.Add(this.fDisablePanel);
    
                Button btnStart = new Button();
                btnStart.Text = "开始异步";
                btnStart.Click += new EventHandler(btnStart_Click);
                this.Controls.Add(btnStart);
            }
    
            private void btnStart_Click(object sender, EventArgs e)
            {
                this.RunWorkerAsync();
            }
    
            public void RunWorkerAsync()
            {
                this.fDisablePanel.Visible = true;
                this.fDisablePanel.BringToFront();
    
                using (BackgroundWorker backgroundWorker = new BackgroundWorker())
                {
                    backgroundWorker.DoWork += new DoWorkEventHandler(delegate(object o, DoWorkEventArgs workerEventArgs)
                    {
                        // 你的异步代码
                        Thread.Sleep(5000);
                    });
    
                    backgroundWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(delegate(object o, RunWorkerCompletedEventArgs e)
                    {
                        MessageBox.Show("加载成功");
                        this.fDisablePanel.Visible = false;
                    });
    
                    backgroundWorker.RunWorkerAsync();
                }
            }
        }
    }
    


    知识改变命运,奋斗成就人生!
    2009年7月24日 7:01
    版主
  • 感谢X.X.Y的例子。
    这个例子是使用的panel,可以实现先隐藏这个panel,然后显示信息。
    而且在显示这个panel时,可以操作界面的控件,这个不能满足要求。
    2009年7月25日 0:36
  • this.fDisablePanel.BringToFront(); // 将 Panel 置顶,会盖住其它控件,其它控件是不能操作的

    知识改变命运,奋斗成就人生!
    2009年7月25日 3:33
    版主
  • 看楼主的需要,为何不考虑使用窗体的FormClosing事件或者FormClosed事件呢?



    理解的越多,需要记忆的就越少
    2009年7月27日 2:39
    版主