WPF 基礎控件之托盤

?WPF 基礎控件之托盤

控件名:NotifyIcon

作者: WPFDevelopersOrg ?- 吳鋒|驚鏵

原文鏈接: ? ?https://github.com/WPFDevelopersOrg/WPFDevelopers

  • 框架使用大于等于.NET40

  • Visual Studio 2022

  • 項目使用 MIT 開源許可協議。

  • 新建NotifyIcon自定義控件繼承自FrameworkElement

  • 創建托盤程序主要借助與 Win32API[1]:

    • 注冊窗體對象RegisterClassEx

    • 注冊消息獲取對應消息標識Id RegisterWindowMessage

    • 創建窗體(本質上托盤在創建時需要一個窗口句柄,完全可以將主窗體的句柄給進去,但是為了更好的管理消息以及托盤的生命周期,通常會創建一個獨立不可見的窗口)CreateWindowEx

  • 以下2點需要注意:

    • 托盤控件的ContextMenu菜單MenuItem 在使用binding時無效,是因為DataContext沒有帶過去,需要重新賦值一次。

    • 托盤控件發送ShowBalloonTip消息通知時候需新建Shell_NotifyIcon

  • Nuget 最新[2]Install-Package WPFDevelopers 1.0.9.1-preview

8209a681c196e903927c377d1d2fa9d2.png

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;//這個是傳遞給托盤的鼠標消息idprivate?int?_TrayMouseMessage;//窗口句柄private?IntPtr?_TrayWindowHandle?=?IntPtr.Zero;//通過注冊窗口消息可以獲取唯一標識Idprivate?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));//銷毀Iconif?(_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;elseThread.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;elsebitsMask[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);}

?鳴謝 - 吳鋒

d37bbee4574ca70d7907e9dd31c7f493.gif

Github|NotifyIcon[3]
碼云|NotifyIcon[4]

參考資料

[1]

Win32API: https://docs.microsoft.com/en-us/windows/win32/api/shellapi/ns-shellapi-notifyicondataw

[2]

Nuget : https://www.nuget.org/packages/WPFDevelopers/

[3]

Github|NotifyIcon: https://github.com/WPFDevelopersOrg/WPFDevelopers/blob/master/src/WPFDevelopers.Samples/ExampleViews/MainWindow.xaml

[4]

碼云|NotifyIcon: https://gitee.com/WPFDevelopersOrg/WPFDevelopers/blob/master/src/WPFDevelopers.Samples/ExampleViews/MainWindow.xaml

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/281855.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/281855.shtml
英文地址,請注明出處:http://en.pswp.cn/news/281855.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

java 匿名 異常_JAVA類(內部類、匿名內部類、異常、自定義異常)

內部類package AA;public class類 {int de123;StringBuffer deenewStringBuffer();public class成員內部類{public voidff() {System.out.println("這是成員內部類方法");}}/*1.可以訪問外部類所有的成員&#xff0c;包括被聲明為私有(private)的&#xff1b;2.可以使…

ASP.NET 多環境下配置文件web.config的靈活配置---轉

注意&#xff1a;本功能在.Net Core中已經不可用&#xff0c;暫時需手動修改web.config中的信息&#xff0c;或者將其設置在appsettings.XXX.json中&#xff0c;然后再使用web.config中的環境變量來制定使用的具體appsettings文件。 轉自&#xff1a;https://www.cnblogs.com/h…

英語之各類人群

工作狂&#xff1a;workaholic 月光族&#xff1a;moonlight group 電燈泡&#xff1a;third wheel 菜鳥&#xff1a;newbie 夜貓子&#xff1a;night owl 路癡&#xff1a;somebody has no sense of derection 宅男宅女&#xff1a;homebody 時尚的潮人&#xff1a;trend sett…

Wget CVE-2014-4877:FTP 符號鏈接任意文件系統訪問

Wget 爆出 CVE-2014-4877 漏洞&#xff1a;FTP 符號鏈接任意文件系統訪問。 Package: wgetVersion: 1.15-1Severity: important Upstream fix&#xff1a; http://git.savannah.gnu.org/cgit/wget.git/commit/?id18b0979357ed7dc4e11d4f2b1d7e0f5932d82aa7 References&#xf…

JavaScript 學習提升

javascript 技能提升 理解閉包 閉包&#xff0c;官方對閉包的解釋是&#xff1a;一個擁有許多變量和綁定了這些變量的環境的表達式&#xff08;通常是一個函數&#xff09;&#xff0c;因而這些變量也是該表達式的一部分。閉包的特點&#xff1a;1. 作為一個函數變量的一個引用…

Uranium UI Kit

Uranium UI Kit控件名&#xff1a;Uranium UI Ki作者&#xff1a;enisn原文鏈接&#xff1a; https://github.com/enisn/UraniumUI項目使用 Apache-2.0 開源許可協議。Uranium 是用于 .NET MAUI 的免費和開源 UI 工具包。它提供了一組控件和實用程序來構建現代應用程序。它建…

數據庫監控框架 oneproxy-monitor 開源了

OneProxy-Monitor 是數據庫監控的框架&#xff0c;通過這個框架可以快速的開發出一款數據庫的監控軟件。目前已經在這個框架下面開發出來了sql server的監控軟件oneproxy-for-sqlserver&#xff0c; postgresql的監控軟件oneproxy-for-postgresql。并且還可以作為協議成的調試軟…

java nio epoll_Java NIO 選擇器(Selector)的內部實現(poll epoll)

http://blog.csdn.net/hsuxu/article/details/9876983之前強調這么多關于linux內核的poll及epoll&#xff0c;無非是想讓大家先有個認識&#xff1a;Java NIO中的選擇器依賴操作系統內核的這些系統調用&#xff0c;我們這里只講解與linux內核相關的NIO實現&#xff0c;當然&…

Next.js 7發布,構建速度提升40%

Next.js團隊發布了其開源React框架的7版本。該版本的Next.js主要是改善整體的開發體驗&#xff0c;包括啟動速度提升57%、開發時的構建速度提升40%、改進錯誤報告和WebAssembly支持。\\Next.js是一個React框架&#xff0c;它的主要目標是在生產環境中提供出色的性能和良好的開發…

《Java多線程編程核心技術》讀后感(十五)

線程的狀態 線程對象在不同的運行時期有不同的狀態&#xff0c;狀態信息就存在與State枚舉類中。 驗證New,Runnable&#xff0c;Terminated new:線程實例化后還從未執行start()方法時的狀態 runnable&#xff1a;線程進入運行的狀態 terminated&#xff1a;線程被銷毀時的狀態 …

隊列(queue)

隊列(queue)和棧一樣支持push和pop兩個操作。但與棧不同的是,pop兩個操作。但與棧的不同的是&#xff0c;pop完成的不是取出最頂端的元素&#xff0c;而是最底端的元素。也就是說最初放入的元素能夠最先被取出&#xff08;這種行為被叫做FIFO:First In First Out&#xff0c;即…

一題多解,ASP.NET Core應用啟動初始化的N種方案[上篇]

ASP.NET Core應用本質上就是一個由中間件構成的管道&#xff0c;承載系統將應用承載于一個托管進程中運行起來&#xff0c;其核心任務就是將這個管道構建起來。在ASP.NET Core的發展歷史上先后出現了三種應用承載的編程方式&#xff0c;而且后一種編程模式都提供了針對之前編程…

java程序猿面試問缺點怎么回答_JAVA程序員面試32問,你能回答多少題

第一&#xff0c;談談final&#xff0c; finally&#xff0c; finalize的區別。第二&#xff0c;Anonymous Inner Class (匿名內部類) 是否可以extends(繼承)其它類&#xff0c;是否可以implements(實現)interface(接口)&#xff1f;第三&#xff0c;Static Nested Class 和 In…

ExecutorService——shutdown方法和awaitTermination方法

ExecutorService的關閉shutdown和awaitTermination為接口ExecutorService定義的兩個方法&#xff0c;一般情況配合使用來關閉線程池。 方法簡介shutdown方法&#xff1a;平滑的關閉ExecutorService&#xff0c;當此方法被調用時&#xff0c;ExecutorService停止接收新的任務并且…

WPF 簡單模仿 VSCode 界面布局

WPF 簡單模仿 VSCode 界面布局本文經原作者授權以原創方式二次分享&#xff0c;歡迎轉載、分享。WPF 簡單模仿 VSCode 界面布局作者&#xff1a;弈虎-呂女士會翻墻原文鏈接&#xff1a; https://github.com/Chen-Lin-Zhao-Wei/WPFLikeVSCode分享一篇群友這幾天自己寫的 WPF 簡…

compare()方法+使用compare方法

compare()方法 compare(lob1,lob2,amount,offset_1,offset_2) 1用于比較2個lob存儲的數據&#xff0c;比較的方式是從指定偏移量開始&#xff0c;對指定數量的字符或者字節進行比較。 2如果比較內容相同&#xff0c;返回0&#xff0c;否則返回-1或1. 3如果參數設置有誤或不合…

linux 下 mysql默認表_linux環境下mysql默認是區分表名大小寫的

在linux環境下&#xff0c;mysql默認表明是區分大小寫的&#xff0c;我們可以查看全局變量發現:mysql> show variables like lower%;-------------------------------| Variable_name | Value |-------------------------------| lower_case_file_system | OFF || lower_cas…

dot-- 資源匯總

http://graphviz.org/doc/info/attrs.htmlhttp://graphs.grevian.org/http://graphs.grevian.org/examplehttp://www.linuxdevcenter.com/pub/a/linux/2004/05/06/graphviz_dot.html轉載于:https://www.cnblogs.com/itzxy/p/7860276.html

兩將軍問題和TCP三次握手

兩將軍問題&#xff0c;又被稱為兩將軍悖論、兩軍問題&#xff0c; 是一個經典的計算機思想實驗。首先&#xff0c; 為避免混淆&#xff0c;我們需要認識到兩將軍問題雖然與拜占庭將軍問題相關&#xff0c;但兩者不是一個東西。拜占庭將軍問題是一個更通用的兩將軍問題版本&…

微信小程序開發系列五:微信小程序中如何響應用戶輸入事件

2019獨角獸企業重金招聘Python工程師標準>>> 微信小程序開發系列教程 微信小程序開發系列一&#xff1a;微信小程序的申請和開發環境的搭建 微信小程序開發系列二&#xff1a;微信小程序的視圖設計 微信小程序開發系列三&#xff1a;微信小程序的調試方法 微信小程序…