wpf完全模仿qq边缘自动隐藏功能,采用鼠标钩子获取鼠标当前状态,在通过当前鼠标的位置和点击状态来计算是否需要隐藏。

  以下是实现的具体方法:

一、鼠标钩子实时获取当前鼠标的位置和点击状态

    /// <summary>
/// 鼠标全局钩子
/// </summary>
public class MouseHook
{
private const int WM_MOUSEMOVE = 0x200;
private const int WM_LBUTTONDOWN = 0x201;
private const int WM_RBUTTONDOWN = 0x204;
private const int WM_MBUTTONDOWN = 0x207;
private const int WM_LBUTTONUP = 0x202;
private const int WM_RBUTTONUP = 0x205;
private const int WM_MBUTTONUP = 0x208;
private const int WM_LBUTTONDBLCLK = 0x203;
private const int WM_RBUTTONDBLCLK = 0x206;
private const int WM_MBUTTONDBLCLK = 0x209; /// <summary>
/// 点
/// </summary>
[StructLayout(LayoutKind.Sequential)]
public class POINT
{
public int x;
public int y;
} /// <summary>
/// 钩子结构体
/// </summary>
[StructLayout(LayoutKind.Sequential)]
public class MouseHookStruct
{
public POINT pt;
public int hWnd;
public int wHitTestCode;
public int dwExtraInfo;
} public const int WH_MOUSE_LL = ; // mouse hook constant // 装置钩子的函数
[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
public static extern int SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hInstance, int threadId); // 卸下钩子的函数
[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
public static extern bool UnhookWindowsHookEx(int idHook); // 下一个钩挂的函数
[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
public static extern int CallNextHookEx(int idHook, int nCode, Int32 wParam, IntPtr lParam);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
public static extern IntPtr GetModuleHandle(string lpModuleName);
// 全局的鼠标事件
public event MouseEventHandler OnMouseActivity; // 钩子回调函数
public delegate int HookProc(int nCode, Int32 wParam, IntPtr lParam); // 声明鼠标钩子事件类型
private HookProc _mouseHookProcedure;
private static int _hMouseHook = ; // 鼠标钩子句柄 /// <summary>
/// 构造函数
/// </summary>
public MouseHook()
{ } /// <summary>
/// 析构函数
/// </summary>
~MouseHook()
{
Stop();
} /// <summary>
/// 启动全局钩子
/// </summary>
public void Start()
{
// 安装鼠标钩子
if (_hMouseHook == )
{
// 生成一个HookProc的实例.
_mouseHookProcedure = new HookProc(MouseHookProc);
ProcessModule cModule = Process.GetCurrentProcess().MainModule; var mh = GetModuleHandle(cModule.ModuleName);
_hMouseHook = SetWindowsHookEx(WH_MOUSE_LL, _mouseHookProcedure,mh,); //如果装置失败停止钩子
if (_hMouseHook == )
{
Stop();
throw new Exception("SetWindowsHookEx failed.");
}
}
} /// <summary>
/// 停止全局钩子
/// </summary>
public void Stop()
{
bool retMouse = true; if (_hMouseHook != )
{
retMouse = UnhookWindowsHookEx(_hMouseHook);
_hMouseHook = ;
} // 如果卸下钩子失败
// if (!(retMouse))
// throw new Exception("UnhookWindowsHookEx failed.");
}
int isUp = ;
/// <summary>
/// 鼠标钩子回调函数
/// </summary>
private int MouseHookProc(int nCode, Int32 wParam, IntPtr lParam)
{
try
{
// 如果正常运行并且用户要监听鼠标的消息
if ((nCode >= ) && (OnMouseActivity != null))
{
MouseButtons button = MouseButtons.None;
int clickCount = ;
switch (wParam)
{
case WM_LBUTTONDOWN:
button = MouseButtons.Left;
clickCount = ;
isUp = ;
break;
case WM_LBUTTONUP:
button = MouseButtons.Left;
clickCount = ;
isUp = ;
break;
case WM_LBUTTONDBLCLK:
button = MouseButtons.Left;
clickCount = ;
break;
case WM_RBUTTONDOWN:
button = MouseButtons.Right;
clickCount = ;
isUp = ;
break;
case WM_RBUTTONUP:
button = MouseButtons.Right;
clickCount = ;
isUp = ;
break;
case WM_RBUTTONDBLCLK:
button = MouseButtons.Right;
clickCount = ;
break;
default:
if (isUp == ) isUp = ;
break;
} // 从回调函数中得到鼠标的信息
MouseHookStruct MyMouseHookStruct =
(MouseHookStruct) Marshal.PtrToStructure(lParam, typeof (MouseHookStruct));
var x = MyMouseHookStruct.pt.x;
var y = MyMouseHookStruct.pt.y;
MouseEventArgs e = new MouseEventArgs(button, clickCount, x, y, isUp); // 如果想要限制鼠标在屏幕中的移动区域可以在此处设置
// 后期需要考虑实际的x、y的容差
if (!Screen.PrimaryScreen.Bounds.Contains(e.X, e.Y))
{
//return 1;
} OnMouseActivity(this, e);
} }
catch (Exception ex)
{
Debug.WriteLine(ex);
} // 启动下一次钩子
return CallNextHookEx(_hMouseHook, nCode, wParam, lParam);
}
}

钩子程序

二、判断窗口是否在屏幕边缘

  如果是在屏幕边缘,并且鼠标离开窗体,那么就需要隐藏窗口

        /// <summary>
/// 检测是否需要隐藏窗体
/// </summary>
/// <param name="e"></param>
/// <param name="rect"></param>
private void CheckIsHide(MouseEventArgs e,System.Drawing.Rectangle rect)
{
var x = e.X;
var y = e.Y;
if (x < rect.Left) x = rect.Left;
if (x > rect.Right) x = rect.Right;
if (y < rect.Top) y = rect.Top;
if (y > rect.Bottom) y = rect.Bottom; bool isLeave = !(x >= this.Left && x <= (this.Left + this.ActualWidth) &&
y >= this.Top && y <= this.Top + this.ActualHeight);
if (!isLeave)
{
//鼠标在窗体内移动时解除双击状态
_isNoticefyShow = false;
return;
}
//isLeave=true
if (_isNoticefyShow == false)
{
//顶部判断
if (this.Top - _border < rect.Top)
{
SetIsHide(true,rect);
//这里修正高度为边界高度,这样做的原因主要是避免鼠标移动到边框上面时出现闪动
_oldTop = rect.Top;
UpdateLeft(rect);
}
//左边判断
else if (this.Left - _border < rect.Left)
{
SetIsHide(true, rect);
//这里修正左边
_oldLeft = rect.Left;
this.Left = rect.Left - this.ActualWidth;
UpdateTop(rect);
}
//右边判断
else if (this.Left+this.ActualWidth + _border > rect.Right)
{
SetIsHide(true, rect);
//修正右边
_oldLeft = rect.Right - this.ActualWidth;
this.Left = rect.Right;
UpdateTop(rect);
}
}
}

检测是否需要隐藏窗口

三、窗口隐藏时根据鼠标位置判断是否需要显示

  如果鼠标在边框位置,并且进入了上次窗体隐藏的边框内,那么就显示窗体

        /// <summary>
/// 判断鼠标时候在窗体边缘
/// </summary>
/// <returns></returns>
private bool CheckMouseIsWindowBorder(MouseEventArgs e, System.Drawing.Rectangle rect)
{
//获取边界的值
//判断top
if (e.Y - _border <= rect.Top && e.X >= this.Left && e.X <= (this.Left + this.ActualWidth))
{
return true;
}
//判断left
if (e.X - _border<=rect.Left && e.Y >= this.Top && e.Y <= (this.Top + this.ActualHeight))
{
//显示
return true;
}
//判断right
if (this.Left > rect.Left + _border && e.X + _border >= rect.Right && e.Y >= this.Top &&
e.Y <= (this.Top + this.ActualHeight))
{
return true;
} //SystemInformation.VirtualScreen.
//判断右边
return false;
}

判断鼠标是否在窗体边缘

四、双击托盘图标显示窗体

  显示窗体简单,直接展示就可以,关键是要实现窗体显示后如果窗体还在屏幕边框位置,那么需要判断什么情况下需要隐藏窗体,现在有以下两种情况需要隐藏:

  1.鼠标在离开任务栏后经过窗口后在离开窗口,窗口需要自动隐藏

  2.鼠标在离开任务栏后在窗口外的其它位置点击,触发窗口自动隐藏

  1情况容易,在进入窗体时清空托盘图标点击的标记就可以了

  2情况处理有点麻烦,由于在窗口外的其他位置点击这个事件在双击托盘图标的时候也会触发,我们要屏蔽掉这时这个触发条件,只有在托盘外面时去点击才有效,具体的方案是,在鼠标从托盘移动时才标记点击事件有效,这样就可以避免顺序错乱了,部分代码如下;

  (1).在双击托盘图标时标记状态:

_isNoticefyShow = true;
_isCanSet = false;

  (2).鼠标双击后,移动

                if (e.Delta ==  && _isNoticefyShow && _isCanSet == false)
{
//鼠标双击后,移动,并且没有设置
_isCanSet = true;
return;
}

  (3).在鼠标移除托盘图标后,点击鼠标后,接触托盘双击状态

                if ((e.Delta ==  || e.Delta == ) && _isNoticefyShow && _isCanSet)
{
_isNoticefyShow = false;
CheckIsHide(e,rect);
return;
}

具体的实现demo地址如下:

https://gitee.com/sczmzx/WindowAutoHide

wpf仿qq边缘自动停靠,支持多屏的更多相关文章

  1. WPF仿QQ聊天框表情文字混排实现

    原文:WPF仿QQ聊天框表情文字混排实现 二话不说.先上图 图中分别有文件.文本+表情.纯文本的展示,对于同一个list不同的展示形式,很明显,应该用多个DataTemplate,那么也就需要Data ...

  2. wpf 仿QQ图片查看器

    参考博客 WPF下的仿QQ图片查看器 wpf图片查看器,支持鼠标滚动缩放拖拽 实现效果 主要参考的WPF下的仿QQ图片查看器,原博主只给出了部分代码. 没有完成的部分 1.右下角缩略图是原图不是缩略图 ...

  3. C# WPF 仿QQ靠近屏幕上方自动缩起功能实现

    碰到了类似需求,但是上网查了一圈发现感觉要么很复杂,要么代码很臃肿,索性自己实现了一个 几乎和QQ是一模一样的效果,而且核心代码只有20行左右. 代码如下: using System; using S ...

  4. (初学)wpf仿QQ界面-整体布局

    跟一个小学弟一起学习wpf,小学弟是刚初中毕业,对编程刚刚接触,我挺怕自己带的不好,影响小学弟以后在编程方向的学习兴趣.我承认自己水平不高,但是在努力去学习新知识!一起加油吧!在此以博客,记录学习进度 ...

  5. wpf 仿QQ音乐歌词卡拉OK

    最近用WPF做了个音乐播放器,读取歌词.歌词同步都已经实现了.卡拉OK逐字变色 也实现了,但是逐字变色时不能根据歌手唱的快慢来逐字显示.请问各位大神,这个如何解决,有何思路?(附上我做的界面) 感谢各 ...

  6. wpf仿QQ之窗体翻转

    很久以前在网上找过窗体翻转的Demo,但看得不是很明白,大多是内容的翻转,研究了下,现在分享给大家. 利用UIElement.RenderTransform 属性就能实现元素的呈现位置的转换,因此只需 ...

  7. 史上最简单,一步集成侧滑(删除)菜单,高仿QQ、IOS。

    重要的话 开头说,not for the RecyclerView or ListView, for the Any ViewGroup. 本控件不依赖任何父布局,不是针对 RecyclerView. ...

  8. WPF ”真正的“高仿QQ

    时常可以在各种论坛 博客 看到 各种所谓的 高仿QQ. 说实话 越看越想笑呢.(PS:纯粹的 抨击 那些 不追求 UI 完美主义者) 例如:       本次模仿 采用 C# WPF XAML , 总 ...

  9. WPF开发实例——仿QQ登录界面

    原文:WPF开发实例--仿QQ登录界面 版权声明:本文为博主原创文章,如需转载请标明转载地址 http://blog.csdn.net/u013981858 https://blog.csdn.net ...

随机推荐

  1. PHP中获取文件扩展名的N种方法

    PHP中获取文件扩展名的N种方法 从网上收罗的,基本上就以下这几种方式: 第1种方法:function get_extension($file){substr(strrchr($file, '.'), ...

  2. @angular/cli项目构建--Dynamic.Form

    导入所需模块: ReactiveFormsModule DynamicFormComponent.html <div [formGroup]="form"> <l ...

  3. New Concept English three (47)

    Pollution is the price we pay for an overpopulated, over industrialized planet. When you come to thi ...

  4. STL空间配置器解析和实现

    STL空间配置器的强大和借鉴作用不言而喻,查阅资料,发现了Dawn_sf已经对其有了极其深入和详细的描述,所以决定偷下懒借用其内容,只提供自己实现STL空间配置器的源码,具体解析内容参考:(一)STL ...

  5. 【转】浅谈Java中的equals和==

    浅谈Java中的equals和== 在初学Java时,可能会经常碰到下面的代码: String str1 = new String("hello"); String str2 = ...

  6. bzoj 1898 [Zjoi2005]Swamp 沼泽鳄鱼——矩阵快速幂

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=1898 当然是邻接矩阵做转移矩阵来快速幂. 对于鳄鱼,好在它们周期的lcm是12,也就是每12 ...

  7. 引用com.sencha.gxt.ui.GXT加载错误解决方案

    环境GWT2.7+GXT4.0 <inherits name='com.sencha.gxt.ui.GXT' /> 出现加载错误 Loading inherited module 'com ...

  8. UML解析

    1.1 UML基础知识扫盲 UML这三个字母的全称是Unified Modeling Language,直接翻译就是统一建模语言,简单地说就是一种有特殊用途的语言. 你可能会问:这明明是一种图形,为什 ...

  9. Spring集成Quartz定时任务框架介绍

    在JavaEE系统中,我们会经常用到定时任务,比如每天凌晨生成前天报表,每一小时生成汇总数据等等.我们可以使用java.util.Timer结合java.util.TimerTask来完成这项工作,但 ...

  10. JDBC---bai

    import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import ...