原文: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 = ; [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() == )//禁止拖动
{
return;
}
base.WndProc(ref m);
}

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

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

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

        //线程暂停时间 单位:毫秒
private int _timespan = ;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 = ;

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, , AW_BLEND | AW_ACTIVATE);
_isActive = false;
}
} /// <summary>
/// 显示窗体
/// </summary>
private void SetActivate()
{
if (!_isActive)
{
AnimateWindow(this.Handle, , 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, , _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, , 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 = ; [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 = ;
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 = ; #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, , 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() == )//禁止拖动
{
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, , AW_BLEND | AW_ACTIVATE);
_isActive = false;
}
} /// <summary>
/// 显示窗体
/// </summary>
private void SetActivate()
{
if (!_isActive)
{
AnimateWindow(this.Handle, , 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, , _timespan);
} private void InvokTimer(object state)
{
if (this.InvokeRequired)
{
this.Invoke(_loaddelegate);
}
}
}
}

WinForm实现类似QQ停靠,显示隐藏过程添加特效效果的更多相关文章

  1. winform-实现类似QQ停靠桌面上边缘隐藏的效果

    //实现类似QQ停靠桌面上边缘隐藏的效果! private void timer1_Tick(object sender, EventArgs e) { System.Drawing.Point pp ...

  2. C#如何实现类似QQ那样靠边隐藏的功能

    http://www.cnblogs.com/yechensi/archive/2009/08/02/1537145.html C#如何实现类似QQ那样靠边隐藏的功能 你想过为自己的程序添加靠边隐藏的 ...

  3. .net winForm 实现类似qq 弹出新闻

    .net winForm 实现类似qq 弹出新闻   一.背景: echong 之前一直用 公司大牛c语言写的一个弹出托管,前几天写东西的时候发现com调用不是那么好使.而手头上写的这个东西又是.ne ...

  4. 利用div显示隐藏实现的分页效果

    实现步骤: 1.创建对应切换div <div class="bottom_daohang"> <div class="bottom_daohang_zo ...

  5. 给Activity切换过程添加动画效果

    首先,在资源文件中定义一些动画效果 例如: <scale android:duration="@android:integer/config_mediumAnimTime" ...

  6. jQuery css3鼠标悬停图片显示遮罩层动画特效

    jQuery css3鼠标悬停图片显示遮罩层动画特效 效果体验:http://hovertree.com/texiao/jquery/39/ 效果图: 源码下载:http://hovertree.co ...

  7. C#:winform窗体 实现类似QQ的窗体在桌面边缘停靠和隐藏

    设计思路:1.使用定时器(Timer)来监控鼠标位置和窗体位置,并实现窗体的停靠和隐藏2.当鼠标拖动窗体时,窗体才有可能根据自身位置决定是否停靠3.如果窗体四周没有接触到屏幕边缘则不会停靠4.如果窗体 ...

  8. 用C#代码实现类似QQ窗体的“上、左、右”停靠功能

    大家都知道QQ有一个自动停靠功能,即“上.左.右”,当你把窗体拖到屏幕边缘,然后移开鼠标它会自动缩放,然后只显示一小小点出来,我们仔细观察会发现其实它只露3像素左右的边缘,当你鼠标移上去它又会伸出来, ...

  9. EditTextPreference点击后输入框显示隐藏内容,类似密码输入(转)

    http://bbs.anzhuo.cn/thread-928131-1-1.html EditTextPreference点击后输入框显示隐藏内容,类似密码输入... [复制链接]     aski ...

随机推荐

  1. Spring它不支持依赖注入static静态变量

    在springframework在,我们不能@Autowired静态变量,制作spring bean,例如,没有那么: @Autowired private static YourClass your ...

  2. 应用层协议系列(两)——HTTPserver之http协议分析

    上一篇文章<抄nginx Httpserver设计与实现(一)--多进程和多通道IO现>中实现了一个仿照nginx的支持高并发的server.但仅仅是实现了port监听和数据接收.并没有实 ...

  3. PHPthinking官方论坛

    PHPthinking官方论坛正式上线一个月!眼下.我们已经有数百个固定用户的.论坛发展迅速,所有份额一些技术贴,我们希望,其他许多用户增加来,创建学习.交流更方便.丰富的内容PHP座谈会! PHPt ...

  4. 条形码/二维码之开源利器ZXing图文介绍(转)

    继前面介绍的一个日本开源软件(该软件只能实现QRCode)原文: Java实现二维码QRCode的编码和解码(http://sjsky.iteye.com/blog/1136934 ),今发现又一优秀 ...

  5. iOS经常使用类别

    我们发现,慢慢积累了很多自己写的各种类别的. .今天,无私.张贴 1.NSDateFomatter @interface NSDateFormatter (MyCategory) + (id)date ...

  6. android 视频通话开启呼叫等待后,来第三方的视频通话,接通后通话时间一直显示为0,过几秒之后视频通话自己主动挂断

    开启通话设置视频通话的"来电等待"; 步骤1:測试机和配合机A处于视频通话过程中; 步骤2:配合机B向測试机呼出视频电话; 步骤3:測试机接听配合机B的视频来电; 现象:视频通话过 ...

  7. openocd 如何支持FreeRTOS 8.1.2

    沉寂了数年,认为我们应该分享一下.前段时间通过FreeRTOS做点什么,大家纷纷拿出来拍砖. 我应该说,Linux现在粉丝.所以,我的业余时间来分享它通常应用的经验Linux作为桌面开发平台.无需再费 ...

  8. 举例说,Linux核心名单(两)

    使用列表 我认为最好的方式,成为熟悉的核心列表功能是看一些简单的例子,素材去更好的理解链表. 以下是一个样例.包括创建.加入.删除和遍历链表. <span style="font-si ...

  9. JMS分布式应用程序异步消息解决方案EhCache 高速缓存同步问题

    部分博客中描述的使用拦截器怎么用EJB公布的WebService加入缓存,这样能够提高WebService的响应效率.但是即使是这样做,还是要经历网络的传输的.于是决定在调用WebService的程序 ...

  10. Java 审查基调

    1.& 与 &&的差别 两个都有逻辑与的功能. 可是所不同的是.当&两边的表达式不是boolean类型的时候,&具有位与的功能:&&是短路与,当 ...