好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

WinForm实现类似QQ停靠,显示隐藏过程添加特效效果

WinForm实现类似QQ停靠,显示隐藏过程添加特效效果

这可能是个老题长谈的问题了,只是在项目中会用到这个效果,所以今天做个记录。大家见了别喷我。在项目中的需求是这样的。

打开程序,在屏幕的右下角会显示一个窗体,一般情况下该窗体会隐藏停靠在右边,只露出很小部分,当鼠标移动到这个很小部分时,窗体全部显示,显示过程是从右边滑动到左边,当鼠标离开窗体时,窗体需要隐藏在右边,只露出很小部分,隐藏过程是从左边滑动到右边。

实现此类效果我碰到的连个难点是:1、如何判断鼠标离开了窗体?2、窗体显示隐藏过程中效果如何表现平滑(就是给人一种流畅感觉)?

1、判断鼠标离开窗体我开始想的是在WndProc方法中来获取鼠标坐标然后根据窗体的Location来判断,可能是小弟愚笨,该方法没有处理好,运行后界面卡住了。然后我只有用个计时器,每隔几秒获取一下鼠标坐标在根据窗体的Location来判断。获取坐标有个API方法GetCursorPos,有资料表明此方法是最优效率的,那我们就用它好了。

2、显示隐藏效果开始想的也是改变窗体坐标来实现,但是这个方法做出来的效果比较差,画面感觉不流畅,后来查到可以用API方法AnimateWindow来实现这个效果,因此我们来认识一下AnimateWindow()方法;

此方法需要传递三个参数:

第一个参数:传入需要显示特效的窗体的句柄。

第二个参数:完成特效所花时间,单位:毫秒,也就是说你可以指定多少时间内完成指定的特效

第三个参数:指定特效类型,此参数可以指定多个,多个之间用|隔开。这里列举了一般的9个特效。有这9个基本够用了。

关于这个方法的详细资料我就不一一列举了,大家在网上搜搜,很多资料的。下面进入正题。

1、建一个winform项目,命名:DockFormsApplication,名字大家可以自定义的。

2、建完后项目中会有个默认创建好的窗体Form1,修改Form1的text属性为:“仿QQ停靠,加特效”

3、添加API方法AnimateWindow()和该方法需要的一些特效参数, 特效参数命名是我自己随便命名的,大家就不要深究了,至于为什么要这么命名,我自己也不知道,反正能用就行 。

注意:AnimateWindow()方法需要引用using System.Runtime.InteropServices;

         ///   <summary> 
         ///   //从左到右
          ///   </summary> 
         public   const  Int32 AW_HOR_LEFT_RIGHT =  0x00000001  ;
          ///   <summary> 
         ///   从右到左
          ///   </summary> 
         private   const  Int32 AW_HOR_RIGHT_LEFT =  0x00000002  ;
          ///   <summary> 
         ///   从上到下
          ///   </summary> 
         private   const  Int32 AW_VER_UP_DOWN =  0x00000004  ;
          ///   <summary> 
         ///   从下到上
          ///   </summary> 
         private   const  Int32 AW_VER_DOWN_UP =  0x00000008  ;
          ///   <summary> 
         ///   从中间到四周
          ///   </summary> 
         private   const  Int32 AW_CENTER =  0x00000010  ;
          ///   <summary> 
         ///   隐藏窗口
          ///   </summary> 
         private   const  Int32 AW_HIDE =  0x00010000  ;
          ///   <summary> 
         ///   显示窗口
          ///   </summary> 
         private   const  Int32 AW_ACTIVATE =  0x00020000  ;
          ///   <summary> 
         ///   使用滑动类型。缺省则为滚动动画类型。当使用AW_CENTER标志时,这个标志就被忽略
          ///   </summary> 
         private   const  Int32 AW_SLIDE =  0x00040000  ;
          ///   <summary> 
         ///   改变透明度
          ///   </summary> 
         private   const  Int32 AW_BLEND =  0x00080000  ;

          ///   <summary> 
         ///   特效花费时间 单位:毫秒
          ///   </summary> 
         private   int  _speed =  500  ;

        [DllImport(  "  user32.dll  "  )]
          public   static   extern   void  AnimateWindow(IntPtr hwnd,  int  stime,  int  style); //  显示效果 

3、添加API方法GetCursorPos用于获取鼠标坐标。此方法需要传入一个坐标对象。该对象是一个二维结构。存储坐标的X值和Y值。

         ///   <summary> 
         ///   鼠标坐标
          ///   </summary> 
         private   Point _cursorPoint;

          //  API获取鼠标坐标 
        [DllImport( "  user32.dll  "  )]
          public   static   extern   bool  GetCursorPos( out  Point pt);

4、设置窗体显示在右下角,并且重写WndProc方法禁止鼠标拖动和双击标题栏最大化

         private   void  Form1_Load( object   sender, EventArgs e)
        {
              //  设置窗体显示位置 右下角 
             int  workY = Screen.PrimaryScreen.WorkingArea.Height -  Height;
              int  X = Screen.PrimaryScreen.Bounds.Width -  Width;
              this .Location =  new   Point(X, workY);
        }

          //  重写WndProc方法,禁止拖动和双击标题栏最大化 
         protected   override   void  WndProc( ref   Message m)
        {
              if  (m.Msg ==  0x231  )
            {
                  this  .SuspendLayout();
            }
              else   if  (m.Msg ==  0x232  )
            {
                  this  .ResumeLayout();
            }
              else   if  (m.Msg ==  0xA1  && m.WParam.ToInt32() ==  2 ) //  禁止拖动 
             {
                  return  ;
            }
              base .WndProc( ref   m);
        } 

后来发现,更改窗体属性:FormBorderStyle值为:FixedSingle也可以达到禁止拖动的效果

5、因为要每隔几秒获取鼠标坐标判断鼠标是否在窗体范围内,因此需要一个计时器。考虑到新能神马的,我比较喜欢使用System.Threading.Timer,下面就是计时器嗦必须的几个变量

    注意:这里我需要说明一下,由于AnimateWindow()方法控制窗体特效只能窗体显示和隐藏两种状态,每个特效完成后窗体要么隐藏,要么显示,如何使特效过后窗体一直显示,我想了个折中办法,只要你隐藏了,我就再把你显示出来,一次在计时器中需要对窗体进行炒作,如此则需要跨线程访问窗体,因此就需要使用委托了,然后Invoke就可以了。

         //  线程暂停时间 单位:毫秒 
         private   int  _timespan =  1000  ;  private   System.Threading.Timer _timer;
          private   delegate   void   LoadListDelegate();
          private  LoadListDelegate _loaddelegate;

6、程序逻辑需要的几个变量

         ///   <summary> 
         ///   窗体是否显示,true——显示、false——隐藏
          ///   </summary> 
         private   bool  _isActive =  true  ;

          ///   <summary> 
         ///   停靠在边缘时,显示窗体的宽度
          ///   </summary> 
         private   const   int  _smallX =  5 ;

7、添加两个方法,显示窗体和隐藏窗体的两个方法

         ///   <summary> 
         ///   隐藏窗体
          ///   </summary> 
         private   void   SetHide()
        {
              if   (_isActive)
            {
                AnimateWindow(  this .Handle, _speed, AW_HOR_LEFT_RIGHT | AW_SLIDE |  AW_HIDE);

                  int  X = Screen.PrimaryScreen.Bounds.Width -  _smallX;
                  int  Y =  this  .Location.Y;
                  this .Location =  new   Point(X, Y);

                AnimateWindow(  this .Handle,  10 , AW_BLEND |  AW_ACTIVATE);
                _isActive  =  false  ;
            }
        }

          ///   <summary> 
         ///   显示窗体
          ///   </summary> 
         private   void   SetActivate()
        {
              if  (! _isActive)
            {
                AnimateWindow(  this .Handle,  10 , AW_BLEND |  AW_HIDE);

                  int  X = Screen.PrimaryScreen.Bounds.Width -  Width;
                  int  Y =  this  .Location.Y;
                  this .Location =  new   Point(X, Y);

                AnimateWindow(  this .Handle, _speed, AW_HOR_RIGHT_LEFT | AW_SLIDE |  AW_ACTIVATE);
                _isActive  =  true  ;
            }
        } 

8、添加方法,判断鼠标是否在窗体范围内,如果在范围内,则显示窗体,如果不在范围内,则停靠在右边并隐藏窗体

         private   void   LoadControl()
        {
              #region  控制窗体显示和隐藏
             //  获取当前鼠标坐标 
            GetCursorPos( out   _cursorPoint);
              //  根据 窗体当前状态,判断窗体接下来是显示还是隐藏。 
             if   (_isActive)
            {
                  //  当前窗体为显示,则接下来是隐藏
                  //  如果鼠标坐标不在窗体范围内,则设置窗体隐藏,否则不处理 
                 if  (_cursorPoint.X <  this .Location.X || _cursorPoint.Y <  this  .Location.Y)
                {
                    SetHide();
                }
            }
              else  
            {
                  //  当前窗体为隐藏,则接下来是显示
                  //  如果鼠标坐标在窗体范围内,则设置窗体显示,否则不处理 
                 if  (_cursorPoint.X >=  this .Location.X && _cursorPoint.Y >=  this  .Location.Y)
                {
                    SetActivate();
                }
            }
              #endregion  
        } 

9、添加计时器,每隔1秒判断当前鼠标位置,因为用到委托,因此需要在构造方法中添加一行代码_loaddelegate = LoadControl;用于指定委托的方法:

         private   void  Form1_Load( object   sender, EventArgs e)
        {
              //  设置窗体显示位置 右下角 
             int  workY = Screen.PrimaryScreen.WorkingArea.Height -  Height;
              int  X = Screen.PrimaryScreen.Bounds.Width -  Width;
              this .Location =  new   Point(X, workY);

              //  窗体打开的时候就开始计时器 
             BeginTimer();
        }

          private   void   BeginTimer()
        {
            TimerCallback tcBack  =  new   TimerCallback(InvokTimer);
            _timer  =  new  System.Threading.Timer(tcBack,  null ,  5000  , _timespan);
        }

          private   void  InvokTimer( object   state)
        {
              if  ( this  .InvokeRequired)
            {
                  this  .Invoke(_loaddelegate);
            }
        } 

10、为了窗体一打开和关闭时有特效显示,需要重写OnLoad方法和实现Form1_Closing方法

         protected   override   void   OnLoad(EventArgs e)
        {
              base  .OnLoad(e);
              //  从右到左滑动 
            AnimateWindow( this .Handle, _speed, AW_HOR_RIGHT_LEFT | AW_SLIDE |  AW_ACTIVATE);
        }

          private   void  Form1_FormClosing( object   sender, FormClosingEventArgs e)
        {
              //  淡出效果 
            AnimateWindow( this .Handle,  1000 , AW_BLEND |  AW_HIDE);
        } 

到此,所有代码编写完成,如果想要有更好的体验,可以设置一下窗体的以下属性值:

             this .MaximizeBox =  false ; //  取消最大化按钮 
             this .MinimizeBox =  false ; //  取消最小化按钮 
             this .ShowInTaskbar =  false ; //  任务栏不显示窗体图标 
             this .TopMost =  false ; //  设置窗体总是显示在最前面 


完整代码如下:

全部代码

 using   System;
  using   System.Collections.Generic;
  using   System.ComponentModel;
  using   System.Data;
  using   System.Drawing;
  using   System.Text;
  using   System.Windows.Forms;
  using   System.Runtime.InteropServices;
  using   System.Threading;

  namespace   DockFormsApplication
{
      public   partial   class   Form1 : Form
    {
          #region  属性 API特效窗体显示和隐藏

         ///   <summary> 
         ///   //从左到右
          ///   </summary> 
         public   const  Int32 AW_HOR_LEFT_RIGHT =  0x00000001  ;
          ///   <summary> 
         ///   从右到左
          ///   </summary> 
         private   const  Int32 AW_HOR_RIGHT_LEFT =  0x00000002  ;
          ///   <summary> 
         ///   从上到下
          ///   </summary> 
         private   const  Int32 AW_VER_UP_DOWN =  0x00000004  ;
          ///   <summary> 
         ///   从下到上
          ///   </summary> 
         private   const  Int32 AW_VER_DOWN_UP =  0x00000008  ;
          ///   <summary> 
         ///   从中间到四周
          ///   </summary> 
         private   const  Int32 AW_CENTER =  0x00000010  ;
          ///   <summary> 
         ///   隐藏窗口
          ///   </summary> 
         private   const  Int32 AW_HIDE =  0x00010000  ;
          ///   <summary> 
         ///   显示窗口
          ///   </summary> 
         private   const  Int32 AW_ACTIVATE =  0x00020000  ;
          ///   <summary> 
         ///   使用滑动类型。缺省则为滚动动画类型。当使用AW_CENTER标志时,这个标志就被忽略
          ///   </summary> 
         private   const  Int32 AW_SLIDE =  0x00040000  ;
          ///   <summary> 
         ///   改变透明度
          ///   </summary> 
         private   const  Int32 AW_BLEND =  0x00080000  ;

          ///   <summary> 
         ///   特效花费时间 单位:毫秒
          ///   </summary> 
         private   int  _speed =  500  ;

        [DllImport(  "  user32.dll  "  )]
          public   static   extern   void  AnimateWindow(IntPtr hwnd,  int  stime,  int  style); //  显示效果 

         ///   <summary> 
         ///   鼠标坐标
          ///   </summary> 
         private   Point _cursorPoint;

          //  API获取鼠标坐标 
        [DllImport( "  user32.dll  "  )]
          public   static   extern   bool  GetCursorPos( out   Point pt);

          //  线程暂停时间 单位:毫秒 
         private   int  _timespan =  1000  ;
          private   System.Threading.Timer _timer;
          private   delegate   void   LoadListDelegate();
          private   LoadListDelegate _loaddelegate;

          ///   <summary> 
         ///   窗体是否显示,true——显示、false——隐藏
          ///   </summary> 
         private   bool  _isActive =  true  ;

          ///   <summary> 
         ///   停靠在边缘时,显示窗体的宽度
          ///   </summary> 
         private   const   int  _smallX =  5  ;

          #endregion 

         public   Form1()
        {
            InitializeComponent();
              this .MaximizeBox =  false ; //  取消最大化按钮 
             this .MinimizeBox =  false ; //  取消最小化按钮 
             this .ShowInTaskbar =  false ; //  任务栏不显示窗体图标 
             this .TopMost =  false ; //  设置窗体总是显示在最前面 
 
            _loaddelegate  =  LoadControl;
        }

          private   void  Form1_Load( object   sender, EventArgs e)
        {
              //  设置窗体显示位置 右下角 
             int  workY = Screen.PrimaryScreen.WorkingArea.Height -  Height;
              int  X = Screen.PrimaryScreen.Bounds.Width -  Width;
              this .Location =  new   Point(X, workY);

              //  窗体打开的时候就开始计时器 
             BeginTimer();
        }

          protected   override   void   OnLoad(EventArgs e)
        {
              base  .OnLoad(e);
              //  从右到左滑动 
            AnimateWindow( this .Handle, _speed, AW_HOR_RIGHT_LEFT | AW_SLIDE |  AW_ACTIVATE);
        }

          private   void  Form1_FormClosing( object   sender, FormClosingEventArgs e)
        {
            _timer.Dispose();
              //  淡出效果 
            AnimateWindow( this .Handle,  1000 , AW_BLEND |  AW_HIDE);
        }

          //  重写WndProc方法,禁止拖动和双击标题栏最大化 
         protected   override   void  WndProc( ref   Message m)
        {
              if  (m.Msg ==  0x231  )
            {
                  this  .SuspendLayout();
            }
              else   if  (m.Msg ==  0x232  )
            {
                  this  .ResumeLayout();
            }
              else   if  (m.Msg ==  0xA1  && m.WParam.ToInt32() ==  2 ) //  禁止拖动 
             {
                  return  ;
            }
              base .WndProc( ref   m);
        }

          ///   <summary> 
         ///   隐藏窗体
          ///   </summary> 
         private   void   SetHide()
        {
              if   (_isActive)
            {
                AnimateWindow(  this .Handle, _speed, AW_HOR_LEFT_RIGHT | AW_SLIDE |  AW_HIDE);

                  int  X = Screen.PrimaryScreen.Bounds.Width -  _smallX;
                  int  Y =  this  .Location.Y;
                  this .Location =  new   Point(X, Y);

                AnimateWindow(  this .Handle,  10 , AW_BLEND |  AW_ACTIVATE);
                _isActive  =  false  ;
            }
        }

          ///   <summary> 
         ///   显示窗体
          ///   </summary> 
         private   void   SetActivate()
        {
              if  (! _isActive)
            {
                AnimateWindow(  this .Handle,  10 , AW_BLEND |  AW_HIDE);

                  int  X = Screen.PrimaryScreen.Bounds.Width -  Width;
                  int  Y =  this  .Location.Y;
                  this .Location =  new   Point(X, Y);

                AnimateWindow(  this .Handle, _speed, AW_HOR_RIGHT_LEFT | AW_SLIDE |  AW_ACTIVATE);
                _isActive  =  true  ;
            }
        }

          private   void   LoadControl()
        {
              #region  控制窗体显示和隐藏
             //  获取当前鼠标坐标 
            GetCursorPos( out   _cursorPoint);
              //  根据 窗体当前状态,判断窗体接下来是显示还是隐藏。 
             if   (_isActive)
            {
                  //  当前窗体为显示,则接下来是隐藏
                  //  如果鼠标坐标不在窗体范围内,则设置窗体隐藏,否则不处理 
                 if  (_cursorPoint.X <  this .Location.X || _cursorPoint.Y <  this  .Location.Y)
                {
                    SetHide();
                }
            }
              else  
            {
                  //  当前窗体为隐藏,则接下来是显示
                  //  如果鼠标坐标在窗体范围内,则设置窗体显示,否则不处理 
                 if  (_cursorPoint.X >=  this .Location.X && _cursorPoint.Y >=  this  .Location.Y)
                {
                    SetActivate();
                }
            }
              #endregion  
        }

          private   void   BeginTimer()
        {
            TimerCallback tcBack  =  new   TimerCallback(InvokTimer);
            _timer  =  new  System.Threading.Timer(tcBack,  null ,  5000  , _timespan);
        }

          private   void  InvokTimer( object   state)
        {
              if  ( this  .InvokeRequired)
            {
                  this  .Invoke(_loaddelegate);
            }
        }
    }
} 

 

 

 

标签:  winform ,  仿QQ停靠 ,  特效 ,  API方法 ,  AnimateWindow ,  GetCursorPos

作者: Leo_wl

    

出处: http://www.cnblogs.com/Leo_wl/

    

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

版权信息

查看更多关于WinForm实现类似QQ停靠,显示隐藏过程添加特效效果的详细内容...

  阅读:46次