一种用户体验-显示对话框时灰化你的主窗体

本文介绍了一种在WinForm程序中,当弹出对话框时,通过使用自定义控件DisableMaskControl灰化主窗体的实现方法。此控件利用Win32API函数和定时器监测主窗体状态,自动调整透明度,提升用户体验。

袁永福 ( http://www.xdesigner.cn ) 2007-8-10

程序全部源代码下载(工程文件使用VS.NET2003格式):/Files/xdesigner/DisableMask.rar

    在一些Web程序中,有一种页面效果,当弹出一个模拟的对话框时,主页面就整体灰化了,其他的元素不能动弹,只有这个对话框能用,用户关闭了对话框,整个页面才恢复原来的操作.这种用户体验是不错的,提示了用户必须处理的对话框才能继续处理页面.如何实现的我猜想是动态生成一个大的DIV层,把它置于顶层并设置半透明的灰色.

    在WinForm程序中也需要这种用户体验,我们有时观察到主窗体显示了一个对话框,此时用户还试图用鼠标点击主窗体搞些操作,但这种操作注定是要失败,影响到软件可用性.于是我就想到把Web程序中的这种用户体验移植到WinForm程序中来帮助用户意识到主窗体的当前状态.

    实现这个用户体验有两个问题,一是如何知道主窗体弹出对话框,二是如何灰化主窗体.

    首先是解决如何知道主窗体何时弹出对话框,研究了一下,没发现窗体对象System.Windows.Forms.Form类型提供有所帮助的事件方法属性.我们可以在程序代码中,每次弹出对话框前添加灰化主窗体的代码,这样加大了程序开发量,而且代码移植性不好。后来想了又想,试了又试,发现弹出对话框时,主窗体的状态是不可用的,但此时窗体的Enabled属性不能反应这种状态,使用Win32API函数却能正确获得其状态.因此最后决定使用计时器System.Windows.Forms.Timer来不断的调用Win32API函数来测试主窗体是否可用,若可用则不必灰化主窗体,若不可用则灰化主窗体。

    第二步就是灰化主窗体了,根据WEB程序中的实现过程,我们很自然的想到用一个半透明的控件来覆盖整个主窗体,于是我们又如何创造这个半透明控件。纵观System.Windows.Forms名称空间,号称提供半透明效果效果的只有Label类型了,经过测试,发现Label类型的半透明属性是假的,是模拟出来的,它是在控件背景中模拟绘制窗体的背景来搞出半透明的效果,若Label控件背后有其他控件还是要被Label无情的覆盖掉。因此我们需要一个真正的半透明控件,于是我又想了又想,试了又试,找了又找,终于把这个真正的半透明控件搞出来了。

    基础问题解决了,然后就是代码的组成和组件化了,我定义了一个DisableMaskControl控件,实现了真正的半透明处理,里面有个定时器,不断的使用Win32API函数测试这个控件所在的窗体是否有效,若有效则隐藏控件,若无效则显示控件,把控件覆盖整个窗体并置于顶层。这样我就用一百来行的C#代码实现了这种弹出对话框灰化主窗体的用户体验,而且这个代码使用非常简单,只要在需要这种效果的主窗体上添加一行代码 this.Controls.Add( new DisableMaskControl()) 即可。

   此处或许有人提出这个定时器的效率问题,我觉得没多大问题,首先控件少,一个窗体才用一个,相对于高速的CPU,用户手动操作来显示和关闭对话框是极其缓慢的操作。而且定时器中进行的判断不多,只调用了一个API函数,无伤大雅。

   以下是程序的运行效果。

disablemask.PNG

   以下是控件 DisableMaskControl的全部代码。

  1  using  System;
  2  using  System.Runtime.InteropServices ;
  3  namespace  DisableMask
  4  {
  5       ///   <summary>
  6       ///  窗体无效时用于掩盖整个状态的半透明控件
  7       ///   </summary>
  8       ///   <remarks> 编制 袁永福(  http://www.xdesigner.cn  ) </remarks>
  9       public   class  DisableMaskControl : System.Windows.Forms.Control
 10      {
 11           ///   <summary>
 12           ///  初始化对象
 13           ///   </summary>
 14           public  DisableMaskControl()
 15          {
 16               this .SetStyle( System.Windows.Forms.ControlStyles.SupportsTransparentBackColor ,  true  );
 17              myTimer  =   new  System.Windows.Forms.Timer();
 18              myTimer.Interval  =   100  ;
 19              myTimer.Tick  += new  EventHandler(myTimer_Tick);
 20               this .BackColor  =  System.Drawing.Color.FromArgb(  80  ,  0  ,  0  ,  0  );
 21              myTimer.Start();
 22          }
 23           ///   <summary>
 24           ///  内部用于定时处理的计时器
 25           ///   </summary>
 26           private  System.Windows.Forms.Timer myTimer  =   null ;
 27 
 28           ///   <summary>
 29           ///  已重载:返回控件创建参数
 30           ///   </summary>
 31           protected   override  System.Windows.Forms.CreateParams CreateParams
 32          {
 33               get
 34              {
 35                  System.Windows.Forms.CreateParams ps  =   base .CreateParams;
 36                  ps.ExStyle  =  ps.ExStyle  |   0x20  ;
 37                   return  ps ;
 38              }
 39          }
 40 
 41           ///   <summary>
 42           ///  绘制控件的背景,啥也不干.
 43           ///   </summary>
 44           ///   <param name="pevent"> 事件参数 </param>
 45           protected   override   void  OnPaintBackground(System.Windows.Forms.PaintEventArgs pevent)
 46          {
 47          }
 48           ///   <summary>
 49           ///  绘制控件
 50           ///   </summary>
 51           ///   <param name="e"> 事件参数 </param>
 52           protected   override   void  OnPaint(System.Windows.Forms.PaintEventArgs e)
 53          {
 54               using ( System.Drawing.SolidBrush b  =   new  System.Drawing.SolidBrush(  this .BackColor ))
 55              {
 56                  e.Graphics.FillRectangle( b , e.ClipRectangle );
 57              }
 58          }
 59 
 60           ///   <summary>
 61           ///  定时器处理
 62           ///   </summary>
 63           ///   <param name="sender"> 事件参数 </param>
 64           ///   <param name="e"> 事件参数 </param>
 65           private   void  myTimer_Tick( object  sender, EventArgs e)
 66          {
 67              System.Windows.Forms.Form frm  =   this .FindForm();
 68               if ( frm  ==   null  )
 69                   return  ;
 70               if ( frm.IsDisposed )
 71                   return  ;
 72               if this .IsDisposed )
 73                   return  ;
 74               if this .IsHandleCreated  ==   false  )
 75                   return  ;
 76 
 77               //  主窗体显示对话框时窗体不可用,但此时它的Enable属性无法判断其是否真的
 78               //  不可用,因此必须调用Win32API来判断其是否真的不可用.
 79               if ( IsWindowEnabled( frm.Handle )  ==   false  )
 80              {
 81                   if this .Visible  ==   false  )
 82                  {
 83                       this .Dock  =  System.Windows.Forms.DockStyle.None ;
 84                       this .Bounds  =   new  System.Drawing.Rectangle( 
 85                           0  , 
 86                           0  , 
 87                          frm.ClientSize.Width ,
 88                          frm.ClientSize.Height );
 89                       this .BringToFront();    
 90                       this .Visible  =   true ;
 91                       this .Refresh();
 92                      frm.Refresh();
 93                  }
 94              }
 95               else
 96              {
 97                   if this .Visible )
 98                       this .Visible  =   false ;
 99              }
100          }
101 
102           ///   <summary>
103           ///  用于判断窗体是否有效的Win32API函数
104           ///   </summary>
105           ///   <param name="hWnd"> 窗体句柄 </param>
106           ///   <returns> 窗体是否有效 </returns>
107          [DllImport( " user32.dll " , CharSet = CharSet.Auto, ExactSpelling = true )]
108           private   static   extern   bool  IsWindowEnabled(IntPtr hWnd);
109 
110      } // public class DisableMaskControl : System.Windows.Forms.Control
111  }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值