CSkin博客

标题: 【QQ揭秘】如何实现窗体靠边隐藏?(赠送:GG4.2 源码) [打印本页]

作者: 乔克斯    时间: 2015-3-30 13:21
标题: 【QQ揭秘】如何实现窗体靠边隐藏?(赠送:GG4.2 源码)
【QQ揭秘】如何实现窗体靠边隐藏?(低调赠送:QQ高仿版GG 4.2 最新源码)
说明:
      QQ有个靠边隐藏的功能,使用起来很方便:在屏幕上拖动QQ的主窗体,当窗体的上边沿与屏幕的上边沿对齐时,主窗体就会duang~~地隐藏起来,当将鼠标移到屏幕上边沿的对应区域时,主窗体又会duang~~显示出来。
  我在GG的最新版4.2中也增加了靠边隐藏的功能,支持靠左边沿隐藏、靠上边沿隐藏、靠右边沿隐藏三种模式,并且,将靠边隐藏实现为了一个可复用的组件AutoDocker。
      那么,靠边隐藏功能到底是怎么实现的了?(最初实现的过程中,遇到了很多问题,花了不少时间,现在直接把成果共享出来)

一.靠边隐藏的原理
      靠边隐藏的本质实际上并不是将窗体的Visiable设为false,而是将整个窗体的位置挪到屏幕区域之外。比如,靠右边沿隐藏,实际的效果图如下所示:
     
     方案说明如下:

二.具体实现过程
1.基本元素定义
  首先,我们需要定义靠边隐藏的类型:靠左、靠上、靠右。使用DockHideType枚举表示:
[C#] 纯文本查看 复制代码
/// <summary>
    /// 靠边隐藏的类型。
    /// </summary>
    public enum DockHideType
    {
        /// <summary>
        /// 不隐藏
        /// </summary>
        None = 0,
        /// <summary>
        /// 靠上边沿隐藏
        /// </summary>
        Top,
        /// <summary>
        /// 靠左边沿隐藏
        /// </summary>
        Left,
        /// <summary>
        /// 靠右边沿隐藏
        /// </summary>
        Right
    }

  其次,根据上面的原理描述,我们知道窗体有三种状态:正常显示、准备隐藏、已经隐藏。这三种状态使用FormDockHideStatus枚举表示:
[C#] 纯文本查看 复制代码
/// <summary>
    /// 窗体的显示或隐藏状态
    /// </summary>
    public enum FormDockHideStatus
    {
        /// <summary>
        /// 已隐藏
        /// </summary>
        Hide = 0,

        /// <summary>
        /// 准备隐藏
        /// </summary>
        ReadyToHide,
        
        /// <summary>
        /// 正常显示
        /// </summary>
        ShowNormally

    }


2.判断是否达到隐藏条件
  很明显,我们应当在每次窗体的位置发生变化时,做出这样的判断,所以,这个判断应该在Form的LocationChanged事件中调用。
[C#] 纯文本查看 复制代码
private void dockedForm_LocationChanged(object sender, EventArgs e)
        {
            this.ComputeDockHideType();
            if (!this.IsOrg)
            {
                this.lastBoard = this.dockedForm.Bounds;
                this.IsOrg = true;
            }
        }

        /// <summary>
        /// 判断是否达到了隐藏的条件?以及是哪种类型的隐藏。
        /// </summary>
        private void ComputeDockHideType()
        {
            if (this.dockedForm.Top <= 0)
            {
                this.dockHideType = DockHideType.Top;
                if (this.dockedForm.Bounds.Contains(Cursor.Position))
                {
                    this.formDockHideStatus = FormDockHideStatus.ReadyToHide;
                    return;
                }
                this.formDockHideStatus = FormDockHideStatus.Hide;
                return;
            }
            else
            {
                if (this.dockedForm.Left <= 0)
                {
                    this.dockHideType = DockHideType.Left;
                    if (this.dockedForm.Bounds.Contains(Cursor.Position))
                    {
                        this.formDockHideStatus = FormDockHideStatus.ReadyToHide;
                        return;
                    }
                    this.formDockHideStatus = FormDockHideStaus.Hide;
                    return;
                }
                else
                {
                    if (this.dockedForm.Left < Screen.PrimaryScreen.Bounds.Width - this.dockedForm.Width)
                    {
                        this.dockHideType = DockHideType.None;
                        this.formDockHideStatus = FormDockHideStatus.ShowNormally;
                        return;
                    }
                    this.dockHideType = DockHideType.Right;
                    if (this.dockedForm.Bounds.Contains(Cursor.Position))
                    {
                        this.formDockHideStatus = FormDockHideStatus.ReadyToHide;
                        return;
                    }
                    this.formDockHideStatus = FormDockHideStatus.Hide;
                    return;
                }
            }
        }

上面的代码主要体现了以下几个要点:

      详细分析一下上面的过程,就会发现,当处于准备隐藏状态时,如果将鼠标移出到窗体外(这次移动并没有拖动窗体改变其位置),那么,窗体会一直处于“准备隐藏”的状态。所以,此时,必须要有一个机制来触发它,真正进行隐藏动作。我是用一个定时器来循环判断的。

3.定时检测满足/退出隐藏条件
       我使用一个定时器,每隔300ms检测一次,用于判断从正常显示到隐藏、以及从隐藏到正常显示的转变。

[C#] 纯文本查看 复制代码
/// <summary>
        /// 定时器循环判断。        
        /// </summary>      
        private void CheckPosTimer_Tick(object sender, EventArgs e)
        {//当鼠标移动到窗体的范围内(此时,窗体的位置位于屏幕之外)
            if (this.dockedForm.Bounds.Contains(Cursor.Position))
            {                             if (this.dockHideType!= DockHideType.Top)
                {
                    if (this.dockHideType!= DockHideType.Left)
                    {
                        if (this.dockHideType!= DockHideType.Right)
                        {
                            return;
                        }
                        if (this.formDockHideStatus == FormDockHideStatus.Hide)
                        {
                            this.dockedForm.Location = new Point(Screen.PrimaryScreen.Bounds.Width - this.dockedForm.Width, this.dockedForm.Location.Y);
                            return;
                        }
                    }
                    else
                    {
                        if (this.formDockHideStatus == FormDockHideStatus.Hide)
                        {
                            this.dockedForm.Location = new Point(0, this.dockedForm.Location.Y);
                            return;
                        }
                    }
                }
                else
                {
                    if (this.formDockHideStatus == FormDockHideStatus.Hide)
                    {
                        this.dockedForm.Location = new Point(this.dockedForm.Location.X, 0);
                        return;
                    }
                }
            }
            else //当鼠标位于窗体范围之外,则根据DockHideType的值,决定窗体的位置。
            {                switch (this.dockHideType)
                {
                    case DockHideType.None:
                        {
                            if (this.IsOrg && this.formDockHideStatus == FormDockHideStatus.ShowNormally &&
                               (this.dockedForm.Bounds.Width != this.lastBoard.Width || this.dockedForm.Bounds.Height != this.lastBoard.Height))
                            {
                                this.dockedForm.Size = new Size(this.lastBoard.Width, this.lastBoard.Height);
                            }
                            break;
                        }
                    case DockHideType.Top:
                        {
                            this.dockedForm.Location = new Point(this.dockedForm.Location.X, (this.dockedForm.Height - 4) * -1);
                            return;
                        }      
                    case DockHideType.Left:
                        {                           
                            this.dockedForm.Location = new Point(-1 * (this.dockedForm.Width - 4), this.dockedForm.Location.Y);
                            return;
                        }
                    default:
                        {
                            if (anchorStyles2 != DockHideType.Right)
                            {
                                return;
                            }                           
                            this.dockedForm.Location = new Point(Screen.PrimaryScreen.Bounds.Width - 4, this.dockedForm.Location.Y);
                            return;
                        }
                }
            }
        }

(1)在窗体隐藏的情况下,准确地说,是窗体在屏幕区域之外时,将鼠标光标移动到窗体上(实际上,是窗体的边界),则修改窗体的Location,让其正常显示。
(2)从(1)描述的窗体隐藏切换到正常显示时,代码对窗体的位置进行了控制,使其的边界恰好与屏幕的边界对齐,这样做的目的是,当鼠标再离开窗体范围时,窗体又可以duang隐藏起来。
(3)定时器的循环检测配合鼠标拖动窗体的事件处理,就完全实现了类似QQ的靠边隐藏的效果,而且,我们比QQ更强,QQ只实现了靠上隐藏。

三.如何使用AutoDocker组件
     AutoDocker是以组件(Component)的形式实现的,编译后,会在工具箱中出现一个AutoDocker组件。其使用非常简单:
     从工具箱中将AutoDocker拖放到主窗体MainForm上,然后在主窗体的构造函数中添加一行代码:

[C#] 纯文本查看 复制代码
this.autoDocker1.Initialize(this);

这样,主窗体运行起来后,就拥有了自动靠边隐藏的功能了,是不是很duang~~~
     在GG 4.2的源码中,找到客户端项目(GG2014)下的AutoDocker.cs文件,即可详细研究靠边隐藏的实现细节。

四.GG V4.2 源码
     GG是可在广域网部署运行的QQ高仿版,2013.8.7发布V1.0版本,至今最新是4.2版本,关于GG更详细的介绍,可以查看 可在广域网部署运行的QQ高仿版 -- GG2013概要

GG 4.2 案例源码 下载:GG-V4.2.rar

最后:如果觉得我的工作对你有帮助,请顶我&粉我啊,duang~~~


作者: CastleDrv    时间: 2015-3-30 16:08
duang ~~duang ~~~好厉害~~支持
作者: huhangfei    时间: 2015-3-30 18:31
duang ~~duang ~~~顶一个~~~
作者: 辰晓晨    时间: 2015-3-31 09:24
duang ~~duang ~~~好厉害~~支持
作者: 贱贱的贱贱    时间: 2015-3-31 17:25
干货!,先mark后看。
作者: cwl12315    时间: 2015-4-1 13:21
duang ~~duang ~~~为什么以前没发现这个论坛呢~~~
支持分享
作者: pain    时间: 2015-4-2 16:08
duang ~~duang ~~~顶,顶,顶~~~
作者: elen    时间: 2015-4-4 10:18
duang ~~duang ~~~,厉害,支持一个
作者: 至尊天魂    时间: 2015-4-4 14:51
很好,很有用,代码清晰易懂
作者: tanglide    时间: 2015-4-8 23:01
一直在努力学习中,谢谢分享。
作者: weiyz2011    时间: 2015-4-15 12:53
很不错,项目里用到了
作者: mumupudding    时间: 2015-4-16 09:50






duang ~~duang ~~~顶一个~~~

作者: gujiayue727    时间: 2015-4-16 10:08
回复只为学习
作者: WaterAsh    时间: 2015-4-27 15:35
必须duang~~~
作者: 864548336    时间: 2015-4-29 15:14
顶你!!!!!!!!!!
作者: 麻辣    时间: 2015-7-30 11:05
厉害厉害厉害厉害厉害厉害厉害厉害厉害厉害厉害
作者: yy2010    时间: 2015-8-10 11:35
隐藏之后点击菜单栏那里的图标它会自动最小化,之后再点图标就不会自动显示出来了,这是bug吗?
作者: yy2010    时间: 2015-8-11 16:02
引用了这个类之后将程序最小化后就不能最大化了,只能强制关闭程序,这怎么破?
作者: 乔克斯    时间: 2015-8-12 14:26
yy2010 发表于 2015-8-11 16:02
引用了这个类之后将程序最小化后就不能最大化了,只能强制关闭程序,这怎么破? ...

有的系统会出现这个问题,有的系统正常。。。
作者: yy2010    时间: 2015-8-12 15:43
乔克斯 发表于 2015-8-12 14:26
有的系统会出现这个问题,有的系统正常。。。

居然是这样
作者: vrayshow    时间: 2015-8-13 13:09
duang ~~duang ~~~顶一个~~~
作者: xieris    时间: 2015-9-1 22:35
感谢楼主分享
作者: staraqw    时间: 2015-9-8 15:44
学习了!!!!
作者: 王龙    时间: 2015-9-16 09:24
一直很厉害
作者: asyom    时间: 2016-1-28 12:56
mark!!!!!!!!!!!
作者: 杀手同学    时间: 2016-2-3 14:19
谢谢分享....................
作者: 嘘,安静    时间: 2016-2-7 15:17
6666666666666666666
作者: Georgos    时间: 2016-6-8 15:54
必须支持一下!!1
作者: 世纪缘    时间: 2016-6-11 10:03
59.175.145.163 网页无法正常运作

作者: mingfai123    时间: 2016-6-11 10:25
一直在努力学习中,谢谢分享。
作者: anran1993    时间: 2016-7-6 09:02
很给力赞一个
作者: esf5021314    时间: 2016-10-21 12:25
谢谢楼猪分享,估计很多人在找这个东西了~
作者: ANCB    时间: 2016-11-1 11:52
duang ~~duang ~~~顶,顶,顶~~~
作者: mingsn    时间: 2016-12-7 17:49
顶顶顶!!!QQ的那个背景动态折纸图咋玩的
作者: wxn401    时间: 2016-12-7 18:13
好东西,感谢楼主分享
作者: 霁无瑕    时间: 2016-12-20 17:14
粉你哟~QAQ怎么粉你呀??
作者: l852461246    时间: 2016-12-23 19:42
也有用到定时器吗?
作者: 758132951    时间: 2017-3-14 21:23
谢谢楼主,共同学习
作者: heping072054214    时间: 2017-3-16 14:10
感谢分享啊
作者: 我在长江尾    时间: 2017-11-15 11:07
下载不鸟了~_~
作者: QW去    时间: 2018-1-11 12:57
楼主 附件挂了
作者: lcj21    时间: 2018-1-14 13:45
支持一下,下来看看,感谢分享!
作者: cd37ycs    时间: 2018-5-18 12:16
代码清晰易懂
作者: shycare    时间: 2018-11-2 14:29
duang ~~duang ~~~好厉害~~支持
作者: cid1702    时间: 2021-8-16 21:48
感谢分享~~~~~~~~~~~~




欢迎光临 CSkin博客 (http://bbs.cskin.net/) Powered by Discuz! X3.2