标签:项目 eth bin pat reg bool leave doc getc
原文: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
项目下载:
标签:项目 eth bin pat reg bool leave doc getc
原文地址:https://www.cnblogs.com/lonelyxmas/p/9919858.html