diff --git a/Lib.NotifyIcon/INotifyIcon.cs b/Lib.NotifyIcon/INotifyIcon.cs
new file mode 100644
index 0000000..37d41d5
--- /dev/null
+++ b/Lib.NotifyIcon/INotifyIcon.cs
@@ -0,0 +1,52 @@
+// Author: Gockner, Simon
+// Created: 2021-04-06
+// Copyright(c) 2021 SimonG. All Rights Reserved.
+
+using System;
+using Avalonia.Controls;
+
+namespace Lib.NotifyIcon
+{
+ public interface INotifyIcon
+ {
+ ///
+ /// Gets or sets the path for the notify icon
+ ///
+ public string IconPath { get; set; }
+
+ ///
+ /// Gets or sets the tooltip text for the notify icon
+ ///
+ public string ToolTipText { get; set; }
+
+ ///
+ /// Gets or sets the context menu for the notify icon
+ ///
+ public ContextMenu ContextMenu { get; set; }
+
+ ///
+ /// Gets or sets if the notify icon is visible in the taskbar notification area or not
+ ///
+ public bool Visible { get; set; }
+
+ ///
+ /// Removes the notify icon from the taskbar notification area
+ ///
+ public void Remove();
+
+ ///
+ /// This event is raised when a user clicks on the notification icon
+ ///
+ public event EventHandler Click;
+
+ ///
+ /// This event is raised when a user double clicks on the notification icon
+ ///
+ public event EventHandler DoubleClick;
+
+ ///
+ /// This event is raised when a user right clicks on the notification icon
+ ///
+ public event EventHandler RightClick;
+ }
+}
\ No newline at end of file
diff --git a/Lib.NotifyIcon/Lib.NotifyIcon.csproj b/Lib.NotifyIcon/Lib.NotifyIcon.csproj
new file mode 100644
index 0000000..ff433e1
--- /dev/null
+++ b/Lib.NotifyIcon/Lib.NotifyIcon.csproj
@@ -0,0 +1,17 @@
+
+
+
+ net5.0
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Lib.NotifyIcon/Windows/Native/NativeWindow.cs b/Lib.NotifyIcon/Windows/Native/NativeWindow.cs
new file mode 100644
index 0000000..1f6a962
--- /dev/null
+++ b/Lib.NotifyIcon/Windows/Native/NativeWindow.cs
@@ -0,0 +1,85 @@
+// Author: Gockner, Simon
+// Created: 2021-04-07
+// Copyright(c) 2021 SimonG. All Rights Reserved.
+
+using System;
+using System.ComponentModel;
+using System.Runtime.InteropServices;
+using Lib.NotifyIcon.Windows.Native.Types;
+
+namespace Lib.NotifyIcon.Windows.Native
+{
+ internal abstract class NativeWindow
+ {
+ private const uint WS_POPUP = 0x80000000;
+
+ // ReSharper disable once PrivateFieldCanBeConvertedToLocalVariable
+ private readonly WindowApi.WndProc _wndProc; //We need to store the window proc as a field so that it doesn't get garbage collected away
+
+ private readonly string _className = "NativeHelperWindow" + Guid.NewGuid();
+
+ ///
+ /// The window handle of the underlying native window
+ ///
+ public IntPtr Handle { get; }
+
+ ///
+ /// Creates a new native (Win32) helper window for receiving window messages
+ ///
+ protected NativeWindow()
+ {
+ _wndProc = WndProc;
+
+ WndClassEx wndClassEx = new()
+ {
+ cbSize = Marshal.SizeOf(),
+ lpfnWndProc = _wndProc,
+ hInstance = WindowApi.GetModuleHandle(null),
+ lpszClassName = _className
+ };
+
+ ushort atom = WindowApi.RegisterClassEx(ref wndClassEx);
+
+ if (atom == 0)
+ throw new Win32Exception();
+
+ Handle = WindowApi.CreateWindowEx(0, atom, null, WS_POPUP, 0, 0, 0, 0, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero);
+
+ if (Handle == IntPtr.Zero)
+ throw new Win32Exception();
+ }
+
+ ///
+ /// Destructs the object and destroys the native window
+ ///
+ ~NativeWindow()
+ {
+ if (Handle != IntPtr.Zero)
+ WindowApi.PostMessage(Handle, (uint) WindowsMessage.WmDestroy, IntPtr.Zero, IntPtr.Zero);
+ }
+
+ ///
+ /// This function will receive all the system window messages relevant to our window
+ ///
+ protected virtual IntPtr WndProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam)
+ {
+ switch (msg)
+ {
+ case (uint) WindowsMessage.WmClose:
+ {
+ WindowApi.DestroyWindow(hWnd);
+ break;
+ }
+ case (uint) WindowsMessage.WmDestroy:
+ {
+ WindowApi.PostQuitMessage(0);
+ break;
+ }
+ default:
+ return WindowApi.DefWindowProc(hWnd, msg, wParam, lParam);
+ }
+
+ return IntPtr.Zero;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Lib.NotifyIcon/Windows/Native/Types/CustomWindowsMessage.cs b/Lib.NotifyIcon/Windows/Native/Types/CustomWindowsMessage.cs
new file mode 100644
index 0000000..56230c8
--- /dev/null
+++ b/Lib.NotifyIcon/Windows/Native/Types/CustomWindowsMessage.cs
@@ -0,0 +1,15 @@
+// Author: Gockner, Simon
+// Created: 2021-04-06
+// Copyright(c) 2021 SimonG. All Rights Reserved.
+
+namespace Lib.NotifyIcon.Windows.Native.Types
+{
+ ///
+ /// Custom Win32 window messages for the NotifyIcon
+ ///
+ internal enum CustomWindowsMessage : uint
+ {
+ WmTrayIcon = WindowsMessage.WmApp + 1024,
+ WmTrayMouse = WindowsMessage.WmUser + 1024
+ }
+}
\ No newline at end of file
diff --git a/Lib.NotifyIcon/Windows/Native/Types/MenuFlags.cs b/Lib.NotifyIcon/Windows/Native/Types/MenuFlags.cs
new file mode 100644
index 0000000..624cdbd
--- /dev/null
+++ b/Lib.NotifyIcon/Windows/Native/Types/MenuFlags.cs
@@ -0,0 +1,17 @@
+// Author: Gockner, Simon
+// Created: 2021-04-06
+// Copyright(c) 2021 SimonG. All Rights Reserved.
+
+using System;
+
+namespace Lib.NotifyIcon.Windows.Native.Types
+{
+ [Flags]
+ internal enum MenuFlags : uint
+ {
+ MfString = 0,
+ MfByPosition = 0x400,
+ MfSeparator = 0x800,
+ MfRemove = 0x1000
+ }
+}
\ No newline at end of file
diff --git a/Lib.NotifyIcon/Windows/Native/Types/NIF.cs b/Lib.NotifyIcon/Windows/Native/Types/NIF.cs
new file mode 100644
index 0000000..3188eea
--- /dev/null
+++ b/Lib.NotifyIcon/Windows/Native/Types/NIF.cs
@@ -0,0 +1,22 @@
+// Author: Gockner, Simon
+// Created: 2021-04-06
+// Copyright(c) 2021 SimonG. All Rights Reserved.
+
+using System;
+
+namespace Lib.NotifyIcon.Windows.Native.Types
+{
+ // ReSharper disable once InconsistentNaming
+ [Flags]
+ internal enum NIF : uint
+ {
+ Message = 0x00000001,
+ Icon = 0x00000002,
+ Tip = 0x00000004,
+ State = 0x00000008,
+ Info = 0x00000010,
+ Guid = 0x00000020,
+ Realtime = 0x00000040,
+ ShowTip = 0x00000080
+ }
+}
\ No newline at end of file
diff --git a/Lib.NotifyIcon/Windows/Native/Types/NIIF.cs b/Lib.NotifyIcon/Windows/Native/Types/NIIF.cs
new file mode 100644
index 0000000..ece6eb5
--- /dev/null
+++ b/Lib.NotifyIcon/Windows/Native/Types/NIIF.cs
@@ -0,0 +1,23 @@
+// Author: Gockner, Simon
+// Created: 2021-04-06
+// Copyright(c) 2021 SimonG. All Rights Reserved.
+
+using System;
+
+namespace Lib.NotifyIcon.Windows.Native.Types
+{
+ // ReSharper disable once InconsistentNaming
+ [Flags]
+ internal enum NIIF : uint
+ {
+ None = 0x00000000,
+ Info = 0x00000001,
+ Warning = 0x00000002,
+ Error = 0x00000003,
+ User = 0x00000004,
+ IconMask = 0x0000000F,
+ NoSound = 0x00000010,
+ LargeIcon = 0x00000020,
+ RespectQuietTime = 0x00000080
+ }
+}
\ No newline at end of file
diff --git a/Lib.NotifyIcon/Windows/Native/Types/NIM.cs b/Lib.NotifyIcon/Windows/Native/Types/NIM.cs
new file mode 100644
index 0000000..9834577
--- /dev/null
+++ b/Lib.NotifyIcon/Windows/Native/Types/NIM.cs
@@ -0,0 +1,16 @@
+// Author: Gockner, Simon
+// Created: 2021-04-06
+// Copyright(c) 2021 SimonG. All Rights Reserved.
+
+namespace Lib.NotifyIcon.Windows.Native.Types
+{
+ // ReSharper disable once InconsistentNaming
+ internal enum NIM : uint
+ {
+ Add = 0x00000000,
+ Modify = 0x00000001,
+ Delete = 0x00000002,
+ SetFocus = 0x00000003,
+ SetVersion = 0x00000004
+ }
+}
\ No newline at end of file
diff --git a/Lib.NotifyIcon/Windows/Native/Types/NotifyIconData.cs b/Lib.NotifyIcon/Windows/Native/Types/NotifyIconData.cs
new file mode 100644
index 0000000..dcb5cf8
--- /dev/null
+++ b/Lib.NotifyIcon/Windows/Native/Types/NotifyIconData.cs
@@ -0,0 +1,36 @@
+// Author: Gockner, Simon
+// Created: 2021-04-06
+// Copyright(c) 2021 SimonG. All Rights Reserved.
+
+using System;
+using System.Runtime.InteropServices;
+
+namespace Lib.NotifyIcon.Windows.Native.Types
+{
+ [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
+ internal class NotifyIconData
+ {
+ public int cbSize = Marshal.SizeOf();
+ public IntPtr hWnd;
+ public int uID;
+ public NIF uFlags;
+ public int uCallbackMessage;
+ public IntPtr hIcon;
+
+ [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
+ public string szTip;
+
+ public int dwState = 0;
+ public int dwStateMask = 0;
+
+ [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
+ public string szInfo;
+
+ public int uTimeoutOrVersion;
+
+ [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)]
+ public string szInfoTitle;
+
+ public NIIF dwInfoFlags;
+ }
+}
\ No newline at end of file
diff --git a/Lib.NotifyIcon/Windows/Native/Types/Point.cs b/Lib.NotifyIcon/Windows/Native/Types/Point.cs
new file mode 100644
index 0000000..3d047e3
--- /dev/null
+++ b/Lib.NotifyIcon/Windows/Native/Types/Point.cs
@@ -0,0 +1,12 @@
+// Author: Gockner, Simon
+// Created: 2021-04-07
+// Copyright(c) 2021 SimonG. All Rights Reserved.
+
+namespace Lib.NotifyIcon.Windows.Native.Types
+{
+ internal struct Point
+ {
+ public int X;
+ public int Y;
+ }
+}
\ No newline at end of file
diff --git a/Lib.NotifyIcon/Windows/Native/Types/UFlags.cs b/Lib.NotifyIcon/Windows/Native/Types/UFlags.cs
new file mode 100644
index 0000000..7a50679
--- /dev/null
+++ b/Lib.NotifyIcon/Windows/Native/Types/UFlags.cs
@@ -0,0 +1,23 @@
+// Author: Gockner, Simon
+// Created: 2021-04-06
+// Copyright(c) 2021 SimonG. All Rights Reserved.
+
+using System;
+
+namespace Lib.NotifyIcon.Windows.Native.Types
+{
+ [Flags]
+ internal enum UFlags : uint
+ {
+ TpmLeftAlign = 0x0000,
+ TpmCenterAlign = 0x0004,
+ TpmRightAlign = 0x0008,
+ TpmTopAlign = 0x0000,
+ TpmVcenterAlign = 0x0010,
+ TpmBottomAlign = 0x0020,
+ TpmHorizontal = 0x0000,
+ TpmVertical = 0x0040,
+ TpmNoNotify = 0x0080,
+ TpmReturnCmd = 0x0100
+ }
+}
\ No newline at end of file
diff --git a/Lib.NotifyIcon/Windows/Native/Types/WindowsMessage.cs b/Lib.NotifyIcon/Windows/Native/Types/WindowsMessage.cs
new file mode 100644
index 0000000..9088cb8
--- /dev/null
+++ b/Lib.NotifyIcon/Windows/Native/Types/WindowsMessage.cs
@@ -0,0 +1,240 @@
+// Author: Gockner, Simon
+// Created: 2021-04-07
+// Copyright(c) 2021 SimonG. All Rights Reserved.
+
+namespace Lib.NotifyIcon.Windows.Native.Types
+{
+ internal enum WindowsMessage : uint
+ {
+ WmNull = 0x0000,
+ WmCreate = 0x0001,
+ WmDestroy = 0x0002,
+ WmMove = 0x0003,
+ WmSize = 0x0005,
+ WmActivate = 0x0006,
+ WmSetFocus = 0x0007,
+ WmKillFocus = 0x0008,
+ WmEnable = 0x000A,
+ WmSetRedraw = 0x000B,
+ WmSetText = 0x000C,
+ WmGetText = 0x000D,
+ WmGetTextLength = 0x000E,
+ WmPaint = 0x000F,
+ WmClose = 0x0010,
+ WmQueryEndSession = 0x0011,
+ WmQueryOpen = 0x0013,
+ WmEndSession = 0x0016,
+ WmQuit = 0x0012,
+ WmEraseBkgnd = 0x0014,
+ WmSysColorChange = 0x0015,
+ WmShowWindow = 0x0018,
+ WmWinIniChange = 0x001A,
+ WmSettingChange = WmWinIniChange,
+ WmDevModeChange = 0x001B,
+ WmActivateApp = 0x001C,
+ WmFontChange = 0x001D,
+ WmTimeChange = 0x001E,
+ WmCancelMode = 0x001F,
+ WmSetCursor = 0x0020,
+ WmMouseActivate = 0x0021,
+ WmChildActivate = 0x0022,
+ WmQueueSync = 0x0023,
+ WmGetMinMaxInfo = 0x0024,
+ WmPaintIcon = 0x0026,
+ WmIconEraseBkgnd = 0x0027,
+ WmNextDlgCtl = 0x0028,
+ WmSpoolerStatus = 0x002A,
+ WmDrawItem = 0x002B,
+ WmMeasureItem = 0x002C,
+ WmDeleteItem = 0x002D,
+ WmVKeyToItem = 0x002E,
+ WmCharToItem = 0x002F,
+ WmSetFont = 0x0030,
+ WmGetFont = 0x0031,
+ WmSetHotKey = 0x0032,
+ WmGetHotKey = 0x0033,
+ WmQueryDragIcon = 0x0037,
+ WmCompareItem = 0x0039,
+ WmGetObject = 0x003D,
+ WmCompacting = 0x0041,
+ WmWindowPosChanging = 0x0046,
+ WmWindowPosChanged = 0x0047,
+ WmCopyData = 0x004A,
+ WmCancelJournal = 0x004B,
+ WmNotify = 0x004E,
+ WmInputLangChangeRequest = 0x0050,
+ WmInputLangChange = 0x0051,
+ WmTCard = 0x0052,
+ WmHelp = 0x0053,
+ WmUserChanged = 0x0054,
+ WmNotifyFormat = 0x0055,
+ WmContextmenu = 0x007B,
+ WmStyleChanging = 0x007C,
+ WmStyleChanged = 0x007D,
+ WmDisplayChange = 0x007E,
+ WmGetIcon = 0x007F,
+ WmSetIcon = 0x0080,
+ WmNcCreate = 0x0081,
+ WmNcDestroy = 0x0082,
+ WmNcCalcSize = 0x0083,
+ WmNcHitTest = 0x0084,
+ WmNcPaint = 0x0085,
+ WmNcActivate = 0x0086,
+ WmGetDlgCode = 0x0087,
+ WmSyncPaint = 0x0088,
+ WmNcMousemove = 0x00A0,
+ WmNclButtonDown = 0x00A1,
+ WmNclButtonUp = 0x00A2,
+ WmNclButtonDblClk = 0x00A3,
+ WmNcrButtonDown = 0x00A4,
+ WmNcrButtonUp = 0x00A5,
+ WmNcrButtonDblClk = 0x00A6,
+ WmNcmButtonDown = 0x00A7,
+ WmNcmButtonUp = 0x00A8,
+ WmNcmButtonDblClk = 0x00A9,
+ WmNcxButtonDown = 0x00AB,
+ WmNcxButtonUp = 0x00AC,
+ WmNcxButtonDblClk = 0x00AD,
+ WmInputDeviceChange = 0x00FE,
+ WmInput = 0x00FF,
+ WmKeyFirst = 0x0100,
+ WmKeydown = 0x0100,
+ WmKeyup = 0x0101,
+ WmChar = 0x0102,
+ WmDeadChar = 0x0103,
+ WmSysKeyDown = 0x0104,
+ WmSysKeyUp = 0x0105,
+ WmSysChar = 0x0106,
+ WmSysDeadChar = 0x0107,
+ WmUniChar = 0x0109,
+ WmKeyLast = 0x0109,
+ WmImeStartComposition = 0x010D,
+ WmImeEndComposition = 0x010E,
+ WmImeComposition = 0x010F,
+ WmImeKeyLast = 0x010F,
+ WmInitDialog = 0x0110,
+ WmCommand = 0x0111,
+ WmSysCommand = 0x0112,
+ WmTimer = 0x0113,
+ WmHScroll = 0x0114,
+ WmVScroll = 0x0115,
+ WmInitMenu = 0x0116,
+ WmInitMenuPopup = 0x0117,
+ WmMenuselect = 0x011F,
+ WmMenuChar = 0x0120,
+ WmEnterIdle = 0x0121,
+ WmMenuRButtonUp = 0x0122,
+ WmMenuDrag = 0x0123,
+ WmMenuGetObject = 0x0124,
+ WmUnInitMenuPopup = 0x0125,
+ WmMenuCommand = 0x0126,
+ WmChangeUiState = 0x0127,
+ WmUpdateUiState = 0x0128,
+ WmQueryUiState = 0x0129,
+ WmCtlColorMsgBox = 0x0132,
+ WmCtlColorEdit = 0x0133,
+ WmCtlColorListBox = 0x0134,
+ WmCtlColorBtn = 0x0135,
+ WmCtlColorDlg = 0x0136,
+ WmCtlColorScrollBar = 0x0137,
+ WmCtlColorStatic = 0x0138,
+ WmMouseFirst = 0x0200,
+ WmMousemove = 0x0200,
+ WmLButtonDown = 0x0201,
+ WmLButtonUp = 0x0202,
+ WmLButtonDblClk = 0x0203,
+ WmRButtonDown = 0x0204,
+ WmRButtonUp = 0x0205,
+ WmRButtonDblClk = 0x0206,
+ WmMButtonDown = 0x0207,
+ WmMButtonUp = 0x0208,
+ WmMButtonDblClk = 0x0209,
+ WmMousewheel = 0x020A,
+ WmXButtonDown = 0x020B,
+ WmXButtonUp = 0x020C,
+ WmXButtonDblClk = 0x020D,
+ WmMouseHWheel = 0x020E,
+ WmMouseLast = 0x020E,
+ WmParentNotify = 0x0210,
+ WmEnterMenuLoop = 0x0211,
+ WmExitMenuLoop = 0x0212,
+ WmNextMenu = 0x0213,
+ WmSizing = 0x0214,
+ WmCaptureChanged = 0x0215,
+ WmMoving = 0x0216,
+ WmPowerBroadcast = 0x0218,
+ WmDevicechange = 0x0219,
+ WmMdiCreate = 0x0220,
+ WmMdiDestroy = 0x0221,
+ WmMdiActivate = 0x0222,
+ WmMdiRestore = 0x0223,
+ WmMdiNext = 0x0224,
+ WmMdiMaximize = 0x0225,
+ WmMdiTile = 0x0226,
+ WmMdiCascade = 0x0227,
+ WmMdiIconArrange = 0x0228,
+ WmMdiGetActive = 0x0229,
+ WmMdiSetMenu = 0x0230,
+ WmEnterSizeMove = 0x0231,
+ WmExitSizeMove = 0x0232,
+ WmDropFiles = 0x0233,
+ WmMdiRefreshMenu = 0x0234,
+ WmImeSetContext = 0x0281,
+ WmImeNotify = 0x0282,
+ WmImeControl = 0x0283,
+ WmImeCompositionFull = 0x0284,
+ WmImeSelect = 0x0285,
+ WmImeChar = 0x0286,
+ WmImeRequest = 0x0288,
+ WmImeKeydown = 0x0290,
+ WmImeKeyup = 0x0291,
+ WmMouseHover = 0x02A1,
+ WmMouseleave = 0x02A3,
+ WmNcMouseHover = 0x02A0,
+ WmNcMouseLeave = 0x02A2,
+ WmWtsSessionChange = 0x02B1,
+ WmTabletFirst = 0x02c0,
+ WmTabletLast = 0x02df,
+ WmDpiChanged = 0x02E0,
+ WmCut = 0x0300,
+ WmCopy = 0x0301,
+ WmPaste = 0x0302,
+ WmClear = 0x0303,
+ WmUndo = 0x0304,
+ WmRenderFormat = 0x0305,
+ WmRenderAllFormats = 0x0306,
+ WmDestroyClipboard = 0x0307,
+ WmDrawClipboard = 0x0308,
+ WmPaintClipboard = 0x0309,
+ WmVScrollClipboard = 0x030A,
+ WmSizeClipboard = 0x030B,
+ WmAskCbForMatName = 0x030C,
+ WmChangeCbChain = 0x030D,
+ WmHScrollClipboard = 0x030E,
+ WmQueryNewPalette = 0x030F,
+ WmPaletteIsChanging = 0x0310,
+ WmPaletteChanged = 0x0311,
+ WmHotkey = 0x0312,
+ WmPrint = 0x0317,
+ WmPrintClient = 0x0318,
+ WmAppCommand = 0x0319,
+ WmThemeChanged = 0x031A,
+ WmClipboardUpdate = 0x031D,
+ WmDwmCompositionChanged = 0x031E,
+ WmDwmNcRenderingChanged = 0x031F,
+ WmDwmColorizationColorChanged = 0x0320,
+ WmDwmWindowMaximizedChange = 0x0321,
+ WmGetTitleBarInfoEx = 0x033F,
+ WmHandheldFirst = 0x0358,
+ WmHandheldLast = 0x035F,
+ WmAfxFirst = 0x0360,
+ WmAfxLast = 0x037F,
+ WmPenWinFirst = 0x0380,
+ WmPenWinLast = 0x038F,
+ WmTouch = 0x0240,
+ WmApp = 0x8000,
+ WmUser = 0x0400,
+
+ WmDispatchWorkItem = WmUser
+ }
+}
\ No newline at end of file
diff --git a/Lib.NotifyIcon/Windows/Native/Types/WndClassEx.cs b/Lib.NotifyIcon/Windows/Native/Types/WndClassEx.cs
new file mode 100644
index 0000000..ec1ccce
--- /dev/null
+++ b/Lib.NotifyIcon/Windows/Native/Types/WndClassEx.cs
@@ -0,0 +1,26 @@
+// Author: Gockner, Simon
+// Created: 2021-04-07
+// Copyright(c) 2021 SimonG. All Rights Reserved.
+
+using System;
+using System.Runtime.InteropServices;
+
+namespace Lib.NotifyIcon.Windows.Native.Types
+{
+ [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
+ internal struct WndClassEx
+ {
+ public int cbSize;
+ public int style;
+ public WindowApi.WndProc lpfnWndProc;
+ public int cbClsExtra;
+ public int cbWndExtra;
+ public IntPtr hInstance;
+ public IntPtr hIcon;
+ public IntPtr hCursor;
+ public IntPtr hbrBackground;
+ public string lpszMenuName;
+ public string lpszClassName;
+ public IntPtr hIconSm;
+ }
+}
\ No newline at end of file
diff --git a/Lib.NotifyIcon/Windows/Native/WindowApi.cs b/Lib.NotifyIcon/Windows/Native/WindowApi.cs
new file mode 100644
index 0000000..b413161
--- /dev/null
+++ b/Lib.NotifyIcon/Windows/Native/WindowApi.cs
@@ -0,0 +1,68 @@
+// Author: Gockner, Simon
+// Created: 2021-04-06
+// Copyright(c) 2021 SimonG. All Rights Reserved.
+
+using System;
+using System.Runtime.InteropServices;
+using Lib.NotifyIcon.Windows.Native.Types;
+using Point = Lib.NotifyIcon.Windows.Native.Types.Point;
+
+namespace Lib.NotifyIcon.Windows.Native
+{
+ internal static class WindowApi
+ {
+ public delegate IntPtr WndProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam);
+
+ [DllImport("shell32", CharSet = CharSet.Auto)]
+ public static extern int Shell_NotifyIcon(NIM dwMessage, NotifyIconData lpData);
+
+ [DllImport("user32.dll")]
+ public static extern void PostQuitMessage(int nExitCode);
+
+ [DllImport("user32.dll", ExactSpelling = true)]
+ public static extern bool SetForegroundWindow(IntPtr hWnd);
+
+ [DllImport("user32.dll")]
+ public static extern IntPtr CreatePopupMenu();
+
+ [DllImport("user32.dll", CharSet = CharSet.Auto)]
+ public static extern bool AppendMenu(IntPtr hMenu, MenuFlags uFlags, uint uIdNewItem, string lpNewItem);
+
+ [DllImport("user32.dll")]
+ public static extern uint TrackPopupMenuEx(IntPtr hMenu, UFlags uFlags, int x, int y, IntPtr hWnd, IntPtr lpTpm);
+
+ [DllImport("user32.dll", SetLastError = true)]
+ public static extern IntPtr CreateWindowEx(
+ int dwExStyle,
+ uint lpClassName,
+ string lpWindowName,
+ uint dwStyle,
+ int x,
+ int y,
+ int nWidth,
+ int nHeight,
+ IntPtr hWndParent,
+ IntPtr hMenu,
+ IntPtr hInstance,
+ IntPtr lpParam);
+
+ [DllImport("user32.dll", EntryPoint = "DefWindowProcW")]
+ public static extern IntPtr DefWindowProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam);
+
+ [DllImport("user32.dll", SetLastError = true)]
+ public static extern bool DestroyWindow(IntPtr hWnd);
+
+ [DllImport("user32.dll")]
+ public static extern bool GetCursorPos(out Point lpPoint);
+
+ [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode, EntryPoint = "RegisterClassExW")]
+ public static extern ushort RegisterClassEx(ref WndClassEx lpWcx);
+
+ [return: MarshalAs(UnmanagedType.Bool)]
+ [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode, EntryPoint = "PostMessageW")]
+ public static extern bool PostMessage(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam);
+
+ [DllImport("kernel32.dll")]
+ public static extern IntPtr GetModuleHandle(string lpModuleName);
+ }
+}
\ No newline at end of file
diff --git a/Lib.NotifyIcon/Windows/NotifyIcon.cs b/Lib.NotifyIcon/Windows/NotifyIcon.cs
new file mode 100644
index 0000000..26bdb05
--- /dev/null
+++ b/Lib.NotifyIcon/Windows/NotifyIcon.cs
@@ -0,0 +1,224 @@
+// Author: Gockner, Simon
+// Created: 2021-04-06
+// Copyright(c) 2021 SimonG. All Rights Reserved.
+
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Platform;
+using Lib.NotifyIcon.Windows.Native;
+using Lib.NotifyIcon.Windows.Native.Types;
+using Point = Lib.NotifyIcon.Windows.Native.Types.Point;
+
+namespace Lib.NotifyIcon.Windows
+{
+ public class NotifyIcon : INotifyIcon
+ {
+ private static int _nextUId;
+
+ private readonly NotifyIconHelperWindow _helperWindow;
+ private readonly int _uId;
+
+ private bool _doubleClick;
+ private bool _iconAdded;
+ private Icon _icon;
+
+ private string _iconPath = "";
+ private string _toolTipText = "";
+ private bool _visible;
+
+ ///
+ /// Creates a new instance and sets up some required resources
+ ///
+ public NotifyIcon()
+ {
+ _uId = ++_nextUId;
+ _helperWindow = new NotifyIconHelperWindow(this);
+ }
+
+ ~NotifyIcon() => UpdateIcon(remove: true);
+
+ ///
+ /// Gets or sets the path for the notify icon
+ ///
+ public string IconPath
+ {
+ get => _iconPath;
+ set
+ {
+ _iconPath = value;
+
+ IAssetLoader assetLoader = AvaloniaLocator.Current.GetService();
+ _icon = new Icon(assetLoader.Open(new Uri(_iconPath)));
+
+ UpdateIcon();
+ }
+ }
+
+ ///
+ /// Gets or sets the tooltip text for the notify icon
+ ///
+ public string ToolTipText
+ {
+ get => _toolTipText;
+ set
+ {
+ _toolTipText = value;
+ UpdateIcon();
+ }
+ }
+
+ ///
+ /// Gets or sets the context menu for the notify icon
+ ///
+ public ContextMenu ContextMenu { get; set; }
+
+ ///
+ /// Gets or sets if the notify icon is visible in the taskbar notification area or not
+ ///
+ public bool Visible
+ {
+ get => _visible;
+ set
+ {
+ _visible = value;
+ UpdateIcon();
+ }
+ }
+
+ ///
+ /// Removes the notify icon from the taskbar notification area
+ ///
+ public void Remove() => UpdateIcon(true);
+
+ ///
+ /// Handles the NotifyIcon-specific window messages sent by the notification icon
+ ///
+ public void WndProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam)
+ {
+ //We only care about tray icon messages
+ if (msg != (uint) CustomWindowsMessage.WmTrayMouse)
+ return;
+
+ //Determine the type of message and call the matching event handlers
+ switch (lParam.ToInt32())
+ {
+ case (int) WindowsMessage.WmLButtonUp:
+ {
+ if (!_doubleClick)
+ Click?.Invoke(this, new EventArgs());
+
+ _doubleClick = false;
+ break;
+ }
+ case (int) WindowsMessage.WmLButtonDblClk:
+ {
+ DoubleClick?.Invoke(this, new EventArgs());
+ _doubleClick = true;
+ break;
+ }
+ case (int) WindowsMessage.WmRButtonUp:
+ {
+ EventArgs e = new EventArgs();
+ RightClick?.Invoke(this, e);
+ ShowContextMenu();
+ break;
+ }
+ }
+ }
+
+ ///
+ /// Shows, hides or removes the notify icon based on the set properties and parameters
+ ///
+ /// If set to true, the notify icon will be removed
+ private void UpdateIcon(bool remove = false)
+ {
+ NotifyIconData iconData = new()
+ {
+ hWnd = _helperWindow.Handle,
+ uID = _uId,
+ uFlags = NIF.Tip | NIF.Message,
+ uCallbackMessage = (int) CustomWindowsMessage.WmTrayMouse,
+ hIcon = IntPtr.Zero,
+ szTip = ToolTipText
+ };
+
+ if (!remove && _icon != null && Visible)
+ {
+ iconData.uFlags |= NIF.Icon;
+ iconData.hIcon = _icon.Handle;
+
+ if (!_iconAdded)
+ {
+ WindowApi.Shell_NotifyIcon(NIM.Add, iconData);
+ _iconAdded = true;
+ }
+ else
+ WindowApi.Shell_NotifyIcon(NIM.Modify, iconData);
+ }
+ else
+ {
+ WindowApi.Shell_NotifyIcon(NIM.Delete, iconData);
+ _iconAdded = false;
+ }
+ }
+
+ ///
+ /// If available, displays the notification icon's context menu
+ ///
+ private void ShowContextMenu()
+ {
+ if (ContextMenu == null)
+ return;
+
+ // Since we can't use the Avalonia ContextMenu directly due to shortcomings
+ // regrading its positioning, we'll create a native context menu instead.
+ // This dictionary will map the menu item IDs which we'll need for the native
+ // menu to the MenuItems of the provided Avalonia ContextMenu.
+ Dictionary contextItemLookup = new();
+
+ // Create a native (Win32) popup menu as the notify icon's context menu.
+ IntPtr popupMenu = WindowApi.CreatePopupMenu();
+
+ uint i = 1;
+ foreach (var item in ContextMenu.Items)
+ {
+ MenuItem menuItem = (MenuItem)item;
+
+ // Add items to the native context menu by simply reusing
+ // the information provided within the Avalonia ContextMenu.
+ WindowApi.AppendMenu(popupMenu, MenuFlags.MfString, i, (string) menuItem.Header);
+
+ // Add the mapping so that we can find the selected item later
+ contextItemLookup.Add(i, menuItem);
+ i++;
+ }
+
+ // To display a context menu for a notification icon, the current window
+ // must be the foreground window before the application calls TrackPopupMenu
+ // or TrackPopupMenuEx. Otherwise, the menu will not disappear when the user
+ // clicks outside of the menu or the window that created the menu (if it is
+ // visible). If the current window is a child window, you must set the
+ // (top-level) parent window as the foreground window.
+ WindowApi.SetForegroundWindow(_helperWindow.Handle);
+
+ // Get the mouse cursor position
+ WindowApi.GetCursorPos(out Point pt);
+
+ // Now display the context menu and block until we get a result
+ uint commandId = WindowApi.TrackPopupMenuEx(popupMenu,
+ UFlags.TpmBottomAlign | UFlags.TpmRightAlign | UFlags.TpmNoNotify | UFlags.TpmReturnCmd,
+ pt.X, pt.Y, _helperWindow.Handle, IntPtr.Zero);
+
+ // If we have a result, execute the corresponding command
+ if (commandId != 0)
+ contextItemLookup[commandId].Command?.Execute(null);
+ }
+
+ public event EventHandler Click;
+ public event EventHandler DoubleClick;
+ public event EventHandler RightClick;
+ }
+}
\ No newline at end of file
diff --git a/Lib.NotifyIcon/Windows/NotifyIconHelperWindow.cs b/Lib.NotifyIcon/Windows/NotifyIconHelperWindow.cs
new file mode 100644
index 0000000..3887fd7
--- /dev/null
+++ b/Lib.NotifyIcon/Windows/NotifyIconHelperWindow.cs
@@ -0,0 +1,40 @@
+// Author: Gockner, Simon
+// Created: 2021-04-07
+// Copyright(c) 2021 SimonG. All Rights Reserved.
+
+using System;
+using Lib.NotifyIcon.Windows.Native;
+using Lib.NotifyIcon.Windows.Native.Types;
+
+namespace Lib.NotifyIcon.Windows
+{
+ ///
+ /// A native Win32 helper window encapsulation for dealing with the window messages sent by the notification icon
+ ///
+ internal class NotifyIconHelperWindow : NativeWindow
+ {
+ private readonly NotifyIcon _notifyIcon;
+
+ public NotifyIconHelperWindow(NotifyIcon notifyIcon) => _notifyIcon = notifyIcon;
+
+ ///
+ /// This function will receive all the system window messages relevant to our window.
+ ///
+ protected override IntPtr WndProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam)
+ {
+ switch (msg)
+ {
+ case (uint) CustomWindowsMessage.WmTrayMouse:
+ {
+ // Forward WM_TRAYMOUSE messages to the tray icon's window procedure
+ _notifyIcon.WndProc(hWnd, msg, wParam, lParam);
+ break;
+ }
+ default:
+ return base.WndProc(hWnd, msg, wParam, lParam);
+ }
+
+ return IntPtr.Zero;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Mystify.sln b/Mystify.sln
index 41c639a..8a9b29e 100644
--- a/Mystify.sln
+++ b/Mystify.sln
@@ -2,6 +2,8 @@
Microsoft Visual Studio Solution File, Format Version 12.00
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mystify", "Mystify\Mystify.csproj", "{87373A7B-B012-4E47-A875-0F189DE731E7}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lib.NotifyIcon", "Lib.NotifyIcon\Lib.NotifyIcon.csproj", "{47CF9922-FBF5-4AA0-9A4C-0A9405A62E7E}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -12,5 +14,9 @@ Global
{87373A7B-B012-4E47-A875-0F189DE731E7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{87373A7B-B012-4E47-A875-0F189DE731E7}.Release|Any CPU.ActiveCfg = Release|Any CPU
{87373A7B-B012-4E47-A875-0F189DE731E7}.Release|Any CPU.Build.0 = Release|Any CPU
+ {47CF9922-FBF5-4AA0-9A4C-0A9405A62E7E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {47CF9922-FBF5-4AA0-9A4C-0A9405A62E7E}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {47CF9922-FBF5-4AA0-9A4C-0A9405A62E7E}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {47CF9922-FBF5-4AA0-9A4C-0A9405A62E7E}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal