shiy720 发表于 2025-3-5 14:21:38

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

本帖最后由 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` 包:

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

    <!-- 托盘图标 -->
    <tb:TaskbarIcon x:Name="MyNotifyIcon"
                  IconSource="/icon.ico"
                  ToolTipText="My WPF Application">
      <tb:TaskbarIcon.ContextMenu>
            <ContextMenu>
                <MenuItem Header="Show" Click="OnShowClicked" />
                <MenuItem Header="Exit" Click="OnExitClicked" />
            </ContextMenu>
      </tb:TaskbarIcon.ContextMenu>
    </tb:TaskbarIcon>
</Window><Window x:Class="WpfTrayIcon.MainWindow"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:tb="http://www.hardcodet.net/taskbar"
      Title="MainWindow" Height="350" Width="525">
    <Grid>
      <!-- 其他内容 -->
    </Grid>

    <!-- 托盘图标 -->
    <tb:TaskbarIcon x:Name="MyNotifyIcon"
                  IconSource="/icon.ico"
                  ToolTipText="My WPF Application">
      <tb:TaskbarIcon.ContextMenu>
            <ContextMenu>
                <MenuItem Header="Show" Click="OnShowClicked" />
                <MenuItem Header="Exit" Click="OnExitClicked" />
            </ContextMenu>
      </tb:TaskbarIcon.ContextMenu>
    </tb:TaskbarIcon>
</Window>

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

private void OnExitClicked(object sender, RoutedEventArgs e)
{
    Application.Current.Shutdown();
}

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

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


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

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

public partial class MainWindow : Window
{
    private const int WM_USER = 0x0400;
    private const int WM_LBUTTONDBLCLK = 0x0203;
    private const int WM_RBUTTONUP = 0x0205;
    private const int NIM_ADD = 0x00000000;
    private const int NIM_MODIFY = 0x00000001;
    private const int NIM_DELETE = 0x00000002;
    private const int NIF_MESSAGE = 0x00000001;
    private const int NIF_ICON = 0x00000002;
    private const int NIF_TIP = 0x00000004;

   
    private struct NOTIFYICONDATA
    {
      public int cbSize;
      public IntPtr hWnd;
      public int uID;
      public int uFlags;
      public int uCallbackMessage;
      public IntPtr hIcon;
      
      public string szTip;
    }

   
    private static extern bool Shell_NotifyIcon(int dwMessage, ref NOTIFYICONDATA lpData);

   
    private static extern IntPtr LoadIcon(IntPtr hInstance, IntPtr lpIconName);

   
    private static extern bool DestroyIcon(IntPtr hIcon);

    private NOTIFYICONDATA notifyIconData;
    private IntPtr hIcon;

    public MainWindow()
    {
      InitializeComponent();
      InitializeTrayIcon();
    }

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

      Shell_NotifyIcon(NIM_ADD, ref notifyIconData);
    }

    protected override void OnSourceInitialized(EventArgs e)
    {
      base.OnSourceInitialized(e);
      HwndSource source = HwndSource.FromHwnd(new WindowInteropHelper(this).Handle);
      source.AddHook(WndProc);
    }

    private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
    {
      if (msg == notifyIconData.uCallbackMessage)
      {
            switch ((int)lParam)
            {
                case WM_LBUTTONDBLCLK:
                  ShowWindow();
                  break;
                case WM_RBUTTONUP:
                  // 显示右键菜单
                  break;
            }
      }
      return IntPtr.Zero;
    }

    private void ShowWindow()
    {
      this.Show();
      this.WindowState = WindowState.Normal;
    }

    protected override void OnClosing(System.ComponentModel.CancelEventArgs e)
    {
      Shell_NotifyIcon(NIM_DELETE, ref notifyIconData);
      DestroyIcon(hIcon);
      base.OnClosing(e);
    }
}
页: [1]
查看完整版本: WPF 中几种方式实现托盘图标功能