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

简介: 原文:C# WPF QQ新消息托盘悬浮窗效果实现今天在做一个项目的时候需要这么一个效果,但是网上找了一会发现并没有现成的给我参考(复制),但是呢,我千(到)辛(处)万(抄)苦(袭)想(复)破(制)头(粘)脑(贴)终于还是给做出来了~嘿嘿嘿 QQ新消息悬浮窗即:QQ有新消息时托盘图标会闪动,此时移动鼠标到托盘图标上就会显示一个弹框了,那么呢我把这个弹框称为“QQ新消息托盘悬浮窗”。
原文: 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(1);
            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 / 2) + (rg.Widht / 2);
           
            
        }



        
        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 界面实现多语言支持 中英文切换 动态加载资源字典
原文:WPF 界面实现多语言支持 中英文切换 动态加载资源字典 1、使用资源字典,首先新建两个字典文件en-us.xaml、zh-cn.xaml。定义中英文的字符串在这里面【注意:添加xmlns:s="clr-namespace:System;assembly=mscorlib】 zh-cn.
2901 0
|
C# 数据可视化 开发工具
WPF实现选项卡效果(1)——使用AvalonDock
原文:WPF实现选项卡效果(1)——使用AvalonDock 简介   公司最近一个项目,软件采用WPF开发,需要实现类似于VS的选项卡(或者是浏览器的选项卡)效果。
2117 0
|
IDE C# 开发工具
WPF钟表效果实现
中WPF中的RotateTransform实现UI元素的旋转,并模拟钟表的秒针、分针和时针。
1125 0
WPF钟表效果实现
|
IDE 编译器 C#
WPF实现强大的动态公式计算
数据库可以定义表不同列之间的计算公式,进行自动公式计算,但如何实现行上的动态公式计算呢?行由于可以动态扩展,在某些应用场景下将能很好的解决实际问题。本文就探讨一下如何在WPF中实现一种基于行字段的动态公式计算。
990 0
WPF实现强大的动态公式计算
|
网络协议 C# 移动开发
C# WPF上位机实现和下位机TCP通讯
C# WPF上位机实现和下位机TCP通讯下位机使用北京大华程控电源DH1766-1,上位机使用WPF。实现了电压电流实时采集,曲线显示。上午在公司调试成功,手头没有程控电源,使用TCP服务端模拟。昨天写的TCP服务端正好排上用场。
2337 0
|
C#
WPF特效-实现3D足球效果
原文:WPF特效-实现3D足球效果 WPF 实现 3D足球效果,效果图如下:  每个面加载不同贴图。                                                          ...
860 0
|
算法 C# 容器
WPF特效-实现弧形旋转轮播图
原文:WPF特效-实现弧形旋转轮播图        项目遇到,琢磨并实现了循环算法,主要处理循环替换显示问题       (如:12张图组成一个圆弧,但总共有120张图需要呈现,如何在滑动中进行显示块的替换,并毫无卡顿)        处理的自己感觉比较满意,记录一下。
2042 0
|
C#
wpf采用Xps实现文档显示、套打功能
原文:wpf采用Xps实现文档显示、套打功能 近期的一个项目需对数据进行套打,用户要求现场不允许安装office、页面预览显示必须要与文档完全一致,xps文档来对数据进行处理。Wpf的DocumentView 控件可以直接将数据进行显示,xps也是一种开放式的文档,如果我们能够替换里面的标签就最终实现了我们想要的效果。
1734 0
|
C# 开发工具 git
WPF实现选项卡效果(3)——自定义动态添加的AvalonDock选项卡内容
原文:WPF实现选项卡效果(3)——自定义动态添加的AvalonDock选项卡内容 简介   在前面一篇文章里面,我们实现了AvalonDock选项卡的动态添加,但是对于选项卡里面的内容,我们并没有实现任何有用的功能。
1292 0
|
C# 开发工具 git
WPF实现选项卡效果(2)——动态添加AvalonDock选项卡
原文:WPF实现选项卡效果(2)——动态添加AvalonDock选项卡 简介   在前面一篇文章里面,我们使用AvalonDock实现了类似于VS的选项卡(或者浏览器的选项卡)效果。
1698 0