C# WPF QQ新消息托盤懸浮窗效果實現

原文: C# WPF QQ新消息托盤懸浮窗效果實現

今天在作一個項目的時候須要這麼一個效果,可是網上找了一會發現並無現成的給我參考(複製),可是呢,我千(到)辛(處)萬(抄)苦(襲)想(復)破(制)頭(粘)腦(貼)終於仍是給作出來了~嘿嘿嘿html

QQ新消息懸浮窗即:QQ有新消息時托盤圖標會閃動,此時移動鼠標到托盤圖標上就會顯示一個彈框了,那麼呢我把這個彈框稱爲「QQ新消息托盤懸浮窗」。當鼠標從托盤圖標移走後彈框隱藏,咱們要作的效果就是這樣的。shell

項目效果圖:express

涉及到的內容主要有:Popup,win32api,DispatcherTimer(定時器)。api

新建wpf項目,命名爲qqnotifybarapp

MainWindow.xaml代碼:ide

<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>

後臺代碼:ui

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 } }

代碼已經寫有超級詳細的註釋了,博客就很少廢話了。核心代碼(獲取托盤圖標位置)的來源也在代碼註釋標註了,下面也是給你們打包了項目方便下載。this

提醒:在正式項目中要將popup換成新的window,由於當popup所在窗體不具有焦點時,popup內的控件沒法正常交互。2018年1月23日09:33:08 url

項目下載:spa

點我下載

相關文章
相關標籤/搜索