zoukankan      html  css  js  c++  java
  • WinForm下WaitForm的设计及其实现

    在WinForm编程中,前台有时需要执行一段耗时程序,或者需要加载一些数据(特别是在程序启动时),这时前台界面会死掉,一般来说解决的办法是采用后台加载技术,例如使用C#自带的BackgroudWorker组件或者使用异步加载技术(利用C#新的Task类很容易实现),但是有时仅仅是这样还不够,进一步的我们需要知道后台进行的实时操作,这就是这篇文章所要说明的:如何在WinForm下实现一个等待窗口WaitForm,能实时显示后台操作的情况!

    首先,思路很简单,就是新建一个Form窗口,显示出来,然后在后台设置该窗口内显示的内容即可,但是这样做需要以下几个问题:

    1.应该在一个新的辅助线程中显示窗口,否则等待窗口运行在主线程,这样如果主线程阻塞时,等待窗口也会阻塞,就不能实时刷新信息;

    2.在辅助线程中显示窗口,为了简单,最好用ShowDialog,因为如果用Show显示,显示完时候线程会自动退出,以后再调用该窗口时,就会出现如下错误信息:

    这是由于显示窗口的线程已经退出了。因此要试用ShowDialog将辅助线程阻塞(当然你也可以试用Show,然后在自己写代码将线程阻塞)

    3.实现上述两点之后,就意味着需要跨线程对等待窗口进行调用,在WinForm下跨线程调用时不被允许的,当然直接运行代码是可以执行的,但是在调试模式下会产生不允许跨线程的错误:

    因为.net检测到调用的线程不是创建线程,.net不允许跨线程调用控件的原因,本人猜测可能是WinForm设计的问题,因为WinForm并没有设计成在多线程模式下运行(据说WPF是基于多线程模式设计,估计可以跨线程调用)

    解决这一问题的方法是检查Form的属性InvokeRequired,当其为True时表示此时是跨线程调用,此时使用Invoke函数调用即可。

    4.在调用Invoke函数时,如果窗口没有创建,程序会抛出窗体句柄没有创建的异常,此时只有添加一个循环,检查IsHandleCreated属性即可。

    5.当然在关闭等待窗口时同样存在跨线程调用问题,解决方法同上。

    基于上述分析,设计一个静态类将等待窗口的控制封装起来,该类只用Show和Close两个简单函数

     1 public static class WaitWin
     2     {
     3         static FormWait form = null;
     4         public static void Show(string waitMsg)
     5         {
     6             if (form == null)
     7             {
     8                 form = new FormWait();
     9                 ThreadPool.QueueUserWorkItem(state =>
    10                 {                    
    11                     form.ShowDialog();
    12                 });
    13             }
    14             form.Msg = waitMsg;
    15         }
    16         public static void Close()
    17         {
    18             if (form != null)
    19                 form.Close();
    20             form = null;
    21         }
    22     }

    可以看出,在Show函数中,为了避免主线程阻塞,使用线程池调用ShowDialog函数,用以显示等待窗口。

    FormWait窗口设计很简单,只是在窗体上放一个lable用于显示等待消息,当然为了更像等待窗口,需要设置一下窗口的属性:

    1 FormBorderStyle = FormBorderStyle.FixedDialog;//固定窗体大小
    2 ShowInTaskbar = false;//不在任务栏中显示
    3 ControlBox = false;//隐藏右上角的关闭等按钮
    4 UseWaitCursor = true;//显示等待光标
    5 StartPosition = FormStartPosition.CenterScreen;//在屏幕中间显示
     1 public partial class FormWait : Form
     2     {
     3         Action<string> setMsg;
     4         Action close;
     5         public FormWait()
     6         {
     7             InitializeComponent();
     8             setMsg = invoke;
     9             close = base.Close;
    10         }
    11         public string Msg
    12         {
    13             get
    14             {
    15                 return this.label1.Text;
    16             }
    17             set
    18             {
    19                 while (!this.IsHandleCreated) ;
    20                 this.Invoke(setMsg, value);
    21             }
    22         }
    23         public new void Close()
    24         {
    25             while (!this.IsHandleCreated) ;
    26             this.Invoke(close);
    27         }
    28         void invoke(string msg)
    29         {
    30             this.label1.Text = msg;
    31         }
    32     }

    这样就可以实现实时显示后台进度情况,而且调用也十分方便

    1 private void Form1_Load(object sender, EventArgs e)
    2         {
    3             for (int i = 1; i <= 5;i++ )
    4             {
    5                 WaitWin.Show("等待中,第 " + i + "");
    6                 Thread.Sleep(1000);
    7             }
    8             WaitWin.Close();                      
    9         }

    示例代码:https://files.cnblogs.com/ILoveSleep/WaitWin.rar

  • 相关阅读:
    游标和视图
    sql server 变量与集合运算
    sql server 流程控制
    sql server 基础查询和子查询
    数据库范式快速理解
    创建数据库与数据表
    SQL server 使用
    Android studio 下载依赖jar包慢
    java根据list数据通过接口姓名并填充
    【翻译】Promises/A+规范
  • 原文地址:https://www.cnblogs.com/ILoveSleep/p/3086195.html
Copyright © 2011-2022 走看看