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停靠,显示隐藏过程添加特效效果的详细内容...