找回密码
 立即注册
搜索
热搜: 活动 交友 discuz
查看: 19|回复: 0

[wpf] WPF 中几种方式实现托盘图标功能

[复制链接]
发表于 7 天前 | 显示全部楼层 |阅读模式
本帖最后由 shiy720 于 2025-3-5 14:24 编辑

除了使用 `System.Windows.Forms` 的 `NotifyIcon` 控件外,WPF 中还可以通过以下几种方式实现托盘图标功能:


### 方法 1:使用 WPF 的 `Window` 和 `TaskbarIcon` 第三方库
`TaskbarIcon` 是一个流行的开源库(如 `Hardcodet.NotifyIcon.Wpf`),专门为 WPF 设计,提供了更现代化的托盘图标实现,避免了依赖 Windows Forms。

#### 实现步骤:
1. **安装 NuGet 包**:
   在项目中安装 `Hardcodet.NotifyIcon.Wpf` 包:

  1. Install-Package Hardcodet.NotifyIcon.Wpf
复制代码

2. **在 XAML 中使用 `TaskbarIcon`**:
   在 `MainWindow.xaml` 或 `App.xaml` 中添加 `TaskbarIcon` 控件:
  1. <Window x:Class="WpfTrayIcon.MainWindow"
  2.         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  3.         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  4.         xmlns:tb="http://www.hardcodet.net/taskbar"
  5.         Title="MainWindow" Height="350" Width="525">
  6.     <Grid>
  7.         <!-- 其他内容 -->
  8.     </Grid>

  9.     <!-- 托盘图标 -->
  10.     <tb:TaskbarIcon x:Name="MyNotifyIcon"
  11.                     IconSource="/icon.ico"
  12.                     ToolTipText="My WPF Application">
  13.         <tb:TaskbarIcon.ContextMenu>
  14.             <ContextMenu>
  15.                 <MenuItem Header="Show" Click="OnShowClicked" />
  16.                 <MenuItem Header="Exit" Click="OnExitClicked" />
  17.             </ContextMenu>
  18.         </tb:TaskbarIcon.ContextMenu>
  19.     </tb:TaskbarIcon>
  20. </Window><Window x:Class="WpfTrayIcon.MainWindow"
  21.         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  22.         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  23.         xmlns:tb="http://www.hardcodet.net/taskbar"
  24.         Title="MainWindow" Height="350" Width="525">
  25.     <Grid>
  26.         <!-- 其他内容 -->
  27.     </Grid>

  28.     <!-- 托盘图标 -->
  29.     <tb:TaskbarIcon x:Name="MyNotifyIcon"
  30.                     IconSource="/icon.ico"
  31.                     ToolTipText="My WPF Application">
  32.         <tb:TaskbarIcon.ContextMenu>
  33.             <ContextMenu>
  34.                 <MenuItem Header="Show" Click="OnShowClicked" />
  35.                 <MenuItem Header="Exit" Click="OnExitClicked" />
  36.             </ContextMenu>
  37.         </tb:TaskbarIcon.ContextMenu>
  38.     </tb:TaskbarIcon>
  39. </Window>
复制代码


3. **在代码中处理事件**:
   在 `MainWindow.xaml.cs` 中处理菜单项的点击事件:
  1. private void OnShowClicked(object sender, RoutedEventArgs e)
  2. {
  3.     this.Show();
  4.     this.WindowState = WindowState.Normal;
  5. }

  6. private void OnExitClicked(object sender, RoutedEventArgs e)
  7. {
  8.     Application.Current.Shutdown();
  9. }
复制代码


4. **隐藏窗口时显示托盘图标**:
   在窗口关闭时隐藏窗口而不是退出应用程序:
  1. protected override void OnClosing(CancelEventArgs e)
  2. {
  3.     e.Cancel = true; // 取消关闭
  4.     this.Hide();    // 隐藏窗口
  5.     base.OnClosing(e);
  6. }
复制代码


5. **添加图标文件**:
   确保项目中有一个图标文件(如 `icon.ico`),并将其“生成操作”设置为“资源”。


### 方法 2:使用 Windows API(P/Invoke)
如果不想依赖任何外部库,可以通过调用 Windows API 实现托盘图标功能。这种方法需要手动处理托盘图标的创建、更新和销毁。

#### 实现步骤:
1. **定义 Windows API 结构和方法**:
   在项目中定义必要的 Windows API 结构和 P/Invoke 方法:
  1. using System;
  2. using System.Runtime.InteropServices;
  3. using System.Windows;
  4. using System.Windows.Interop;

  5. public partial class MainWindow : Window
  6. {
  7.     private const int WM_USER = 0x0400;
  8.     private const int WM_LBUTTONDBLCLK = 0x0203;
  9.     private const int WM_RBUTTONUP = 0x0205;
  10.     private const int NIM_ADD = 0x00000000;
  11.     private const int NIM_MODIFY = 0x00000001;
  12.     private const int NIM_DELETE = 0x00000002;
  13.     private const int NIF_MESSAGE = 0x00000001;
  14.     private const int NIF_ICON = 0x00000002;
  15.     private const int NIF_TIP = 0x00000004;

  16.     [StructLayout(LayoutKind.Sequential)]
  17.     private struct NOTIFYICONDATA
  18.     {
  19.         public int cbSize;
  20.         public IntPtr hWnd;
  21.         public int uID;
  22.         public int uFlags;
  23.         public int uCallbackMessage;
  24.         public IntPtr hIcon;
  25.         [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
  26.         public string szTip;
  27.     }

  28.     [DllImport("shell32.dll", SetLastError = true)]
  29.     private static extern bool Shell_NotifyIcon(int dwMessage, ref NOTIFYICONDATA lpData);

  30.     [DllImport("user32.dll", SetLastError = true)]
  31.     private static extern IntPtr LoadIcon(IntPtr hInstance, IntPtr lpIconName);

  32.     [DllImport("user32.dll", SetLastError = true)]
  33.     private static extern bool DestroyIcon(IntPtr hIcon);

  34.     private NOTIFYICONDATA notifyIconData;
  35.     private IntPtr hIcon;

  36.     public MainWindow()
  37.     {
  38.         InitializeComponent();
  39.         InitializeTrayIcon();
  40.     }

  41.     private void InitializeTrayIcon()
  42.     {
  43.         notifyIconData = new NOTIFYICONDATA();
  44.         notifyIconData.cbSize = Marshal.SizeOf(notifyIconData);
  45.         notifyIconData.hWnd = new WindowInteropHelper(this).Handle;
  46.         notifyIconData.uID = 0;
  47.         notifyIconData.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP;
  48.         notifyIconData.uCallbackMessage = WM_USER + 1;
  49.         hIcon = LoadIcon(IntPtr.Zero, new IntPtr(32512)); // 使用默认图标
  50.         notifyIconData.hIcon = hIcon;
  51.         notifyIconData.szTip = "My WPF Application";

  52.         Shell_NotifyIcon(NIM_ADD, ref notifyIconData);
  53.     }

  54.     protected override void OnSourceInitialized(EventArgs e)
  55.     {
  56.         base.OnSourceInitialized(e);
  57.         HwndSource source = HwndSource.FromHwnd(new WindowInteropHelper(this).Handle);
  58.         source.AddHook(WndProc);
  59.     }

  60.     private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
  61.     {
  62.         if (msg == notifyIconData.uCallbackMessage)
  63.         {
  64.             switch ((int)lParam)
  65.             {
  66.                 case WM_LBUTTONDBLCLK:
  67.                     ShowWindow();
  68.                     break;
  69.                 case WM_RBUTTONUP:
  70.                     // 显示右键菜单
  71.                     break;
  72.             }
  73.         }
  74.         return IntPtr.Zero;
  75.     }

  76.     private void ShowWindow()
  77.     {
  78.         this.Show();
  79.         this.WindowState = WindowState.Normal;
  80.     }

  81.     protected override void OnClosing(System.ComponentModel.CancelEventArgs e)
  82.     {
  83.         Shell_NotifyIcon(NIM_DELETE, ref notifyIconData);
  84.         DestroyIcon(hIcon);
  85.         base.OnClosing(e);
  86.     }
  87. }
复制代码
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|Archiver|手机版|小黑屋|西兴社区 ( 蜀ICP备2022005627号 )|网站地图

GMT+8, 2025-3-12 19:48 , Processed in 0.148775 second(s), 22 queries .

Powered by Discuz! X3.5 Licensed

© 2001-2025 Discuz! Team.

快速回复 返回顶部 返回列表