原文:C# WPF QQ新消息托盘悬浮窗效果实现

今天在做一个项目的时候需要这么一个效果,但是网上找了一会发现并没有现成的给我参考(复制),但是呢,我千(到)辛(处)万(抄)苦(袭)想(复)破(制)头(粘)脑(贴)终于还是给做出来了~嘿嘿嘿

QQ新消息悬浮窗即:QQ有新消息时托盘图标会闪动,此时移动鼠标到托盘图标上就会显示一个弹框了,那么呢我把这个弹框称为“QQ新消息托盘悬浮窗”。当鼠标从托盘图标移走后弹框隐藏,我们要做的效果就是这样的。

项目效果图:

涉及到的内容主要有:Popup,win32api,DispatcherTimer(定时器)。

新建wpf项目,命名为qqnotifybar

MainWindow.xaml代码:

<Window x:Class="qqnotifybar.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:qqnotifybar"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Popup Name="NotifyBar" AllowsTransparency="True" Placement="Absolute">
<Grid Name="NotifyBar_Grid" Width="285" Height="126" Background="Red">
<!--为了能更清晰地看到托盘图标是否在弹框的中间位置加一个Grid-->
<Grid Background="Yellow" Width="5" Height="126" HorizontalAlignment="Center"></Grid>
</Grid>
</Popup>
</Grid>
</Window>

后台代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Forms;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Threading; namespace qqnotifybar
{
/// <summary>
/// MainWindow.xaml 的交互逻辑
/// </summary>
public partial class MainWindow : Window
{ //winform托盘图标组件
NotifyIcon notifyicon; //计时器
DispatcherTimer notifybar_timer; //鼠标是否在通知栏内变量
bool NotifyBar_IsMouseEnter = false; public MainWindow()
{ InitializeComponent(); //初始化托盘图标
notifyicon = new NotifyIcon();
notifyicon.Icon = System.Drawing.Icon.ExtractAssociatedIcon(System.Windows.Forms.Application.ExecutablePath);
notifyicon.Text = "Dove";
notifyicon.Visible = true;
notifyicon.MouseMove += notifyicon_mousemove; //初始化计时器
notifybar_timer = new DispatcherTimer();
notifybar_timer.Interval = TimeSpan.FromSeconds();
notifybar_timer.Tick += notifybar_timer_tick; //给弹框中的grid加上鼠标移入移出事件
NotifyBar_Grid.MouseEnter += NotifyBar_MouseEnter;
NotifyBar_Grid.MouseLeave += NotifyBar_MouseLeave; } #region win32api #region 获取鼠标指针相对于屏幕的坐标
[StructLayout(LayoutKind.Sequential)]
public struct POINT
{
public int x;
public int y; public POINT(int x, int y)
{
this.x = x;
this.y = y;
}
} [DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern bool GetCursorPos(out POINT pt);
#endregion #region 获取托盘图标的位置
//代码来自于:http://blog.csdn.net/haoqi9999/article/details/76527981?locationNum=6&fps=1
public class RG
{
public int Left { get; set; }
public int Top { get; set; }
public int Widht { get; set; }
public int Height { get; set; } public RG(int left, int top, int widht, int height)
{
Left = left;
Top = top;
Widht = widht;
Height = height;
}
public override string ToString()
{
return "Left:" + Left + ",Top:" + Top + ",Width:" + Widht + ",Height:" + Height;
}
}
public static RG GetIconRect(NotifyIcon icon)
{
RECT rect = new RECT();
NOTIFYICONIDENTIFIER notifyIcon = new NOTIFYICONIDENTIFIER(); notifyIcon.cbSize = Marshal.SizeOf(notifyIcon);
//use hWnd and id of NotifyIcon instead of guid is needed
notifyIcon.hWnd = GetHandle(icon);
notifyIcon.uID = GetId(icon); int hresult = Shell_NotifyIconGetRect(ref notifyIcon, out rect);
//rect now has the position and size of icon return new RG(rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top);
} [StructLayout(LayoutKind.Sequential)]
private struct RECT
{
public Int32 left;
public Int32 top;
public Int32 right;
public Int32 bottom;
} [StructLayout(LayoutKind.Sequential)]
private struct NOTIFYICONIDENTIFIER
{
public Int32 cbSize;
public IntPtr hWnd;
public Int32 uID;
public Guid guidItem;
} [DllImport("shell32.dll", SetLastError = true)]
private static extern int Shell_NotifyIconGetRect([In]ref NOTIFYICONIDENTIFIER identifier, [Out]out RECT iconLocation); private static FieldInfo windowField = typeof(NotifyIcon).GetField("window", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
private static IntPtr GetHandle(NotifyIcon icon)
{
if (windowField == null) throw new InvalidOperationException("[Useful error message]");
NativeWindow window = windowField.GetValue(icon) as NativeWindow; if (window == null) throw new InvalidOperationException("[Useful error message]"); // should not happen?
return window.Handle;
} private static FieldInfo idField = typeof(NotifyIcon).GetField("id", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
private static int GetId(NotifyIcon icon)
{
if (idField == null) throw new InvalidOperationException("[Useful error message]");
return (int)idField.GetValue(icon);
}
#endregion #endregion #region 操作通知栏
//定时器方法
private void notifybar_timer_tick(object sender, EventArgs e)
{
POINT pt = new POINT(); //获取鼠标的位置
GetCursorPos(out pt); //获取托盘图标的位置
RG rg = GetIconRect(notifyicon); if (pt.x > rg.Left && pt.x < (rg.Left + rg.Widht) && pt.y > rg.Top && pt.y < (rg.Top + rg.Height))
{
//鼠标指针还在托盘图标中,不需要处理
}
else
{
//鼠标移除托盘图标区域 //停止计时器
notifybar_timer.Stop(); //判断指针是否移入了弹框popup的区域
if (NotifyBar_IsMouseEnter == false)
{
//不是则关闭popup
NotifyBar.IsOpen = false;
} }
} //托盘图标鼠标移入
private void notifyicon_mousemove(object sender, System.Windows.Forms.MouseEventArgs e)
{
//鼠标移入托盘图标后显示弹框
NotifyBar.IsOpen = true; //如果计时器没有启动则启动
if (notifybar_timer.IsEnabled == false)
{
notifybar_timer.Start();
} //获取托盘图标位置
RG rg = GetIconRect(notifyicon); //计算弹框的位置,使其在托盘图标的正上方中间的位置
//弹框(popup)距离屏幕上方的位置 = 托盘距离屏幕上方位置 - 弹框的实际高度
NotifyBar.VerticalOffset = rg.Top - NotifyBar_Grid.ActualHeight;
//这里计算使弹框在托盘图标中间的位置
NotifyBar.HorizontalOffset = rg.Left - (NotifyBar_Grid.ActualWidth / ) + (rg.Widht / ); } private void NotifyBar_MouseEnter(object sender, System.Windows.Input.MouseEventArgs e)
{
//鼠标移入弹框GRID区域了 NotifyBar_IsMouseEnter = true;
} private void NotifyBar_MouseLeave(object sender, System.Windows.Input.MouseEventArgs e)
{
//鼠标离开了弹框的GRID区域 //判断鼠标是否曾经移入到弹框GRID区域中过
if (NotifyBar_IsMouseEnter)
{
//是,关闭。 NotifyBar.IsOpen = false;
} //重置鼠标是否移入弹框GRID区域变量值
NotifyBar_IsMouseEnter = false;
}
#endregion } }

代码已经写有超级详细的注释了,博客就不多废话了。核心代码(获取托盘图标位置)的来源也在代码注释标注了,下面也是给大家打包了项目方便下载。

提醒:在正式项目中要将popup换成新的window,因为当popup所在窗体不具备焦点时,popup内的控件无法正常交互。2018年1月23日09:33:08

项目下载:

点我下载

C# WPF QQ新消息托盘悬浮窗效果实现的更多相关文章

  1. Android桌面悬浮窗效果实现,仿360手机卫士悬浮窗效果

    大家好,今天给大家带来一个仿360手机卫士悬浮窗效果的教程,在开始之前请允许我说几句不相干的废话. 不知不觉我发现自己接触Android已有近三个年头了,期间各种的成长少不了各位高手的帮助,总是有很多 ...

  2. 简易的可拖动的桌面悬浮窗效果Demo

    首先,我们需要知道,悬浮窗分为两种:Activity级别的悬浮窗,系统级别的悬浮窗 Activity级别的悬浮窗跟随所属Activity的生命周期而变化,而系统级别的悬浮窗则可以脱离Activity而 ...

  3. Android仿360手机卫士悬浮窗效果

    请看下图:                         首先是一个小的悬浮窗显示的是当前使用了百分之多少的内存,点击一下小悬浮窗,就会弹出一个大的悬浮窗,可以一键加速.好,我们现在就来模拟实现一下 ...

  4. Android 桌面悬浮窗效果实现,仿360手机卫士悬浮窗效果

    首先是一个小的悬浮窗显示的是当前使用了百分之多少的内存,点击一下小悬浮窗,就会弹出一个大的悬浮窗,可以一键加速.好,我们现在就来模拟实现一下类似的效果. 先谈一下基本的实现原理,这种桌面悬浮窗的效果很 ...

  5. Qt实现悬浮窗效果

    当鼠标移动到头像控件时,显示悬浮窗,当鼠标离开时,悬浮窗隐藏.   1.控件选择 悬浮窗可以从QDialog派生,并将窗口的属性设置为无边框 this->setWindowFlags(this- ...

  6. 小程序 movable-area 实现悬浮窗效果

    最近做一个小程序 实现页面内悬浮窗的效果 给自己制定两个方案: 1.通过一个自定义的组件,通过触摸事件进行实现: 2.使用微信的movable移动组件实现: 第一种方案: 结果:实现了 悬浮窗和自动靠 ...

  7. Android WindowManager实现悬浮窗效果 (一)——与当前Activity绑定

    最近有学生做毕业设计,想使用悬浮窗这种效果,其实很简单,我们可以通过系统服务WindowManager来实现此功能,本章我们来试验一下在当前Activity之上创建一个悬浮的view. 第一步:认识W ...

  8. Android BGABadgeView:新消息/未接来电/未读消息/新通知圆球红点提示(1)

     Android BGABadgeView:新消息/未接来电/未读消息/新通知圆球红点提示(1) 现在很多的APP会有新消息/未接来电/未读消息/新通知圆球红点提示,典型的以微信.QQ新消息提示为 ...

  9. Android 悬浮窗

    悬浮窗是一种比较常见的需求.例如把视频通话界面缩小成一个悬浮窗,然后用户可以在其他界面上处理事情. 本文给出一个简单的悬浮窗实现.可缩小activity和还原大小.可悬浮在其他activity上.使用 ...

随机推荐

  1. GTK入门学习:glade的使用

    搭建好环境后,在终端敲 glade 就可以启动glade工具. glade的总体框图: 经常使用控件选择区:列举了经常使用的控件,经常使用的有三类:顶层(主窗体等).容器(各种布局容器等).控制和显示 ...

  2. windows ffmpeg 的安装

    本文我们要安装的是 windows 下的 ffmpeg 命令行工具,安装的步骤十分简单,分为:下载.解压.配置环境变量. 下载,进入 http://ffmpeg.org/download.html#b ...

  3. FATFS在SD卡里,写入多行数据出的问题

    串口接收的数据存入数组,然后把数组截取有效部分,存入SD卡里的一行没有问题 但是从SD卡读出这一行之后,重新写入SD卡就有了问题,经过调试发现,错误在于  \n 一直是这一串数据,为什么会出错呢??? ...

  4. 一个例子讲解wav头文件 stm32声音程序 录音和播放 wav

    下面我们一wav头文件来分析一下: 下面是双声道的,16位,48000采样录的wav文件: 打开属性,能看到的有用信息只有比特率了: 上图的比特率就是 wav头文件里的bitrate: 1536kbp ...

  5. [AngularFire2] Update multi collections at the same time with FirebaseRef

    At some point, you might need to udpate multi collections and those collections should all updated s ...

  6. PWA之Web 应用清单

    原文 简书原文:https://www.jianshu.com/p/5c96242188e8 大纲 1.什么是Web 应用清单 2.“清单文件”:Web App Manifest 规范的应用 3.we ...

  7. JVM调优2

    原文地址:https://blog.csdn.net/sun1021873926/article/details/78002118 一.什么是JVM  JVM是Java Virtual Machine ...

  8. Android addTextChangedListener(文本监听)参数解释及实现EditText字数监听

    由于最近做项目要检测EditText中输入的字数长度,从而接触到了Android中EditText的监听接口,TextWatcher.它有三个成员方法,第一个after很简单,这个方法就是在EditT ...

  9. js获取计算后的样式表

    在编写html时,使用dom对象的style属性可以获取标签里的style属性,但是不能获取单独css样式文件或者style标签的属性值 <div style="width:10px& ...

  10. ITFriend创业败局(序):简要概述我的第一次创业经历

    是时候, 面对过去,继续踏上未来之路了.    是时候,该给自己一个交待了,给ITFriend创业合伙人.ITFriend用户.关注我的朋友和网友们一个答复了.    是时候,全面认真总结过去的经历. ...