zoukankan      html  css  js  c++  java
  • 一种用户体验显示对话框时灰化你的主窗体

    袁永福 ( 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函数,无伤大雅。

       以下是程序的运行效果。



       以下是控件 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             ifthis.IsDisposed )
     73                 return ;
     74             ifthis.IsHandleCreated == false )
     75                 return ;
     76 
     77             // 主窗体显示对话框时窗体不可用,但此时它的Enable属性无法判断其是否真的
     78             // 不可用,因此必须调用Win32API来判断其是否真的不可用.
     79             if( IsWindowEnabled( frm.Handle ) == false )
     80             {
     81                 ifthis.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                 ifthis.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 }
  • 相关阅读:
    Redis入门(二)——基本操作
    Redis入门(一)——安装
    Switch按钮
    Vue入门(三)——模拟网络请求加载本地数据
    Vue入门(二)——Demo
    Vue入门(一)——环境搭建
    Oracle 存储过程
    函数节流
    jQuery实现瀑布流
    二分搜索法整理
  • 原文地址:https://www.cnblogs.com/xdesigner/p/850235.html
Copyright © 2011-2022 走看看