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);
            }
        }
    }
}
时间: 2025-01-20 19:44:56

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

图片-HTML 怎么实现类似QQ邮箱中右边 通讯录 下拉条效果..

问题描述 HTML 怎么实现类似QQ邮箱中右边 通讯录 下拉条效果.. 新手提问..就是类似这样有个下拉条的..我想不通DIV标签是怎么实现的 解决方案 这个简单,你直接打开qq邮箱,然后打开IE F12,照着抄就可以了. 解决方案二: 不就是个treeview么......... 解决方案三: ...哦.刚找到了overflow: scroll就可以: - =还是谢谢了 解决方案四: scroll的css

Winform窗体类似QQ差不多的样式

问题描述 在创建Winform窗体应用程序的时候,希望实现像QQ窗体那种半透明窗体,看起来很柔滑的感觉.不要隐藏边框,让整个窗体融为一体的那种感觉,好像是继承什么API函数,忘记了,求高手! 解决方案 解决方案二:网上解决方案好像挺多的啊,google搜一下,参考http://826891.blog.163.com/blog/static/8637693201132061446148/解决方案三:这个是设置窗口的属性..Opacity这个属性.这个属性是一个百分比哈.解决方案四:去我的资源里面下

js图片展示特效(类似QQ网的JS相册展示特效)

<html> <head> <meta http-equiv="content-type" content="text/html; charset=gb2312" /> <title>特效)</title> <link href='images/layout.css教程' type="text/css" rel="stylesheet" media="

如何在给listbox赋值的时候显示成滚动添加的效果

问题描述 我想给一个listbox赋值,想实现一行一行添加的效果.怎么实现啊 解决方案 解决方案二:Application.DoEvents();解决方案三:C/S?B/S?

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

阅读对象:入门级,老鸟漂过 大家都知道QQ有一个自动停靠功能,即"上.左.右",当你把窗体拖到屏幕边缘,然后移开鼠标它会自动缩放,然后只显示一小小点出来,我们仔细观察会发现其实它只露3像素左右的边缘,当你鼠标移上去它又会伸出来,介于普通入门级学者要求艾伟就在这里给需要的朋友们分享分享我是怎么实现的,代码很少,效果如下: 先在当前类里弄几个变量,方便逻辑判断: QQ_MODE(用于记录窗体当前的停靠状态,即0为不停靠,1为X轴,2为Y轴,3为顶部),QQ_T(窗体缩放时显示出来的边缘大小

基于JS如何实现类似QQ好友头像hover时显示资料卡的效果(推荐)_javascript技巧

一.应用场景 鼠标hover弹出div,并且鼠标离开后不能马上隐藏,因为这个div上还有功能入口.比如: 鼠标经过好友列表中的好友头像时显示资料卡的效果如下所示: hover时显示二维码 二.实现 用如下这样一个简单的效果:鼠标hover到A上显示B来模拟 有2种实现方式,推荐第二种,第一种有弊端下面会说. 1.方法一 原理:把触发元素A和要显示元素B放于同一个父级元素内,鼠标经过父级元素时触发显示B.这样鼠标移动到B时仍然 处于该父级元素内,则div不会隐藏. 代码: <!DOCTYPE ht

Android 桌面图标上显示未读消息的图标(类似qq的桌面提示效果)

问题描述 Android 桌面图标上显示未读消息的图标(类似qq的桌面提示效果) 当有新的消息之后,在桌面上提示出来,在网上找了几天,都是只有几个牌子的手机支持,哪位大神有比较全的. 解决方案 Android 类似未读短信图标显示数字效果的分析Android 类似未读短信图标显示数字效果的分析Android 类似未读短信图标显示数字效果的分析 解决方案二: 没研究过,你可以去github上看看 解决方案三: https://github.com/xuyisheng/ShortcutHelper

Android软键盘的显示隐藏功能实现过程

一.软键盘显示的原理 软件盘的本质是什么?软键盘其实是一个Dialog! InputMethodService为我们的输入法创建了一个Dialog,并且将该Dialog的Window的某些参数(如Gravity)进行了设置,使之能够在底部或者全屏显示.当我们点击输入框时,系统对活动主窗口进行调整,从而为输入法腾出相应的空间,然后将该Dialog显示在底部,或者全屏显示. 二.活动主窗口调整 android定义了一个属性,名字为windowSoftInputMode, 用它可以让程序可以控制活动主

JQUERY 实现窗口滚动搜索框停靠效果(类似滚动停靠)_jquery

当页面需要显示的内容较多时,我们很多人采用分页的方法解决. 而有的时候,分页的效果却又是非常令人厌恶的.滚动条无疑是一种简单而又高效的一种方式.而这里,处于对用户体验的考虑,我使用Jquery实现了一种类似"滚动停靠"的效果.这样当我们向下滚动内容的时候,搜索框会"悬挂(停靠)"在窗口顶端. 这样做的好处是,当用户需要重新筛选内容的时候,不必再次向上滚动,随时可以输入条件进行搜索. 以下是我实现这个效果的思路: 首先,设计一个隐藏的搜索框.这个隐藏的搜索框的样式.事