WPF实现基础控件之托盘的示例代码

2022-10-17 18:38:35

WPF 基础控件之托盘

框架使用大于等于.NET40

Visual Studio 2022

项目使用 MIT 开源许可协议。

新建NotifyIcon自定义控件继承自FrameworkElement

创建托盘程序主要借助与 Win32API:

    注册窗体对象RegisterClassEx注册消息获取对应消息标识Id RegisterWindowMessage创建窗体(本质上托盘在创建时需要一个窗口句柄,完全可以将主窗体的句柄给进去,但是为了更好的管理消息以及托盘的生命周期,通常会创建一个独立不可见的窗口)CreateWindowEx

    以下2点需要注意:

      托盘控件的ContextMenu菜单MenuItem 在使用binding时无效,是因为DataContext没有带过去,需要重新赋值一次。托盘控件发送ShowBalloonTip消息通知时候需新建Shell_NotifyIconNuget 最新 Install-Package WPFDevelopers 1.0.9.1-preview

      示例代码

      1) NotifyIcon.cs 代码如下:

      using System;
      using System.IO;
      using System.Runtime.InteropServices;
      using System.Threading;
      using System.Windows;
      using System.Windows.Controls;
      using System.Windows.Controls.Primitives;
      using System.Windows.Data;
      using System.Windows.Input;
      using System.Windows.Media;
      using System.Windows.Media.Imaging;
      using WPFDevelopers.Controls.Runtimes;
      using WPFDevelopers.Controls.Runtimes.Interop;
      using WPFDevelopers.Controls.Runtimes.Shell32;
      using WPFDevelopers.Controls.Runtimes.User32;
      
      namespace WPFDevelopers.Controls
      {
          public class NotifyIcon : FrameworkElement, IDisposable
          {
              private static NotifyIcon NotifyIconCache;
      
              public static readonly DependencyProperty ContextContentProperty = DependencyProperty.Register(
                  "ContextContent", typeof(object), typeof(NotifyIcon), new PropertyMetadata(default));
      
              public static readonly DependencyProperty IconProperty =
                  DependencyProperty.Register("Icon", typeof(ImageSource), typeof(NotifyIcon),
                      new PropertyMetadata(default, OnIconPropertyChanged));
      
              public static readonly DependencyProperty TitleProperty =
                  DependencyProperty.Register("Title", typeof(string), typeof(NotifyIcon),
                      new PropertyMetadata(default, OnTitlePropertyChanged));
      
              public static readonly RoutedEvent ClickEvent =
                  EventManager.RegisterRoutedEvent("Click", RoutingStrategy.Bubble,
                      typeof(RoutedEventHandler), typeof(NotifyIcon));
      
              public static readonly RoutedEvent MouseDoubleClickEvent =
                  EventManager.RegisterRoutedEvent("MouseDoubleClick", RoutingStrategy.Bubble,
                      typeof(RoutedEventHandler), typeof(NotifyIcon));
      
              private static bool s_Loaded = false;
      
              private static NotifyIcon s_NotifyIcon;
      
              //这是窗口名称
              private readonly string _TrayWndClassName;
      
              //这个是窗口消息名称
              private readonly string _TrayWndMessage;
      
              //这个是窗口消息回调(窗口消息都需要在此捕获)
              private readonly WndProc _TrayWndProc;
              private Popup _contextContent;
      
              private bool _doubleClick;
      
              //图标句柄
              private IntPtr _hIcon = IntPtr.Zero;
              private ImageSource _icon;
              private IntPtr _iconHandle;
      
              private int _IsShowIn;
      
              //托盘对象
              private NOTIFYICONDATA _NOTIFYICONDATA;
      
              //这个是传递给托盘的鼠标消息id
              private int _TrayMouseMessage;
      
              //窗口句柄
              private IntPtr _TrayWindowHandle = IntPtr.Zero;
      
              //通过注册窗口消息可以获取唯一标识Id
              private int _WmTrayWindowMessage;
      
              private bool disposedValue;
      
              public NotifyIcon()
              {
                  _TrayWndClassName = $"WPFDevelopers_{Guid.NewGuid()}";
                  _TrayWndProc = WndProc_CallBack;
                  _TrayWndMessage = "TrayWndMessageName";
                  _TrayMouseMessage = (int)WM.USER + 1024;
                  Start();
                  if (Application.Current != null)
                  {
                      //Application.Current.MainWindow.Closed += (s, e) => Dispose();
                      Application.Current.Exit += (s, e) => Dispose();
                  }
                  NotifyIconCache = this;
              }
              static NotifyIcon()
              {
                  DataContextProperty.OverrideMetadata(typeof(NotifyIcon), new FrameworkPropertyMetadata(DataContextPropertyChanged));
                  ContextMenuProperty.OverrideMetadata(typeof(NotifyIcon), new FrameworkPropertyMetadata(ContextMenuPropertyChanged));
              }
              private static void DataContextPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) =>
                  ((NotifyIcon)d).OnDataContextPropertyChanged(e);
              private void OnDataContextPropertyChanged(DependencyPropertyChangedEventArgs e)
              {
                  UpdateDataContext(_contextContent, e.OldValue, e.NewValue);
                  UpdateDataContext(ContextMenu, e.OldValue, e.NewValue);
              }
              private void UpdateDataContext(FrameworkElement target, object oldValue, object newValue)
              {
                  if (target == null || BindingOperations.GetBindingExpression(target, DataContextProperty) != null) return;
                  if (ReferenceEquals(this, target.DataContext) || Equals(oldValue, target.DataContext))
                  {
                      target.DataContext = newValue ?? this;
                  }
              }
              private static void ContextMenuPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
              {
                  var ctl = (NotifyIcon)d;
                  ctl.OnContextMenuPropertyChanged(e);
              }
      
              private void OnContextMenuPropertyChanged(DependencyPropertyChangedEventArgs e) =>
                  UpdateDataContext((ContextMenu)e.NewValue, null, DataContext);
              public object ContextContent
              {
                  get => GetValue(ContextContentProperty);
                  set => SetValue(ContextContentProperty, value);
              }
      
              public ImageSource Icon
              {
                  get => (ImageSource)GetValue(IconProperty);
                  set => SetValue(IconProperty, value);
              }
      
      
              public string Title
              {
                  get => (string)GetValue(TitleProperty);
                  set => SetValue(TitleProperty, value);
              }
      
              public void Dispose()
              {
                  Dispose(true);
                  GC.SuppressFinalize(this);
              }
      
              private static void OnTitlePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
              {
                  if (d is NotifyIcon trayService)
                      trayService.ChangeTitle(e.NewValue?.ToString());
              }
      
              private static void OnIconPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
              {
                  if (d is NotifyIcon trayService)
                  {
                      var notifyIcon = (NotifyIcon)d;
                      notifyIcon._icon = (ImageSource)e.NewValue;
                      trayService.ChangeIcon();
                  }
              }
      
              public event RoutedEventHandler Click
              {
                  add => AddHandler(ClickEvent, value);
                  remove => RemoveHandler(ClickEvent, value);
              }
      
              public event RoutedEventHandler MouseDoubleClick
              {
                  add => AddHandler(MouseDoubleClickEvent, value);
                  remove => RemoveHandler(MouseDoubleClickEvent, value);
              }
      
              private static void Current_Exit(object sender, ExitEventArgs e)
              {
                  s_NotifyIcon?.Dispose();
                  s_NotifyIcon = default;
              }
      
      
              public bool Start()
              {
                  RegisterClass(_TrayWndClassName, _TrayWndProc, _TrayWndMessage);
                  LoadNotifyIconData(string.Empty);
                  Show();
      
                  return true;
              }
      
              public bool Stop()
              {
                  //销毁窗体
                  if (_TrayWindowHandle != IntPtr.Zero)
                      if (User32Interop.IsWindow(_TrayWindowHandle))
                          User32Interop.DestroyWindow(_TrayWindowHandle);
      
                  //反注册窗口类
                  if (!string.IsNullOrWhiteSpace(_TrayWndClassName))
                      User32Interop.UnregisterClassName(_TrayWndClassName, Kernel32Interop.GetModuleHandle(default));
      
                  //销毁Icon
                  if (_hIcon != IntPtr.Zero)
                      User32Interop.DestroyIcon(_hIcon);
      
                  Hide();
      
                  return true;
              }
      
              /// <summary>
              ///     注册并创建窗口对象
              /// </summary>
              /// <param name="className">窗口名称</param>
              /// <param name="messageName">窗口消息名称</param>
              /// <returns></returns>
              private bool RegisterClass(string className, WndProc wndproccallback, string messageName)
              {
                  var wndClass = new WNDCLASSEX
                  {
                      cbSize = Marshal.SizeOf(typeof(WNDCLASSEX)),
                      style = 0,
                      lpfnWndProc = wndproccallback,
                      cbClsExtra = 0,
                      cbWndExtra = 0,
                      hInstance = IntPtr.Zero,
                      hCursor = IntPtr.Zero,
                      hbrBackground = IntPtr.Zero,
                      lpszMenuName = string.Empty,
                      lpszClassName = className
                  };
      
                  //注册窗体对象
                  User32Interop.RegisterClassEx(ref wndClass);
                  //注册消息获取对应消息标识id
                  _WmTrayWindowMessage = User32Interop.RegisterWindowMessage(messageName);
                  //创建窗体(本质上托盘在创建时需要一个窗口句柄,完全可以将主窗体的句柄给进去,但是为了更好的管理消息以及托盘的生命周期,通常会创建一个独立不可见的窗口)
                  _TrayWindowHandle = User32Interop.CreateWindowEx(0, className, "", 0, 0, 0, 1, 1, IntPtr.Zero, IntPtr.Zero,
                      IntPtr.Zero, IntPtr.Zero);
      
                  return true;
              }
      
              /// <summary>
              ///     创建托盘对象
              /// </summary>
              /// <param name="icon">图标路径,可以修改托盘图标(本质上是可以接受用户传入一个图片对象,然后将图片转成Icon,但是算了这个有点复杂)</param>
              /// <param name="title">托盘的tooltip</param>
              /// <returns></returns>
              private bool LoadNotifyIconData(string title)
              {
                  lock (this)
                  {
                      _NOTIFYICONDATA = NOTIFYICONDATA.GetDefaultNotifyData(_TrayWindowHandle);
      
                      if (_TrayMouseMessage != 0)
                          _NOTIFYICONDATA.uCallbackMessage = (uint)_TrayMouseMessage;
                      else
                          _TrayMouseMessage = (int)_NOTIFYICONDATA.uCallbackMessage;
      
                      if (_iconHandle == IntPtr.Zero)
                      {
                          var processPath = Kernel32Interop.GetModuleFileName(new HandleRef());
                          if (!string.IsNullOrWhiteSpace(processPath))
                          {
                              var index = IntPtr.Zero;
                              var hIcon = Shell32Interop.ExtractAssociatedIcon(IntPtr.Zero, processPath, ref index);
                              _NOTIFYICONDATA.hIcon = hIcon;
                              _hIcon = hIcon;
                          }
                      }
      
                      if (!string.IsNullOrWhiteSpace(title))
                          _NOTIFYICONDATA.szTip = title;
                  }
      
                  return true;
              }
      
              private bool Show()
              {
                  var command = NotifyCommand.NIM_Add;
                  if (Thread.VolatileRead(ref _IsShowIn) == 1)
                      command = NotifyCommand.NIM_Modify;
                  else
                      Thread.VolatileWrite(ref _IsShowIn, 1);
      
                  lock (this)
                  {
                      return Shell32Interop.Shell_NotifyIcon(command, ref _NOTIFYICONDATA);
                  }
              }
      
              internal static int AlignToBytes(double original, int nBytesCount)
              {
                  var nBitsCount = 8 << (nBytesCount - 1);
                  return ((int)Math.Ceiling(original) + (nBitsCount - 1)) / nBitsCount * nBitsCount;
              }
      
              private static byte[] GenerateMaskArray(int width, int height, byte[] colorArray)
              {
                  var nCount = width * height;
                  var bytesPerScanLine = AlignToBytes(width, 2) / 8;
                  var bitsMask = new byte[bytesPerScanLine * height];
      
                  for (var i = 0; i < nCount; i++)
                  {
                      var hPos = i % width;
                      var vPos = i / width;
                      var byteIndex = hPos / 8;
                      var offsetBit = (byte)(0x80 >> (hPos % 8));
      
                      if (colorArray[i * 4 + 3] == 0x00)
                          bitsMask[byteIndex + bytesPerScanLine * vPos] |= offsetBit;
                      else
                          bitsMask[byteIndex + bytesPerScanLine * vPos] &= (byte)~offsetBit;
      
                      if (hPos == width - 1 && width == 8) bitsMask[1 + bytesPerScanLine * vPos] = 0xff;
                  }
      
                  return bitsMask;
              }
      
              private byte[] BitmapImageToByteArray(BitmapImage bmp)
              {
                  byte[] bytearray = null;
                  try
                  {
                      var smarket = bmp.StreamSource;
                      if (smarket != null && smarket.Length > 0)
                      {
                          //设置当前位置
                          smarket.Position = 0;
                          using (var br = new BinaryReader(smarket))
                          {
                              bytearray = br.ReadBytes((int)smarket.Length);
                          }
                      }
                  }
                  catch (Exception ex)
                  {
                  }
      
                  return bytearray;
              }
      
              private byte[] ConvertBitmapSourceToBitmapImage(
                  BitmapSource bitmapSource)
              {
                  byte[] imgByte = default;
                  if (!(bitmapSource is BitmapImage bitmapImage))
                  {
                      bitmapImage = new BitmapImage();
      
                      var encoder = new BmpBitmapEncoder();
                      encoder.Frames.Add(BitmapFrame.Create(bitmapSource));
      
                      using (var memoryStream = new MemoryStream())
                      {
                          encoder.Save(memoryStream);
                          memoryStream.Position = 0;
      
                          bitmapImage.BeginInit();
                          bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
                          bitmapImage.StreamSource = memoryStream;
                          bitmapImage.EndInit();
                          imgByte = BitmapImageToByteArray(bitmapImage);
                      }
                  }
      
                  return imgByte;
              }
      
              internal static IconHandle CreateIconCursor(byte[] xor, int width, int height, int xHotspot,
                  int yHotspot, bool isIcon)
              {
                  var bits = IntPtr.Zero;
      
                  BitmapHandle colorBitmap = null;
                  var bi = new BITMAPINFO(width, -height, 32)
                  {
                      bmiHeader_biCompression = 0
                  };
      
                  colorBitmap = Gdi32Interop.CreateDIBSection(new HandleRef(null, IntPtr.Zero), ref bi, 0, ref bits, null, 0);
      
                  if (colorBitmap.IsInvalid || bits == IntPtr.Zero) return IconHandle.GetInvalidIcon();
                  Marshal.Copy(xor, 0, bits, xor.Length);
                  var maskArray = GenerateMaskArray(width, height, xor);
                  var maskBitmap = Gdi32Interop.CreateBitmap(width, height, 1, 1, maskArray);
                  if (maskBitmap.IsInvalid) return IconHandle.GetInvalidIcon();
                  var iconInfo = new Gdi32Interop.ICONINFO
                  {
                      fIcon = isIcon,
                      xHotspot = xHotspot,
                      yHotspot = yHotspot,
                      hbmMask = maskBitmap,
                      hbmColor = colorBitmap
                  };
      
                  return User32Interop.CreateIconIndirect(iconInfo);
              }
      
      
              private bool ChangeIcon()
              {
                  var bitmapFrame = _icon as BitmapFrame;
                  if (bitmapFrame != null && bitmapFrame.Decoder != null)
                      if (bitmapFrame.Decoder is IconBitmapDecoder)
                      {
                          //var iconBitmapDecoder = new Rect(0, 0, _icon.Width, _icon.Height);
                          //var dv = new DrawingVisual();
                          //var dc = dv.RenderOpen();
                          //dc.DrawImage(_icon, iconBitmapDecoder);
                          //dc.Close();
      
                          //var bmp = new RenderTargetBitmap((int)_icon.Width, (int)_icon.Height, 96, 96,
                          //    PixelFormats.Pbgra32);
                          //bmp.Render(dv);
      
      
                          //BitmapSource bitmapSource = bmp;
      
                          //if (bitmapSource.Format != PixelFormats.Bgra32 && bitmapSource.Format != PixelFormats.Pbgra32)
                          //    bitmapSource = new FormatConvertedBitmap(bitmapSource, PixelFormats.Bgra32, null, 0.0);
                          var w = bitmapFrame.PixelWidth;
                          var h = bitmapFrame.PixelHeight;
                          var bpp = bitmapFrame.Format.BitsPerPixel;
                          var stride = (bpp * w + 31) / 32 * 4;
                          var sizeCopyPixels = stride * h;
                          var xor = new byte[sizeCopyPixels];
                          bitmapFrame.CopyPixels(xor, stride, 0);
      
                          var iconHandle = CreateIconCursor(xor, w, h, 0, 0, true);
                          _iconHandle = iconHandle.CriticalGetHandle();
                      }
      
      
                  if (Thread.VolatileRead(ref _IsShowIn) != 1)
                      return false;
      
                  if (_hIcon != IntPtr.Zero)
                  {
                      User32Interop.DestroyIcon(_hIcon);
                      _hIcon = IntPtr.Zero;
                  }
      
                  lock (this)
                  {
                      if (_iconHandle != IntPtr.Zero)
                      {
                          var hIcon = _iconHandle;
                          _NOTIFYICONDATA.hIcon = hIcon;
                          _hIcon = hIcon;
                      }
                      else
                      {
                          _NOTIFYICONDATA.hIcon = IntPtr.Zero;
                      }
      
                      return Shell32Interop.Shell_NotifyIcon(NotifyCommand.NIM_Modify, ref _NOTIFYICONDATA);
                  }
              }
      
              private bool ChangeTitle(string title)
              {
                  if (Thread.VolatileRead(ref _IsShowIn) != 1)
                      return false;
      
                  lock (this)
                  {
                      _NOTIFYICONDATA.szTip = title;
                      return Shell32Interop.Shell_NotifyIcon(NotifyCommand.NIM_Modify, ref _NOTIFYICONDATA);
                  }
              }
      
              public static void ShowBalloonTip(string title, string content, NotifyIconInfoType infoType)
              {
                  if (NotifyIconCache != null)
                      NotifyIconCache.ShowBalloonTips(title, content, infoType);
              }
      
              public void ShowBalloonTips(string title, string content, NotifyIconInfoType infoType)
              {
                  if (Thread.VolatileRead(ref _IsShowIn) != 1)
                      return;
                  var _ShowNOTIFYICONDATA = NOTIFYICONDATA.GetDefaultNotifyData(_TrayWindowHandle);
                  _ShowNOTIFYICONDATA.uFlags = NIFFlags.NIF_INFO;
                  _ShowNOTIFYICONDATA.szInfoTitle = title ?? string.Empty;
                  _ShowNOTIFYICONDATA.szInfo = content ?? string.Empty;
      
                  switch (infoType)
                  {
                      case NotifyIconInfoType.Info:
                          _ShowNOTIFYICONDATA.dwInfoFlags = NIIFFlags.NIIF_INFO;
                          break;
                      case NotifyIconInfoType.Warning:
                          _ShowNOTIFYICONDATA.dwInfoFlags = NIIFFlags.NIIF_WARNING;
                          break;
                      case NotifyIconInfoType.Error:
                          _ShowNOTIFYICONDATA.dwInfoFlags = NIIFFlags.NIIF_ERROR;
                          break;
                      case NotifyIconInfoType.None:
                          _ShowNOTIFYICONDATA.dwInfoFlags = NIIFFlags.NIIF_NONE;
                          break;
                  }
      
                  Shell32Interop.Shell_NotifyIcon(NotifyCommand.NIM_Modify, ref _ShowNOTIFYICONDATA);
              }
      
              private bool Hide()
              {
                  var isShow = Thread.VolatileRead(ref _IsShowIn);
                  if (isShow != 1)
                      return true;
      
                  Thread.VolatileWrite(ref _IsShowIn, 0);
      
                  lock (this)
                  {
                      return Shell32Interop.Shell_NotifyIcon(NotifyCommand.NIM_Delete, ref _NOTIFYICONDATA);
                  }
              }
      
              private IntPtr WndProc_CallBack(IntPtr hwnd, WM msg, IntPtr wParam, IntPtr lParam)
              {
                  //这是窗口相关的消息
                  if ((int)msg == _WmTrayWindowMessage)
                  {
                  }
                  else if ((int)msg == _TrayMouseMessage) //这是托盘上鼠标相关的消息
                  {
                      switch ((WM)(long)lParam)
                      {
                          case WM.LBUTTONDOWN:
                              break;
                          case WM.LBUTTONUP:
                              WMMouseUp(MouseButton.Left);
                              break;
                          case WM.LBUTTONDBLCLK:
                              WMMouseDown(MouseButton.Left, 2);
                              break;
                          case WM.RBUTTONDOWN:
                              break;
                          case WM.RBUTTONUP:
                              OpenMenu();
                              break;
                          case WM.MOUSEMOVE:
                              break;
                          case WM.MOUSEWHEEL:
                              break;
                      }
                  }
                  else if (msg == WM.COMMAND)
                  {
                  }
      
      
                  return User32Interop.DefWindowProc(hwnd, msg, wParam, lParam);
              }
      
              private void WMMouseUp(MouseButton button)
              {
                  if (!_doubleClick && button == MouseButton.Left)
                      RaiseEvent(new MouseButtonEventArgs(
                          Mouse.PrimaryDevice,
                          Environment.TickCount, button)
                      {
                          RoutedEvent = ClickEvent
                      });
                  _doubleClick = false;
              }
      
              private void WMMouseDown(MouseButton button, int clicks)
              {
                  if (clicks == 2)
                  {
                      RaiseEvent(new MouseButtonEventArgs(
                          Mouse.PrimaryDevice,
                          Environment.TickCount, button)
                      {
                          RoutedEvent = MouseDoubleClickEvent
                      });
                      _doubleClick = true;
                  }
              }
      
              private void OpenMenu()
              {
                  if (ContextContent != null)
                  {
                      _contextContent = new Popup
                      {
                          Placement = PlacementMode.Mouse,
                          AllowsTransparency = true,
                          StaysOpen = false,
                          UseLayoutRounding = true,
                          SnapsToDevicePixels = true
                      };
      
                      _contextContent.Child = new ContentControl
                      {
                          Content = ContextContent
                      };
                      UpdateDataContext(_contextContent, null, DataContext);
                      _contextContent.IsOpen = true;
                      User32Interop.SetForegroundWindow(_contextContent.Child.GetHandle());
                  }
                  else if (ContextMenu != null)
                  {
                      if (ContextMenu.Items.Count == 0) return;
      
                      ContextMenu.InvalidateProperty(StyleProperty);
                      foreach (var item in ContextMenu.Items)
                          if (item is MenuItem menuItem)
                          {
                              menuItem.InvalidateProperty(StyleProperty);
                          }
                          else
                          {
                              var container = ContextMenu.ItemContainerGenerator.ContainerFromItem(item) as MenuItem;
                              container?.InvalidateProperty(StyleProperty);
                          }
                      ContextMenu.Placement = PlacementMode.Mouse;
                      ContextMenu.IsOpen = true;
      
                      User32Interop.SetForegroundWindow(ContextMenu.GetHandle());
                  }
              }
      
              protected virtual void Dispose(bool disposing)
              {
                  if (!disposedValue)
                  {
                      if (disposing)
                          Stop();
      
                      disposedValue = true;
                  }
              }
          }
      
          public enum NotifyIconInfoType
          {
              /// <summary>
              ///     No Icon.
              /// </summary>
              None,
      
              /// <summary>
              ///     A Information Icon.
              /// </summary>
              Info,
      
              /// <summary>
              ///     A Warning Icon.
              /// </summary>
              Warning,
      
              /// <summary>
              ///     A Error Icon.
              /// </summary>
              Error
          }
      }
      

      2) NotifyIconExample.xaml 代码如下:

      ContextMenu 使用如下:

      <wpfdev:NotifyIcon Title="WPF开发者">
                  <wpfdev:NotifyIcon.ContextMenu>
                      <ContextMenu>
                          <MenuItem Header="托盘消息" Click="SendMessage_Click"/>
                          <MenuItem Header="退出" Click="Quit_Click"/>
                      </ContextMenu>
                  </wpfdev:NotifyIcon.ContextMenu>
              </wpfdev:NotifyIcon>
      

      ContextContent 使用如下:

      <wpfdev:NotifyIcon Title="WPF开发者">
          <wpfdev:NotifyIcon.ContextContent>
                <Border CornerRadius="3" Margin="10" 
                        Background="{DynamicResource BackgroundSolidColorBrush}" 
                Effect="{StaticResource NormalShadowDepth}">
                <StackPanel VerticalAlignment="Center" Margin="16">
                  <Rectangle Width="100" Height="100">
                    <Rectangle.Fill>
                        <ImageBrush ImageSource="pack://application:,,,/Logo.ico"/>
                    </Rectangle.Fill>
                  </Rectangle>
                      <StackPanel Margin="0,16,0,0" HorizontalAlignment="Center" Orientation="Horizontal">
                        <Button MinWidth="100" Content="关于" 
                                Style="{DynamicResource PrimaryButton}" 
                                Command="{Binding GithubCommand}" />
                        <Button Margin="16,0,0,0" MinWidth="100" Content="退出" Click="Quit_Click"/>
                      </StackPanel>
                    </StackPanel>
                </Border>
           </wpfdev:NotifyIcon.ContextContent>
       </wpfdev:NotifyIcon>
      

      3) NotifyIconExample.cs 代码如下:

      ContextMenu 使用如下:

       private void Quit_Click(object sender, RoutedEventArgs e)
              {
                  Application.Current.Shutdown();
              }
              private void SendMessage_Click(object sender, RoutedEventArgs e)
              {
                  NotifyIcon.ShowBalloonTip("Message", " Welcome to WPFDevelopers.Minimal ", NotifyIconInfoType.None);
              }
      
      

      ContextContent 使用如下:

       private void Quit_Click(object sender, RoutedEventArgs e)
              {
                  Application.Current.Shutdown();
              }
              private void SendMessage_Click(object sender, RoutedEventArgs e)
              {
                  NotifyIcon.ShowBalloonTip("Message", " Welcome to WPFDevelopers.Minimal ", NotifyIconInfoType.None);
              }

      实现效果

      以上就是WPF实现基础控件之托盘的示例代码的详细内容,更多关于WPF托盘的资料请关注易采站长站其它相关文章!