From 94085b72938cf4cdcc32d2e267c1b169feb247ee Mon Sep 17 00:00:00 2001 From: Philipp Sumi Date: Mon, 30 Mar 2009 22:01:35 +0000 Subject: [PATCH] WPF NotifyIcon -------------- Initial import git-svn-id: https://svn.evolvesoftware.ch/repos/evolve.net/WPF/NotifyIcon@52 9f600761-6f11-4665-b6dc-0185e9171623 --- Source/NotifyIconWpf.sln | 38 + Source/NotifyIconWpf.sln.cache | 236 +++++ Source/NotifyIconWpf/BalloonIcon.cs | 30 + Source/NotifyIconWpf/DefaultTrayIcon.ico | Bin 0 -> 1150 bytes Source/NotifyIconWpf/Interop/BalloonFlags.cs | 55 ++ .../NotifyIconWpf/Interop/IconDataMembers.cs | 67 ++ Source/NotifyIconWpf/Interop/IconState.cs | 23 + Source/NotifyIconWpf/Interop/MouseEvent.cs | 46 + Source/NotifyIconWpf/Interop/NotifyCommand.cs | 39 + .../NotifyIconWpf/Interop/NotifyIconData.cs | 153 +++ .../Interop/NotifyIconVersion.cs | 28 + Source/NotifyIconWpf/Interop/TrayLocator.cs | 95 ++ Source/NotifyIconWpf/Interop/WinApi.cs | 80 ++ Source/NotifyIconWpf/Interop/WindowClass.cs | 31 + .../Interop/WindowMessageSink.Handle.cs | 102 ++ .../Interop/WindowMessageSink.cs | 255 +++++ Source/NotifyIconWpf/NotifyIconWpf.csproj | 108 +++ Source/NotifyIconWpf/PopupActivationMode.cs | 45 + .../NotifyIconWpf/Properties/AssemblyInfo.cs | 59 ++ .../Properties/Resources.Designer.cs | 70 ++ .../NotifyIconWpf/Properties/Resources.resx | 124 +++ .../Properties/Settings.Designer.cs | 26 + .../Properties/Settings.settings | 7 + Source/NotifyIconWpf/RoutedEventHelper.cs | 81 ++ .../NotifyIconWpf/TaskbarIcon.Declarations.cs | 868 ++++++++++++++++++ Source/NotifyIconWpf/TaskbarIcon.Interop.cs | 250 +++++ Source/NotifyIconWpf/TaskbarIcon.cs | 379 ++++++++ Source/NotifyIconWpf/Util.cs | 225 +++++ Source/Sample Project/App.xaml | 8 + Source/Sample Project/App.xaml.cs | 16 + Source/Sample Project/Icons/Bulb.ico | Bin 0 -> 1150 bytes Source/Sample Project/Icons/Computers.ico | Bin 0 -> 1150 bytes Source/Sample Project/Icons/NetDrives.ico | Bin 0 -> 42135 bytes .../Sample Project/Properties/AssemblyInfo.cs | 55 ++ .../Properties/Resources.Designer.cs | 84 ++ .../Sample Project/Properties/Resources.resx | 130 +++ .../Properties/Settings.Designer.cs | 30 + .../Properties/Settings.settings | 7 + Source/Sample Project/Sample Project.csproj | 121 +++ Source/Sample Project/Window1.xaml | 147 +++ Source/Sample Project/Window1.xaml.cs | 43 + 41 files changed, 4161 insertions(+) create mode 100644 Source/NotifyIconWpf.sln create mode 100644 Source/NotifyIconWpf.sln.cache create mode 100644 Source/NotifyIconWpf/BalloonIcon.cs create mode 100644 Source/NotifyIconWpf/DefaultTrayIcon.ico create mode 100644 Source/NotifyIconWpf/Interop/BalloonFlags.cs create mode 100644 Source/NotifyIconWpf/Interop/IconDataMembers.cs create mode 100644 Source/NotifyIconWpf/Interop/IconState.cs create mode 100644 Source/NotifyIconWpf/Interop/MouseEvent.cs create mode 100644 Source/NotifyIconWpf/Interop/NotifyCommand.cs create mode 100644 Source/NotifyIconWpf/Interop/NotifyIconData.cs create mode 100644 Source/NotifyIconWpf/Interop/NotifyIconVersion.cs create mode 100644 Source/NotifyIconWpf/Interop/TrayLocator.cs create mode 100644 Source/NotifyIconWpf/Interop/WinApi.cs create mode 100644 Source/NotifyIconWpf/Interop/WindowClass.cs create mode 100644 Source/NotifyIconWpf/Interop/WindowMessageSink.Handle.cs create mode 100644 Source/NotifyIconWpf/Interop/WindowMessageSink.cs create mode 100644 Source/NotifyIconWpf/NotifyIconWpf.csproj create mode 100644 Source/NotifyIconWpf/PopupActivationMode.cs create mode 100644 Source/NotifyIconWpf/Properties/AssemblyInfo.cs create mode 100644 Source/NotifyIconWpf/Properties/Resources.Designer.cs create mode 100644 Source/NotifyIconWpf/Properties/Resources.resx create mode 100644 Source/NotifyIconWpf/Properties/Settings.Designer.cs create mode 100644 Source/NotifyIconWpf/Properties/Settings.settings create mode 100644 Source/NotifyIconWpf/RoutedEventHelper.cs create mode 100644 Source/NotifyIconWpf/TaskbarIcon.Declarations.cs create mode 100644 Source/NotifyIconWpf/TaskbarIcon.Interop.cs create mode 100644 Source/NotifyIconWpf/TaskbarIcon.cs create mode 100644 Source/NotifyIconWpf/Util.cs create mode 100644 Source/Sample Project/App.xaml create mode 100644 Source/Sample Project/App.xaml.cs create mode 100644 Source/Sample Project/Icons/Bulb.ico create mode 100644 Source/Sample Project/Icons/Computers.ico create mode 100644 Source/Sample Project/Icons/NetDrives.ico create mode 100644 Source/Sample Project/Properties/AssemblyInfo.cs create mode 100644 Source/Sample Project/Properties/Resources.Designer.cs create mode 100644 Source/Sample Project/Properties/Resources.resx create mode 100644 Source/Sample Project/Properties/Settings.Designer.cs create mode 100644 Source/Sample Project/Properties/Settings.settings create mode 100644 Source/Sample Project/Sample Project.csproj create mode 100644 Source/Sample Project/Window1.xaml create mode 100644 Source/Sample Project/Window1.xaml.cs diff --git a/Source/NotifyIconWpf.sln b/Source/NotifyIconWpf.sln new file mode 100644 index 0000000..f4acf09 --- /dev/null +++ b/Source/NotifyIconWpf.sln @@ -0,0 +1,38 @@ + +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual Studio 2008 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NotifyIconWpf", "NotifyIconWpf\NotifyIconWpf.csproj", "{7AC63864-7638-41C4-969C-D3197EF2BED9}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConsoleApplication2", "ConsoleApplication2\ConsoleApplication2.csproj", "{A92D287A-6DD2-4D75-9CE5-64BFB990E2D8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConsoleApplication1", "ConsoleApplication1\ConsoleApplication1.csproj", "{10BB9EF0-20F9-460E-96A3-284FF0D1C2E6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sample Project", "Sample Project\Sample Project.csproj", "{71C74F29-F1C2-49C5-969F-C25AC4CDFCCC}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {7AC63864-7638-41C4-969C-D3197EF2BED9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7AC63864-7638-41C4-969C-D3197EF2BED9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7AC63864-7638-41C4-969C-D3197EF2BED9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7AC63864-7638-41C4-969C-D3197EF2BED9}.Release|Any CPU.Build.0 = Release|Any CPU + {A92D287A-6DD2-4D75-9CE5-64BFB990E2D8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A92D287A-6DD2-4D75-9CE5-64BFB990E2D8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A92D287A-6DD2-4D75-9CE5-64BFB990E2D8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A92D287A-6DD2-4D75-9CE5-64BFB990E2D8}.Release|Any CPU.Build.0 = Release|Any CPU + {10BB9EF0-20F9-460E-96A3-284FF0D1C2E6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {10BB9EF0-20F9-460E-96A3-284FF0D1C2E6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {10BB9EF0-20F9-460E-96A3-284FF0D1C2E6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {10BB9EF0-20F9-460E-96A3-284FF0D1C2E6}.Release|Any CPU.Build.0 = Release|Any CPU + {71C74F29-F1C2-49C5-969F-C25AC4CDFCCC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {71C74F29-F1C2-49C5-969F-C25AC4CDFCCC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {71C74F29-F1C2-49C5-969F-C25AC4CDFCCC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {71C74F29-F1C2-49C5-969F-C25AC4CDFCCC}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/Source/NotifyIconWpf.sln.cache b/Source/NotifyIconWpf.sln.cache new file mode 100644 index 0000000..e57874d --- /dev/null +++ b/Source/NotifyIconWpf.sln.cache @@ -0,0 +1,236 @@ + + + <_SolutionProjectConfiguration>Debug|Any CPU + <_SolutionProjectToolsVersion>3.5 + <_SolutionProjectCacheVersion>3.5 + + + <_SolutionProjectProjects Include="ConsoleApplication1\ConsoleApplication1.csproj" /> + <_SolutionProjectProjects Include="ConsoleApplication2\ConsoleApplication2.csproj" /> + <_SolutionProjectProjects Include="NotifyIconWpf\NotifyIconWpf.csproj" /> + <_SolutionProjectProjects Include="Sample Project\Sample Project.csproj" /> + + + + Debug + AnyCPU + + + Debug + AnyCPU + + + Debug + AnyCPU + + + Debug + AnyCPU + + + + + Release + AnyCPU + + + Release + AnyCPU + + + Release + AnyCPU + + + Release + AnyCPU + + + + + + Debug + + + Any CPU + + + $(Configuration) + + + D:\Philipp\Repositories\evolve.net\WPF\NotifyIcon\Source\ + .sln + NotifyIconWpf.sln + NotifyIconWpf + D:\Philipp\Repositories\evolve.net\WPF\NotifyIcon\Source\NotifyIconWpf.sln + + + v2.0 + v3.5 + + + + + Debug|AnyCPU + Debug|AnyCPU + Debug|AnyCPU + Debug|AnyCPU + + + + + + + Release|AnyCPU + Release|AnyCPU + Release|AnyCPU + Release|AnyCPU + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Source/NotifyIconWpf/BalloonIcon.cs b/Source/NotifyIconWpf/BalloonIcon.cs new file mode 100644 index 0000000..018aae9 --- /dev/null +++ b/Source/NotifyIconWpf/BalloonIcon.cs @@ -0,0 +1,30 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Hardcodet.Wpf.TaskbarNotification +{ + /// + /// Supported icons for the tray's ballon messages. + /// + public enum BalloonIcon + { + /// + /// The balloon message is displayed without an icon. + /// + None, + /// + /// An information is displayed. + /// + Info, + /// + /// A warning is displayed. + /// + Warning, + /// + /// An error is displayed. + /// + Error + } +} diff --git a/Source/NotifyIconWpf/DefaultTrayIcon.ico b/Source/NotifyIconWpf/DefaultTrayIcon.ico new file mode 100644 index 0000000000000000000000000000000000000000..e3ff3c4bf60174f7cc6140efc302f7098ae911fc GIT binary patch literal 1150 zcmZQzU<5(|0R|wcz>vYhz#zuJz@P!dKp~(AL>x$w1a$TERC983*Voq8Zn3qs)g?tU zCpWjSlZ)G?#N^cfwRLs>laiAU35$rx5o-pYfPk)xhv)0k%Ig0cHg5Xg)7$q!R7}j2 zSpDJ>68b^GA#ZbX^8PnBH~*hJY0@V(H8o3O^;;S1>%>QePs>V8Sec)dvZ^pUW4fc2 znK7~Y&jq(p@p5TifL-HtaeCEjKBj2Twl4W)iS958p* z46CxdEXNo>cYZ=<#0UErw>LGotX#I#a^duT&$WwZ2XEiJ(QVq~NluyZk%rz@hK#t( z2z9p?%TG)6S+aPs?T&3*y|!&!nYefRy4>SO4khi_zRk0$q|iAw$XgVb{wP0p^M=}L zk2R}TyB<5VzvTGgy$weX?y5U^^l<5cy}OgQZ{3p6*WO}}O}_#s17l8docFGsJ2K|Z zo$I<~!@7(;+cq_9-?YAH|BkJ#ySHrUx^?~9!i}p}W&`zO^+Rn*e!#i2XS%0Mnc_Wt z>eRrEtCqFyShsTV)-}slZd2l!QxvW<-EUOKI-h>D{fXmd>5oym{@a#*ORO zWp7wAf9;~lJ$IXmGPflMdCKC_4-(Ib4$`g5PoH;i*Y-1y9y~bt^vRP0XO11d(o~$Y zI@;5os63G1=PEI0Qcv=sJv*lzJGgJg%7t^%!rbj8vDy2_evRV~c})C)e~se@_BD + /// Flags that define the icon that is shown on a balloon + /// tooltip. + /// + public enum BalloonFlags + { + /// + /// No icon is displayed. + /// + None = 0x00, + /// + /// An information icon is displayed. + /// + Info = 0x01, + /// + /// A warning icon is displayed. + /// + Warning = 0x02, + /// + /// An error icon is displayed. + /// + Error = 0x03, + /// + /// Windows XP Service Pack 2 (SP2) and later. + /// Use a custom icon as the title icon. + /// + User = 0x04, + /// + /// Windows XP (Shell32.dll version 6.0) and later. + /// Do not play the associated sound. Applies only to balloon ToolTips. + /// + NoSound = 0x10, + /// + /// Windows Vista (Shell32.dll version 6.0.6) and later. The large version + /// of the icon should be used as the balloon icon. This corresponds to the + /// icon with dimensions SM_CXICON x SM_CYICON. If this flag is not set, + /// the icon with dimensions XM_CXSMICON x SM_CYSMICON is used.
+ /// - This flag can be used with all stock icons.
+ /// - Applications that use older customized icons (NIIF_USER with hIcon) must + /// provide a new SM_CXICON x SM_CYICON version in the tray icon (hIcon). These + /// icons are scaled down when they are displayed in the System Tray or + /// System Control Area (SCA).
+ /// - New customized icons (NIIF_USER with hBalloonIcon) must supply an + /// SM_CXICON x SM_CYICON version in the supplied icon (hBalloonIcon). + ///
+ LargeIcon = 0x20, + /// + /// Windows 7 and later. + /// + RespectQuietTime = 0x80 + + } +} \ No newline at end of file diff --git a/Source/NotifyIconWpf/Interop/IconDataMembers.cs b/Source/NotifyIconWpf/Interop/IconDataMembers.cs new file mode 100644 index 0000000..d3dd8db --- /dev/null +++ b/Source/NotifyIconWpf/Interop/IconDataMembers.cs @@ -0,0 +1,67 @@ +using System; + +namespace Hardcodet.Wpf.TaskbarNotification.Interop +{ + /// + /// Indicates which members of a structure + /// were set, and thus contain valid data or provide additional information + /// to the ToolTip as to how it should display. + /// + [Flags] + public enum IconDataMembers + { + /// + /// The message ID is set. + /// + Message = 0x01, + /// + /// The notification icon is set. + /// + Icon = 0x02, + /// + /// The tooltip is set. + /// + Tip = 0x04, + /// + /// State information () is set. This + /// applies to both and + /// . + /// + State = 0x08, + /// + /// The ballon ToolTip is set. Accordingly, the following + /// members are set: , + /// , , + /// and . + /// + Info = 0x10, + + /// + /// Internal identifier is set. Reserved, thus commented out. + /// + //Guid = 0x20, + + /// + /// Windows Vista (Shell32.dll version 6.0.6) and later. If the ToolTip + /// cannot be displayed immediately, discard it.
+ /// Use this flag for ToolTips that represent real-time information which + /// would be meaningless or misleading if displayed at a later time. + /// For example, a message that states "Your telephone is ringing."
+ /// This modifies and must be combined with the flag. + ///
+ Realtime = 0x40, + /// + /// Windows Vista (Shell32.dll version 6.0.6) and later. + /// Use the standard ToolTip. Normally, when uVersion is set + /// to NOTIFYICON_VERSION_4, the standard ToolTip is replaced + /// by the application-drawn pop-up user interface (UI). + /// If the application wants to show the standard tooltip + /// in that case, regardless of whether the on-hover UI is showing, + /// it can specify NIF_SHOWTIP to indicate the standard tooltip + /// should still be shown.
+ /// Note that the NIF_SHOWTIP flag is effective until the next call + /// to Shell_NotifyIcon. + ///
+ UseLegacyToolTips = 0x80 + } +} \ No newline at end of file diff --git a/Source/NotifyIconWpf/Interop/IconState.cs b/Source/NotifyIconWpf/Interop/IconState.cs new file mode 100644 index 0000000..44a9e7f --- /dev/null +++ b/Source/NotifyIconWpf/Interop/IconState.cs @@ -0,0 +1,23 @@ +namespace Hardcodet.Wpf.TaskbarNotification.Interop +{ + /// + /// The state of the icon - can be set to + /// hide the icon. + /// + public enum IconState + { + /// + /// The icon is visible. + /// + Visible = 0x00, + /// + /// Hide the icon. + /// + Hidden = 0x01, + + /// + /// The icon is shared - currently not supported, thus commented out. + /// + //Shared = 0x02 + } +} \ No newline at end of file diff --git a/Source/NotifyIconWpf/Interop/MouseEvent.cs b/Source/NotifyIconWpf/Interop/MouseEvent.cs new file mode 100644 index 0000000..c304289 --- /dev/null +++ b/Source/NotifyIconWpf/Interop/MouseEvent.cs @@ -0,0 +1,46 @@ +namespace Hardcodet.Wpf.TaskbarNotification.Interop +{ + /// + /// Event flags for clicked events. + /// + public enum MouseEvent + { + /// + /// The mouse was moved withing the + /// taskbar icon's area. + /// + MouseMove, + /// + /// The right mouse button was clicked. + /// + IconRightMouseDown, + /// + /// The left mouse button was clicked. + /// + IconLeftMouseDown, + /// + /// The right mouse button was released. + /// + IconRightMouseUp, + /// + /// The left mouse button was released. + /// + IconLeftMouseUp, + /// + /// The middle mouse button was clicked. + /// + IconMiddleMouseDown, + /// + /// The middle mouse button was released. + /// + IconMiddleMouseUp, + /// + /// The taskbar icon was double clicked. + /// + IconDoubleClick, + /// + /// The balloon tip was clicked. + /// + BalloonToolTipClicked + } +} diff --git a/Source/NotifyIconWpf/Interop/NotifyCommand.cs b/Source/NotifyIconWpf/Interop/NotifyCommand.cs new file mode 100644 index 0000000..7c3abde --- /dev/null +++ b/Source/NotifyIconWpf/Interop/NotifyCommand.cs @@ -0,0 +1,39 @@ +using System; + +namespace Hardcodet.Wpf.TaskbarNotification.Interop +{ + /// + /// Main operations performed on the + /// function. + /// + public enum NotifyCommand + { + /// + /// The taskbar icon is being created. + /// + Add = 0x00, + /// + /// The settings of the taskbar icon are being updated. + /// + Modify = 0x01, + /// + /// The taskbar icon is deleted. + /// + Delete = 0x02, + /// + /// Focus is returned to the taskbar icon. Currently not in use. + /// + SetFocus = 0x03, + /// + /// Shell32.dll version 5.0 and later only. Instructs the taskbar + /// to behave according to the version number specified in the + /// uVersion member of the structure pointed to by lpdata. + /// This message allows you to specify whether you want the version + /// 5.0 behavior found on Microsoft Windows 2000 systems, or the + /// behavior found on earlier Shell versions. The default value for + /// uVersion is zero, indicating that the original Windows 95 notify + /// icon behavior should be used. + /// + SetVersion = 0x04 + } +} \ No newline at end of file diff --git a/Source/NotifyIconWpf/Interop/NotifyIconData.cs b/Source/NotifyIconWpf/Interop/NotifyIconData.cs new file mode 100644 index 0000000..9f6f396 --- /dev/null +++ b/Source/NotifyIconWpf/Interop/NotifyIconData.cs @@ -0,0 +1,153 @@ +using System; +using System.Drawing; +using System.Runtime.InteropServices; + +namespace Hardcodet.Wpf.TaskbarNotification.Interop +{ + /// + /// A struct that is submitted in order to configure + /// the taskbar icon. Provides various members that + /// can be configured partially, according to the + /// values of the + /// that were defined. + /// + [StructLayout(LayoutKind.Sequential)] + public struct NotifyIconData + { + /// + /// Size of this structure, in bytes. + /// + public uint cbSize; + + /// + /// Handle to the window that receives notification messages associated with an icon in the + /// taskbar status area. The Shell uses hWnd and uID to identify which icon to operate on + /// when Shell_NotifyIcon is invoked. + /// + public IntPtr WindowHandle; + + /// + /// Application-defined identifier of the taskbar icon. The Shell uses hWnd and uID to identify + /// which icon to operate on when Shell_NotifyIcon is invoked. You can have multiple icons + /// associated with a single hWnd by assigning each a different uID. This feature, however + /// is currently not used. + /// + public uint TaskbarIconId; + + /// + /// Flags that indicate which of the other members contain valid data. This member can be + /// a combination of the NIF_XXX constants. + /// + public IconDataMembers ValidMembers; + + /// + /// Application-defined message identifier. The system uses this identifier to send + /// notifications to the window identified in hWnd. + /// + public uint CallbackMessageId; + + /// + /// A handle to the icon that should be displayed. Just + /// . + /// + public IntPtr IconHandle; + + /// + /// String with the text for a standard ToolTip. It can have a maximum of 64 characters including + /// the terminating NULL. For Version 5.0 and later, szTip can have a maximum of + /// 128 characters, including the terminating NULL. + /// + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)] public string ToolTipText; + + + /// + /// State of the icon. Remember to also set the . + /// + public IconState IconState; + + /// + /// A value that specifies which bits of the state member are retrieved or modified. + /// For example, setting this member to + /// causes only the item's hidden + /// state to be retrieved. + /// + public IconState StateMask; + + /// + /// String with the text for a balloon ToolTip. It can have a maximum of 255 characters. + /// To remove the ToolTip, set the NIF_INFO flag in uFlags and set szInfo to an empty string. + /// + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] public string BalloonText; + + /// + /// Mainly used to set the version when is invoked + /// with . However, for legacy operations, + /// the same member is also used to set timouts for balloon ToolTips. + /// + public uint VersionOrTimeout; + + /// + /// String containing a title for a balloon ToolTip. This title appears in boldface + /// above the text. It can have a maximum of 63 characters. + /// + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)] public string BalloonTitle; + + /// + /// Adds an icon to a balloon ToolTip, which is placed to the left of the title. If the + /// member is zero-length, the icon is not shown. + /// + public BalloonFlags BalloonFlags; + + /// + /// Windows XP (Shell32.dll version 6.0) and later.
+ /// - Windows 7 and later: A registered GUID that identifies the icon. + /// This value overrides uID and is the recommended method of identifying the icon.
+ /// - Windows XP through Windows Vista: Reserved. + ///
+ public Guid TaskbarIconGuid; + + /// + /// Windows Vista (Shell32.dll version 6.0.6) and later. The handle of a customized + /// balloon icon provided by the application that should be used independently + /// of the tray icon. If this member is non-NULL and the + /// flag is set, this icon is used as the balloon icon.
+ /// If this member is NULL, the legacy behavior is carried out. + ///
+ public IntPtr CustomBalloonIconHandle; + + + /// + /// Creates a default data structure that provides + /// a hidden taskbar icon without the icon being set. + /// + /// + /// + public static NotifyIconData CreateDefault(IntPtr handle) + { + var data = new NotifyIconData(); + + data.cbSize = (uint) Marshal.SizeOf(data); + + data.WindowHandle = handle; + data.TaskbarIconId = 0x0; + data.CallbackMessageId = WindowMessageSink.CallbackMessageId; + data.VersionOrTimeout = (uint) NotifyIconVersion.Win95; + + data.IconHandle = IntPtr.Zero; + + //hide initially + data.IconState = IconState.Hidden; + data.StateMask = IconState.Hidden; + + //set flags + data.ValidMembers = IconDataMembers.Message + | IconDataMembers.Icon + | IconDataMembers.Tip; + + //reset strings + data.ToolTipText = data.BalloonText = data.BalloonTitle = String.Empty; + + return data; + } + } +} \ No newline at end of file diff --git a/Source/NotifyIconWpf/Interop/NotifyIconVersion.cs b/Source/NotifyIconWpf/Interop/NotifyIconVersion.cs new file mode 100644 index 0000000..8be1618 --- /dev/null +++ b/Source/NotifyIconWpf/Interop/NotifyIconVersion.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Hardcodet.Wpf.TaskbarNotification.Interop +{ + /// + /// The notify icon version that is used. The higher + /// the version, the more capabilities are available. + /// + public enum NotifyIconVersion + { + /// + /// Default behavior (legacy Win95). + /// + Win95 = 0x0, + /// + /// Behavior representing Win2000 an higher. + /// + Win2000 = 0x3, + /// + /// Extended tooltip support, which is available + /// for Vista and later. + /// + Vista = 0x4 + } +} \ No newline at end of file diff --git a/Source/NotifyIconWpf/Interop/TrayLocator.cs b/Source/NotifyIconWpf/Interop/TrayLocator.cs new file mode 100644 index 0000000..de524a8 --- /dev/null +++ b/Source/NotifyIconWpf/Interop/TrayLocator.cs @@ -0,0 +1,95 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Runtime.InteropServices; + +namespace Hardcodet.Wpf.TaskbarNotification.Interop +{ + public struct Rect + { + public int left; + public int top; + public int right; + public int bottom; + public override string ToString() + { + return "(" + left + ", " + top + ") --> (" + right + ", " + bottom + ")"; + } + } + + public struct TaskbarInfo + { + public int cbSize; + public IntPtr WindowHandle; + public int uCallbackMessage; + public TaskbarPosition Position; + public Rect Rectangle; + public IntPtr lParam; + } + + + + public enum TaskbarPosition + { + Left = 0, + Top, + Right, + Bottom + } + + + + + /// + /// Locates the position of the tray area. + /// + public class TrayLocator + { + public enum ABMsg + { + ABM_NEW = 0, + ABM_REMOVE = 1, + ABM_QUERYPOS = 2, + ABM_SETPOS = 3, + ABM_GETSTATE = 4, + ABM_GETTASKBARPOS = 5, + ABM_ACTIVATE = 6, + ABM_GETAUTOHIDEBAR = 7, + ABM_SETAUTOHIDEBAR = 8, + ABM_WINDOWPOSCHANGED = 9, + ABM_SETSTATE = 10 + } + + public enum ABNotify + { + ABN_STATECHANGE = 0, + ABN_POSCHANGED, + ABN_FULLSCREENAPP, + ABN_WINDOWARRANGE + } + + [DllImport("shell32.dll", EntryPoint = "SHAppBarMessage", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)] + public static extern int SHAppBarMessage(int dwMessage, ref TaskbarInfo pData); + + + /// + /// Determines the current location of the taskbar. + /// + /// + public static TaskbarInfo GetTaskbarInformation() + { + TaskbarInfo tbInfo = new TaskbarInfo(); + tbInfo.cbSize = Marshal.SizeOf(tbInfo); + + //retrieve the bounding rectangle of the Windows taskbar. + SHAppBarMessage((int)ABMsg.ABM_GETTASKBARPOS, ref tbInfo); + + return tbInfo; + } + + } +} \ No newline at end of file diff --git a/Source/NotifyIconWpf/Interop/WinApi.cs b/Source/NotifyIconWpf/Interop/WinApi.cs new file mode 100644 index 0000000..7df8f20 --- /dev/null +++ b/Source/NotifyIconWpf/Interop/WinApi.cs @@ -0,0 +1,80 @@ +using System; +using System.Runtime.InteropServices; + +namespace Hardcodet.Wpf.TaskbarNotification.Interop +{ + /// + /// Win32 API imports. + /// + internal static class WinApi + { + /// + /// Creates, updates or deletes the taskbar icon. + /// + [DllImport("shell32.Dll")] + public static extern bool Shell_NotifyIcon(NotifyCommand cmd, [In]ref NotifyIconData data); + + + /// + /// Creates the helper window that receives messages from the taskar icon. + /// + [DllImport("USER32.DLL", EntryPoint = "CreateWindowExW", SetLastError = true)] + public static extern IntPtr CreateWindowEx(int dwExStyle, [MarshalAs(UnmanagedType.LPWStr)] string lpClassName, + [MarshalAs(UnmanagedType.LPWStr)] string lpWindowName, int dwStyle, int x, int y, + int nWidth, int nHeight, uint hWndParent, int hMenu, int hInstance, + int lpParam); + + + /// + /// Processes a default windows procedure. + /// + [DllImport("USER32.DLL")] + public static extern long DefWindowProc(IntPtr hWnd, uint msg, uint wparam, uint lparam); + + /// + /// Registers the helper window class. + /// + [DllImport("USER32.DLL", EntryPoint = "RegisterClassW", SetLastError = true)] + public static extern short RegisterClass(ref WindowClass lpWndClass); + + /// + /// Registers a listener for a window message. + /// + /// + /// + [DllImport("User32.Dll", EntryPoint = "RegisterWindowMessageW")] + public static extern uint RegisterWindowMessage([MarshalAs(UnmanagedType.LPWStr)] string lpString); + + /// + /// Used to destroy the hidden helper window that receives messages from the + /// taskbar icon. + /// + /// + /// + [DllImport("USER32.DLL", SetLastError = true)] + public static extern bool DestroyWindow(IntPtr hWnd); + + + /// + /// Gives focus to a given window. + /// + /// + /// + [DllImport("USER32.DLL")] + public static extern bool SetForegroundWindow(IntPtr hWnd); + + + /// + /// Gets the maximum number of milliseconds that can elapse between a + /// first click and a second click for the OS to consider the + /// mouse action a double-click. + /// + /// The maximum amount of time, in milliseconds, that can + /// elapse between a first click and a second click for the OS to + /// consider the mouse action a double-click. + [DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)] + public static extern int GetDoubleClickTime(); + + + } +} \ No newline at end of file diff --git a/Source/NotifyIconWpf/Interop/WindowClass.cs b/Source/NotifyIconWpf/Interop/WindowClass.cs new file mode 100644 index 0000000..e4f625a --- /dev/null +++ b/Source/NotifyIconWpf/Interop/WindowClass.cs @@ -0,0 +1,31 @@ +using System; +using System.Runtime.InteropServices; + +namespace Hardcodet.Wpf.TaskbarNotification.Interop +{ + /// + /// Callback delegate which is used by the Windows API to + /// submit window messages. + /// + public delegate long WindowProcedureHandler(IntPtr hwnd, uint uMsg, uint wparam, uint lparam); + + + /// + /// Win API WNDCLASS struct - represents a single window. + /// Used to receive window messages. + /// + [StructLayout(LayoutKind.Sequential)] + public struct WindowClass + { + public uint style; + public WindowProcedureHandler lpfnWndProc; + public int cbClsExtra; + public int cbWndExtra; + public IntPtr hInstance; + public IntPtr hIcon; + public IntPtr hCursor; + public IntPtr hbrBackground; + [MarshalAs(UnmanagedType.LPWStr)] public string lpszMenuName; + [MarshalAs(UnmanagedType.LPWStr)] public string lpszClassName; + } +} \ No newline at end of file diff --git a/Source/NotifyIconWpf/Interop/WindowMessageSink.Handle.cs b/Source/NotifyIconWpf/Interop/WindowMessageSink.Handle.cs new file mode 100644 index 0000000..5a4d4e9 --- /dev/null +++ b/Source/NotifyIconWpf/Interop/WindowMessageSink.Handle.cs @@ -0,0 +1,102 @@ +using System; +using System.ComponentModel; + +namespace Hardcodet.Wpf.TaskbarNotification.Interop +{ + /// + /// Provides low level code that is used to receive + /// window messages without having a window that + /// prevents a WPF application from shutting down + /// properly. + /// + public partial class WindowMessageSink + { + /// + /// Window class ID. + /// + private string WindowId; + + /// + /// Handle for the message window. + /// + /// The ID of the message that is being received if the + /// taskbar is (re)started. + /// + private uint taskbarRestartMessageId; + + /// + /// A delegate that processes messages of the hidden + /// native window that receives window messages. Storing + /// this reference makes sure we don't loose our reference + /// to the message window. + /// + private WindowProcedureHandler messageHandler; + + /// + /// Creates the helper message window that is used + /// to receive messages from the taskbar icon. + /// + private void CreateMessageWindow() + { + WindowId = "WPFTaskbarIcon_" + Guid.NewGuid().ToString(); + + //register window message handler + messageHandler = OnWindowMessageReceived; + + // Create a simple window class which is reference through + //the messageHandler delegate + WindowClass wc; + + wc.style = 0; + wc.lpfnWndProc = messageHandler; + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + wc.hInstance = IntPtr.Zero; + wc.hIcon = IntPtr.Zero; + wc.hCursor = IntPtr.Zero; + wc.hbrBackground = IntPtr.Zero; + wc.lpszMenuName = ""; + wc.lpszClassName = WindowId; + + // Register the window class + WinApi.RegisterClass(ref wc); + + // Get the message used to indicate the taskbar has been restarted + // This is used to re-add icons when the taskbar restarts + taskbarRestartMessageId = WinApi.RegisterWindowMessage("TaskbarCreated"); + + // Create the message window + MessageWindowHandle = WinApi.CreateWindowEx(0, WindowId, "", 0, 0, 0, 1, 1, 0, 0, 0, 0); + + if (MessageWindowHandle == IntPtr.Zero) + { + throw new Win32Exception(); + } + } + + + + /// + /// Callback method that receives messages from the taskbar area. + /// + private long OnWindowMessageReceived(IntPtr hwnd, uint messageId, uint wparam, uint lparam) + { + if (messageId == taskbarRestartMessageId) + { + //recreate the icon if the taskbar was restarted + //TODO refresh icon + } + + ProcessWindowMessage(messageId, wparam, lparam); + + //handle mouse clicks... + + // Pass the message to the default window procedure + return WinApi.DefWindowProc(hwnd, messageId, wparam, lparam); + } + + } +} \ No newline at end of file diff --git a/Source/NotifyIconWpf/Interop/WindowMessageSink.cs b/Source/NotifyIconWpf/Interop/WindowMessageSink.cs new file mode 100644 index 0000000..29e2542 --- /dev/null +++ b/Source/NotifyIconWpf/Interop/WindowMessageSink.cs @@ -0,0 +1,255 @@ +using System; +using System.Diagnostics; + +namespace Hardcodet.Wpf.TaskbarNotification.Interop +{ + /// + /// Receives messages from the taskbar icon through + /// window messages of an underlying helper window. + /// + public partial class WindowMessageSink : IDisposable + { + + #region members + + /// + /// The ID of messages that are received from the the + /// taskbar icon. + /// + public const int CallbackMessageId = 0x400; + + /// + /// The version of the underlying icon. Defines how + /// incoming messages are interpreted. + /// + public NotifyIconVersion Version { get; set; } + + #endregion + + + #region events + + /// + /// The custom tooltip should be closed or hidden. + /// + public event Action ChangeToolTipStateRequest; + + /// + /// Fired in case the user clicked or moved within + /// the taskbar icon area. + /// + public event Action MouseEventReceived; + + /// + /// Fired if a balloon ToolTip was either displayed + /// or closed (indicated by the boolean flag). + /// + public event Action BallonToolTipChanged; + + /// + /// Fired if the taskbar was created. Requires the taskbar + /// icon to be reset. + /// + public event Action TaskbarCreated; + + #endregion + + + #region construction + + /// + /// Creates a new message sink that receives message from + /// a given taskbar icon. + /// + /// + public WindowMessageSink(NotifyIconVersion version) + { + Version = version; + CreateMessageWindow(); + } + + + private WindowMessageSink() + { + } + + + /// + /// Creates a dummy instance that provides an empty + /// pointer rather than a real window handler.
+ /// Used at design time. + ///
+ /// + internal static WindowMessageSink CreateEmpty() + { + return new WindowMessageSink + { + MessageWindowHandle = IntPtr.Zero, + Version = NotifyIconVersion.Vista + }; + } + + #endregion + + + #region Process Window Messages + + /// + /// Processes incoming system messages. + /// + /// + /// + /// + /// + private void ProcessWindowMessage(uint msg, uint wParam, uint lParam) + { + if (msg != CallbackMessageId) return; + + switch (lParam) + { + case 0x200: +// Debug.WriteLine("MOVE"); + MouseEventReceived(MouseEvent.MouseMove); + break; + + case 0x201: + Debug.WriteLine("left down 1"); + MouseEventReceived(MouseEvent.IconLeftMouseDown); + break; + + case 0x202: + Debug.WriteLine("left up"); + MouseEventReceived(MouseEvent.IconLeftMouseUp); + break; + + case 0x203: + Debug.WriteLine("left click 2"); + MouseEventReceived(MouseEvent.IconDoubleClick); + break; + + case 0x204: + Debug.WriteLine("right click 1"); + MouseEventReceived(MouseEvent.IconRightMouseDown); + break; + + case 0x205: + Console.Out.WriteLine("right mouse up"); + MouseEventReceived(MouseEvent.IconRightMouseUp); + break; + + case 0x206: + //double click with right mouse button - do not trigger event + Debug.WriteLine("right click 2"); + break; + + case 0x207: + Debug.WriteLine("middle click 1"); + MouseEventReceived(MouseEvent.IconMiddleMouseDown); + break; + + case 520: + Debug.WriteLine("mouse up middle"); + MouseEventReceived(MouseEvent.IconMiddleMouseUp); + break; + + case 0x209: + //double click with middle mouse button - do not trigger event + Debug.WriteLine("middle click 2"); + break; + + case 0x402: + Debug.WriteLine("balloon shown"); + BallonToolTipChanged(true); + break; + + case 0x403: + case 0x404: + Debug.WriteLine("balloon close"); + BallonToolTipChanged(false); + break; + + case 0x405: + Debug.WriteLine("balloon clicked"); + MouseEventReceived(MouseEvent.BalloonToolTipClicked); + break; + + case 0x406: + Debug.WriteLine("show custom tooltip"); + ChangeToolTipStateRequest(true); + break; + + case 0x407: + Debug.WriteLine("close custom tooltip"); + ChangeToolTipStateRequest(false); + break; + + default: + Debug.WriteLine("Unhandled message ID: " + lParam); + break; + } + + } + + #endregion + + + + #region Dispose + + /// + /// Set to true as soon as + /// has been invoked. + /// + public bool IsDisposed { get; private set; } + + + /// + /// Disposes the object. + /// + /// This method is not virtual by design. Derived classes + /// should override . + /// + public void Dispose() + { + Dispose(true); + + // This object will be cleaned up by the Dispose method. + // Therefore, you should call GC.SupressFinalize to + // take this object off the finalization queue + // and prevent finalization code for this object + // from executing a second time. + GC.SuppressFinalize(this); + } + + /// + /// This destructor will run only if the + /// method does not get called. This gives this base class the + /// opportunity to finalize. + /// + /// Important: Do not provide destructors in types derived from + /// this class. + /// + /// + ~WindowMessageSink() + { + Dispose(false); + } + + + /// + /// Removes the windows hook that receives window + /// messages and closes the underlying helper window. + /// + private void Dispose(bool disposing) + { + //don't do anything if the component is already disposed + if (IsDisposed || !disposing) return; + IsDisposed = true; + + WinApi.DestroyWindow(MessageWindowHandle); + messageHandler = null; + } + + #endregion + } +} \ No newline at end of file diff --git a/Source/NotifyIconWpf/NotifyIconWpf.csproj b/Source/NotifyIconWpf/NotifyIconWpf.csproj new file mode 100644 index 0000000..a3b1416 --- /dev/null +++ b/Source/NotifyIconWpf/NotifyIconWpf.csproj @@ -0,0 +1,108 @@ + + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {7AC63864-7638-41C4-969C-D3197EF2BED9} + library + Properties + Hardcodet.Wpf.TaskbarNotification + Hardcodet.Wpf.TaskbarNotification + v3.5 + 512 + {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + 4 + Client + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + 3.5 + + + + 3.5 + + + 3.5 + + + + + + + + + + + + + + + + + + + + + + + + + + Code + + + True + True + Resources.resx + + + True + Settings.settings + True + + + + + + ResXFileCodeGenerator + Resources.Designer.cs + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + + + + + + + \ No newline at end of file diff --git a/Source/NotifyIconWpf/PopupActivationMode.cs b/Source/NotifyIconWpf/PopupActivationMode.cs new file mode 100644 index 0000000..81248b8 --- /dev/null +++ b/Source/NotifyIconWpf/PopupActivationMode.cs @@ -0,0 +1,45 @@ +namespace Hardcodet.Wpf.TaskbarNotification +{ + /// + /// Defines flags that define when a popup + /// is being displyed. + /// + public enum PopupActivationMode + { + /// + /// The item is displayed if the user clicks the + /// tray icon with the left mouse button. + /// + LeftClick, + /// + /// The item is displayed if the user clicks the + /// tray icon with the right mouse button. + /// + RightClick, + /// + /// The item is displayed if the user double-clicks the + /// tray icon. + /// + DoubleClick, + /// + /// The item is displayed if the user clicks the + /// tray icon with the left or the right mouse button. + /// + LeftOrRightClick, + /// + /// The item is displayed if the user clicks the + /// tray icon with the left mouse button or if a + /// double-click is being performed. + /// + LeftOrDoubleClick, + /// + /// The item is displayed if the user clicks the + /// tray icon with the middle mouse button. + /// + MiddleClick, + /// + /// The item is displayed whenever a click occurs. + /// + All + } +} diff --git a/Source/NotifyIconWpf/Properties/AssemblyInfo.cs b/Source/NotifyIconWpf/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..adfde1d --- /dev/null +++ b/Source/NotifyIconWpf/Properties/AssemblyInfo.cs @@ -0,0 +1,59 @@ +using System.Reflection; +using System.Resources; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Windows; +using System.Windows.Markup; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("NotifyIconWpf")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Microsoft")] +[assembly: AssemblyProduct("NotifyIconWpf")] +[assembly: AssemblyCopyright("Copyright © Microsoft 2009")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + + +[assembly: XmlnsDefinition("http://www.hardcodet.net/taskbar", "Hardcodet.Wpf.TaskbarNotification")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +//In order to begin building localizable applications, set +//CultureYouAreCodingWith in your .csproj file +//inside a . For example, if you are using US english +//in your source files, set the to en-US. Then uncomment +//the NeutralResourceLanguage attribute below. Update the "en-US" in +//the line below to match the UICulture setting in the project file. + +//[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)] + + +[assembly: ThemeInfo( + ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located + //(used if a resource is not found in the page, + // or application resource dictionaries) + ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located + //(used if a resource is not found in the page, + // app, or any theme specific resource dictionaries) +)] + + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Source/NotifyIconWpf/Properties/Resources.Designer.cs b/Source/NotifyIconWpf/Properties/Resources.Designer.cs new file mode 100644 index 0000000..d78e26d --- /dev/null +++ b/Source/NotifyIconWpf/Properties/Resources.Designer.cs @@ -0,0 +1,70 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:2.0.50727.3053 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Hardcodet.Wpf.TaskbarNotification.Properties { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "2.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Hardcodet.Wpf.TaskbarNotification.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + internal static System.Drawing.Icon DefaultTrayIcon { + get { + object obj = ResourceManager.GetObject("DefaultTrayIcon", resourceCulture); + return ((System.Drawing.Icon)(obj)); + } + } + } +} diff --git a/Source/NotifyIconWpf/Properties/Resources.resx b/Source/NotifyIconWpf/Properties/Resources.resx new file mode 100644 index 0000000..c0f916f --- /dev/null +++ b/Source/NotifyIconWpf/Properties/Resources.resx @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + ..\defaulttrayicon.ico;System.Drawing.Icon, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + \ No newline at end of file diff --git a/Source/NotifyIconWpf/Properties/Settings.Designer.cs b/Source/NotifyIconWpf/Properties/Settings.Designer.cs new file mode 100644 index 0000000..b9cf0e1 --- /dev/null +++ b/Source/NotifyIconWpf/Properties/Settings.Designer.cs @@ -0,0 +1,26 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:2.0.50727.3053 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Hardcodet.Wpf.TaskbarNotification.Properties { + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "9.0.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default { + get { + return defaultInstance; + } + } + } +} diff --git a/Source/NotifyIconWpf/Properties/Settings.settings b/Source/NotifyIconWpf/Properties/Settings.settings new file mode 100644 index 0000000..8f2fd95 --- /dev/null +++ b/Source/NotifyIconWpf/Properties/Settings.settings @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/Source/NotifyIconWpf/RoutedEventHelper.cs b/Source/NotifyIconWpf/RoutedEventHelper.cs new file mode 100644 index 0000000..127ec77 --- /dev/null +++ b/Source/NotifyIconWpf/RoutedEventHelper.cs @@ -0,0 +1,81 @@ +using System; +using System.Windows; + +namespace Hardcodet.Wpf.TaskbarNotification +{ + /// + /// Helper class used by routed events of the + /// class. + /// + internal static class RoutedEventHelper + { + #region RoutedEvent Helper Methods + + /// + /// A static helper method to raise a routed event on a target UIElement or ContentElement. + /// + /// UIElement or ContentElement on which to raise the event + /// RoutedEventArgs to use when raising the event + internal static void RaiseEvent(DependencyObject target, RoutedEventArgs args) + { + if (target is UIElement) + { + (target as UIElement).RaiseEvent(args); + } + else if (target is ContentElement) + { + (target as ContentElement).RaiseEvent(args); + } + } + + /// + /// A static helper method that adds a handler for a routed event + /// to a target UIElement or ContentElement. + /// + /// UIElement or ContentElement that listens to the event + /// Event that will be handled + /// Event handler to be added + internal static void AddHandler(DependencyObject element, RoutedEvent routedEvent, Delegate handler) + { + UIElement uie = element as UIElement; + if (uie != null) + { + uie.AddHandler(routedEvent, handler); + } + else + { + ContentElement ce = element as ContentElement; + if (ce != null) + { + ce.AddHandler(routedEvent, handler); + } + } + } + + /// + /// A static helper method that removes a handler for a routed event + /// from a target UIElement or ContentElement. + /// + /// UIElement or ContentElement that listens to the event + /// Event that will no longer be handled + /// Event handler to be removed + internal static void RemoveHandler(DependencyObject element, RoutedEvent routedEvent, Delegate handler) + { + UIElement uie = element as UIElement; + if (uie != null) + { + uie.RemoveHandler(routedEvent, handler); + } + else + { + ContentElement ce = element as ContentElement; + if (ce != null) + { + ce.RemoveHandler(routedEvent, handler); + } + } + } + + #endregion + } +} diff --git a/Source/NotifyIconWpf/TaskbarIcon.Declarations.cs b/Source/NotifyIconWpf/TaskbarIcon.Declarations.cs new file mode 100644 index 0000000..e3a893a --- /dev/null +++ b/Source/NotifyIconWpf/TaskbarIcon.Declarations.cs @@ -0,0 +1,868 @@ +using System; +using System.Drawing; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Controls.Primitives; +using System.Windows.Media; +using Hardcodet.Wpf.TaskbarNotification.Interop; + +namespace Hardcodet.Wpf.TaskbarNotification +{ + /// + /// Contains declarations of WPF dependency properties + /// and events. + /// + partial class TaskbarIcon + { + //DEPENDENCY PROPERTIES + + #region ToolTipText dependency property + + /// + /// A tooltip text that is being displayed if no custom + /// was set or if custom tooltips are not supported. + /// + public static readonly DependencyProperty ToolTipTextProperty = + DependencyProperty.Register("ToolTipText", + typeof (string), + typeof (TaskbarIcon), + new FrameworkPropertyMetadata(String.Empty, ToolTipTextPropertyChanged)); + + + /// + /// A property wrapper for the + /// dependency property:
+ /// A tooltip text that is being displayed if no custom + /// was set or if custom tooltips are not supported. + ///
+ public string ToolTipText + { + get { return (string) GetValue(ToolTipTextProperty); } + set { SetValue(ToolTipTextProperty, value); } + } + + + /// + /// A static callback listener which is being invoked if the + /// dependency property has + /// been changed. Invokes the + /// instance method of the changed instance. + /// + /// The currently processed owner of the property. + /// Provides information about the updated property. + private static void ToolTipTextPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + TaskbarIcon owner = (TaskbarIcon) d; + owner.OnToolTipTextPropertyChanged(e); + } + + + /// + /// Handles changes of the dependency property. As + /// WPF internally uses the dependency property system and bypasses the + /// property wrapper, updates of the property's value + /// should be handled here. + /// Provides information about the updated property. + private void OnToolTipTextPropertyChanged(DependencyPropertyChangedEventArgs e) + { + string newValue = (string) e.NewValue; + + iconData.ToolTipText = newValue ?? String.Empty; + WriteToolTipSettings(); + } + + #endregion + + + #region ToolTip dependency property override + + /// + /// A static callback listener which is being invoked if the + /// dependency property has + /// been changed. Invokes the + /// instance method of the changed instance. + /// + /// The currently processed owner of the property. + /// Provides information about the updated property. + private static void ToolTipPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + TaskbarIcon owner = (TaskbarIcon) d; + owner.OnToolTipPropertyChanged(e); + } + + + /// + /// Handles changes of the dependency property. As + /// WPF internally uses the dependency property system and bypasses the + /// property wrapper, updates of the property's value + /// should be handled here. + /// Provides information about the updated property. + private void OnToolTipPropertyChanged(DependencyPropertyChangedEventArgs e) + { + if (e.NewValue != null) + { + ToolTip tt = e.NewValue as ToolTip; + if (tt == null) + { + tt = new ToolTip(); + tt.Content = e.NewValue; + + ToolTip = tt; + return; + } + } + + WriteToolTipSettings(); + } + + #endregion + + + #region Icon property / IconSource dependency property + + private Icon icon; + + /// + /// Gets or sets the icon to be displayed. This is not a + /// dependency property - if you want to assign the property + /// through XAML, please use the + /// dependency property. + /// + public Icon Icon + { + get { return icon; } + set + { + icon = value; + iconData.IconHandle = value == null ? IntPtr.Zero : icon.Handle; + + Util.WriteIconData(ref iconData, NotifyCommand.Modify, IconDataMembers.Icon); + } + } + + + /// + /// Resolves an image source and updates the property accordingly. + /// + public static readonly DependencyProperty IconSourceProperty = + DependencyProperty.Register("IconSource", + typeof (ImageSource), + typeof (TaskbarIcon), + new FrameworkPropertyMetadata(null, IconSourcePropertyChanged)); + + /// + /// A property wrapper for the + /// dependency property:
+ /// Resolves an image source and updates the property accordingly. + ///
+ public ImageSource IconSource + { + get { return (ImageSource) GetValue(IconSourceProperty); } + set { SetValue(IconSourceProperty, value); } + } + + + /// + /// A static callback listener which is being invoked if the + /// dependency property has + /// been changed. Invokes the + /// instance method of the changed instance. + /// + /// The currently processed owner of the property. + /// Provides information about the updated property. + private static void IconSourcePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + TaskbarIcon owner = (TaskbarIcon) d; + owner.OnIconSourcePropertyChanged(e); + } + + + /// + /// Handles changes of the dependency property. As + /// WPF internally uses the dependency property system and bypasses the + /// property wrapper, updates of the property's value + /// should be handled here. + /// Provides information about the updated property. + private void OnIconSourcePropertyChanged(DependencyPropertyChangedEventArgs e) + { + ImageSource newValue = (ImageSource) e.NewValue; + Icon = newValue.ToIcon(); + } + + #endregion + + + #region TaskbarIconPopup dependency property + + /// + /// A custom popup that is displayed when the taskbar icon is clicked. + /// + public static readonly DependencyProperty TaskbarIconPopupProperty = + DependencyProperty.Register("TaskbarIconPopup", + typeof (Popup), + typeof (TaskbarIcon), + new FrameworkPropertyMetadata(null, TaskbarIconPopupPropertyChanged)); + + /// + /// A property wrapper for the + /// dependency property:
+ /// A custom popup that is displayed when the taskbar icon is clicked. + ///
+ public Popup TaskbarIconPopup + { + get { return (Popup) GetValue(TaskbarIconPopupProperty); } + set { SetValue(TaskbarIconPopupProperty, value); } + } + + + /// + /// A static callback listener which is being invoked if the + /// dependency property has + /// been changed. Invokes the + /// instance method of the changed instance. + /// + /// The currently processed owner of the property. + /// Provides information about the updated property. + private static void TaskbarIconPopupPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + TaskbarIcon owner = (TaskbarIcon) d; + owner.OnTaskbarIconPopupPropertyChanged(e); + } + + + /// + /// Handles changes of the dependency property. As + /// WPF internally uses the dependency property system and bypasses the + /// property wrapper, updates of the property's value + /// should be handled here. + /// Provides information about the updated property. + private void OnTaskbarIconPopupPropertyChanged(DependencyPropertyChangedEventArgs e) + { + Popup newValue = (Popup) e.NewValue; + } + + #endregion + + + #region MenuActivation dependency property + + /// + /// Defines what mouse events display the context menu. + /// Defaults to . + /// + public static readonly DependencyProperty MenuActivationProperty = + DependencyProperty.Register("MenuActivation", + typeof (PopupActivationMode), + typeof (TaskbarIcon), + new FrameworkPropertyMetadata(PopupActivationMode.RightClick, MenuActivationPropertyChanged)); + + /// + /// A property wrapper for the + /// dependency property:
+ /// Defines what mouse events display the context menu. + /// Defaults to . + ///
+ public PopupActivationMode MenuActivation + { + get { return (PopupActivationMode) GetValue(MenuActivationProperty); } + set { SetValue(MenuActivationProperty, value); } + } + + + /// + /// A static callback listener which is being invoked if the + /// dependency property has + /// been changed. Invokes the + /// instance method of the changed instance. + /// + /// The currently processed owner of the property. + /// Provides information about the updated property. + private static void MenuActivationPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + TaskbarIcon owner = (TaskbarIcon) d; + owner.OnMenuActivationPropertyChanged(e); + } + + + /// + /// Handles changes of the dependency property. As + /// WPF internally uses the dependency property system and bypasses the + /// property wrapper, updates of the property's value + /// should be handled here. + /// Provides information about the updated property. + private void OnMenuActivationPropertyChanged(DependencyPropertyChangedEventArgs e) + { + PopupActivationMode newValue = (PopupActivationMode) e.NewValue; + + //TODO provide implementation + throw new NotImplementedException("Change event handler for dependency property MenuActivation not implemented."); + } + + #endregion + + + #region PopupActivation dependency property + + /// + /// Defines what mouse events trigger the . + /// Default is . + /// + public static readonly DependencyProperty PopupActivationProperty = + DependencyProperty.Register("PopupActivation", + typeof (PopupActivationMode), + typeof (TaskbarIcon), + new FrameworkPropertyMetadata(PopupActivationMode.LeftClick, PopupActivationPropertyChanged)); + + /// + /// A property wrapper for the + /// dependency property:
+ /// Defines what mouse events trigger the . + /// Default is . + ///
+ public PopupActivationMode PopupActivation + { + get { return (PopupActivationMode) GetValue(PopupActivationProperty); } + set { SetValue(PopupActivationProperty, value); } + } + + + /// + /// A static callback listener which is being invoked if the + /// dependency property has + /// been changed. Invokes the + /// instance method of the changed instance. + /// + /// The currently processed owner of the property. + /// Provides information about the updated property. + private static void PopupActivationPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + TaskbarIcon owner = (TaskbarIcon) d; + owner.OnPopupActivationPropertyChanged(e); + } + + + /// + /// Handles changes of the dependency property. As + /// WPF internally uses the dependency property system and bypasses the + /// property wrapper, updates of the property's value + /// should be handled here. + /// Provides information about the updated property. + private void OnPopupActivationPropertyChanged(DependencyPropertyChangedEventArgs e) + { + PopupActivationMode newValue = (PopupActivationMode) e.NewValue; + + //TODO provide implementation + throw new NotImplementedException("Change event handler for dependency property PopupActivation not implemented."); + } + + #endregion + + + #region Visibility dependency property override + + /// + /// A static callback listener which is being invoked if the + /// dependency property has + /// been changed. Invokes the + /// instance method of the changed instance. + /// + /// The currently processed owner of the property. + /// Provides information about the updated property. + private static void VisibilityPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + TaskbarIcon owner = (TaskbarIcon) d; + owner.OnVisibilityPropertyChanged(e); + } + + + /// + /// Handles changes of the dependency property. As + /// WPF internally uses the dependency property system and bypasses the + /// property wrapper, updates of the property's value + /// should be handled here. + /// Provides information about the updated property. + private void OnVisibilityPropertyChanged(DependencyPropertyChangedEventArgs e) + { + Visibility newValue = (Visibility) e.NewValue; + + //update + if (newValue == Visibility.Visible) + { + CreateTaskbarIcon(); + } + else + { + RemoveTaskbarIcon(); + } + } + + #endregion + + + #region ContextMenu dependency property override + + /// + /// A static callback listener which is being invoked if the + /// dependency property has + /// been changed. Invokes the + /// instance method of the changed instance. + /// + /// The currently processed owner of the property. + /// Provides information about the updated property. + private static void ContextMenuPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + TaskbarIcon owner = (TaskbarIcon) d; + owner.OnContextMenuPropertyChanged(e); + } + + + /// + /// Handles changes of the dependency property. As + /// WPF internally uses the dependency property system and bypasses the + /// property wrapper, updates of the property's value + /// should be handled here. + /// Provides information about the updated property. + private void OnContextMenuPropertyChanged(DependencyPropertyChangedEventArgs e) + { + ContextMenu newValue = (ContextMenu) e.NewValue; + //TODO provide implementation + } + + #endregion + + + + //EVENTS + + #region TaskbarIconLeftMouseDown + + /// + /// TaskbarIconLeftMouseDown Routed Event + /// + public static readonly RoutedEvent TaskbarIconLeftMouseDownEvent = EventManager.RegisterRoutedEvent("TaskbarIconLeftMouseDown", + RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(TaskbarIcon)); + + /// + /// Occurs when the user presses the left mouse button. + /// + public event RoutedEventHandler TaskbarIconLeftMouseDown + { + add { AddHandler(TaskbarIconLeftMouseDownEvent, value); } + remove { RemoveHandler(TaskbarIconLeftMouseDownEvent, value); } + } + + /// + /// A helper method to raise the TaskbarIconLeftMouseDown event. + /// + protected RoutedEventArgs RaiseTaskbarIconLeftMouseDownEvent() + { + return RaiseTaskbarIconLeftMouseDownEvent(this); + } + + /// + /// A static helper method to raise the TaskbarIconLeftMouseDown event on a target element. + /// + /// UIElement or ContentElement on which to raise the event + internal static RoutedEventArgs RaiseTaskbarIconLeftMouseDownEvent(DependencyObject target) + { + if (target == null) return null; + + RoutedEventArgs args = new RoutedEventArgs(); + args.RoutedEvent = TaskbarIconLeftMouseDownEvent; + RoutedEventHelper.RaiseEvent(target, args); + return args; + } + + #endregion + + #region TaskbarIconRightMouseDown + + /// + /// TaskbarIconRightMouseDown Routed Event + /// + public static readonly RoutedEvent TaskbarIconRightMouseDownEvent = EventManager.RegisterRoutedEvent("TaskbarIconRightMouseDown", + RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(TaskbarIcon)); + + /// + /// Occurs when the presses the right mouse button. + /// + public event RoutedEventHandler TaskbarIconRightMouseDown + { + add { AddHandler(TaskbarIconRightMouseDownEvent, value); } + remove { RemoveHandler(TaskbarIconRightMouseDownEvent, value); } + } + + /// + /// A helper method to raise the TaskbarIconRightMouseDown event. + /// + protected RoutedEventArgs RaiseTaskbarIconRightMouseDownEvent() + { + return RaiseTaskbarIconRightMouseDownEvent(this); + } + + /// + /// A static helper method to raise the TaskbarIconRightMouseDown event on a target element. + /// + /// UIElement or ContentElement on which to raise the event + internal static RoutedEventArgs RaiseTaskbarIconRightMouseDownEvent(DependencyObject target) + { + if (target == null) return null; + + RoutedEventArgs args = new RoutedEventArgs(); + args.RoutedEvent = TaskbarIconRightMouseDownEvent; + RoutedEventHelper.RaiseEvent(target, args); + return args; + } + + #endregion + + #region TaskbarIconLeftMouseUp + + /// + /// TaskbarIconLeftMouseUp Routed Event + /// + public static readonly RoutedEvent TaskbarIconLeftMouseUpEvent = EventManager.RegisterRoutedEvent("TaskbarIconLeftMouseUp", + RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(TaskbarIcon)); + + /// + /// Occurs when the user releases the left mouse button. + /// + public event RoutedEventHandler TaskbarIconLeftMouseUp + { + add { AddHandler(TaskbarIconLeftMouseUpEvent, value); } + remove { RemoveHandler(TaskbarIconLeftMouseUpEvent, value); } + } + + /// + /// A helper method to raise the TaskbarIconLeftMouseUp event. + /// + protected RoutedEventArgs RaiseTaskbarIconLeftMouseUpEvent() + { + return RaiseTaskbarIconLeftMouseUpEvent(this); + } + + /// + /// A static helper method to raise the TaskbarIconLeftMouseUp event on a target element. + /// + /// UIElement or ContentElement on which to raise the event + internal static RoutedEventArgs RaiseTaskbarIconLeftMouseUpEvent(DependencyObject target) + { + if (target == null) return null; + + RoutedEventArgs args = new RoutedEventArgs(); + args.RoutedEvent = TaskbarIconLeftMouseUpEvent; + RoutedEventHelper.RaiseEvent(target, args); + return args; + } + + #endregion + + #region TaskbarIconRightMouseUp + + /// + /// TaskbarIconRightMouseUp Routed Event + /// + public static readonly RoutedEvent TaskbarIconRightMouseUpEvent = EventManager.RegisterRoutedEvent("TaskbarIconRightMouseUp", + RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(TaskbarIcon)); + + /// + /// Occurs when the user releases the right mouse button. + /// + public event RoutedEventHandler TaskbarIconRightMouseUp + { + add { AddHandler(TaskbarIconRightMouseUpEvent, value); } + remove { RemoveHandler(TaskbarIconRightMouseUpEvent, value); } + } + + /// + /// A helper method to raise the TaskbarIconRightMouseUp event. + /// + protected RoutedEventArgs RaiseTaskbarIconRightMouseUpEvent() + { + return RaiseTaskbarIconRightMouseUpEvent(this); + } + + /// + /// A static helper method to raise the TaskbarIconRightMouseUp event on a target element. + /// + /// UIElement or ContentElement on which to raise the event + internal static RoutedEventArgs RaiseTaskbarIconRightMouseUpEvent(DependencyObject target) + { + if (target == null) return null; + + RoutedEventArgs args = new RoutedEventArgs(); + args.RoutedEvent = TaskbarIconRightMouseUpEvent; + RoutedEventHelper.RaiseEvent(target, args); + return args; + } + + #endregion + + + #region TaskbarIconPopupOpen (and PreviewTaskbarIconPopupOpen) + + /// + /// TaskbarIconPopupOpen Routed Event + /// + public static readonly RoutedEvent TaskbarIconPopupOpenEvent = EventManager.RegisterRoutedEvent("TaskbarIconPopupOpen", + RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(TaskbarIcon)); + + /// + /// Bubbled event that occurs when the custom popup is being opened. + /// + public event RoutedEventHandler TaskbarIconPopupOpen + { + add { AddHandler(TaskbarIconPopupOpenEvent, value); } + remove { RemoveHandler(TaskbarIconPopupOpenEvent, value); } + } + + /// + /// A helper method to raise the TaskbarIconPopupOpen event. + /// + protected RoutedEventArgs RaiseTaskbarIconPopupOpenEvent() + { + return RaiseTaskbarIconPopupOpenEvent(this); + } + + /// + /// A static helper method to raise the TaskbarIconPopupOpen event on a target element. + /// + /// UIElement or ContentElement on which to raise the event + internal static RoutedEventArgs RaiseTaskbarIconPopupOpenEvent(DependencyObject target) + { + if (target == null) return null; + + RoutedEventArgs args = new RoutedEventArgs(); + args.RoutedEvent = TaskbarIconPopupOpenEvent; + RoutedEventHelper.RaiseEvent(target, args); + return args; + } + + /// + /// PreviewTaskbarIconPopupOpen Routed Event + /// + public static readonly RoutedEvent PreviewTaskbarIconPopupOpenEvent = EventManager.RegisterRoutedEvent("PreviewTaskbarIconPopupOpen", + RoutingStrategy.Tunnel, typeof(RoutedEventHandler), typeof(TaskbarIcon)); + + /// + /// Tunneled event that occurs when the custom popup is being opened. + /// + public event RoutedEventHandler PreviewTaskbarIconPopupOpen + { + add { AddHandler(PreviewTaskbarIconPopupOpenEvent, value); } + remove { RemoveHandler(PreviewTaskbarIconPopupOpenEvent, value); } + } + + /// + /// A helper method to raise the PreviewTaskbarIconPopupOpen event. + /// + protected RoutedEventArgs RaisePreviewTaskbarIconPopupOpenEvent() + { + return RaisePreviewTaskbarIconPopupOpenEvent(this); + } + + /// + /// A static helper method to raise the PreviewTaskbarIconPopupOpen event on a target element. + /// + /// UIElement or ContentElement on which to raise the event + internal static RoutedEventArgs RaisePreviewTaskbarIconPopupOpenEvent(DependencyObject target) + { + if (target == null) return null; + + RoutedEventArgs args = new RoutedEventArgs(); + args.RoutedEvent = PreviewTaskbarIconPopupOpenEvent; + RoutedEventHelper.RaiseEvent(target, args); + return args; + } + + #endregion + + #region TaskbarIconToolTipOpen (and PreviewTaskbarIconToolTipOpen) + + /// + /// TaskbarIconToolTipOpen Routed Event + /// + public static readonly RoutedEvent TaskbarIconToolTipOpenEvent = EventManager.RegisterRoutedEvent("TaskbarIconToolTipOpen", + RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(TaskbarIcon)); + + /// + /// Bubbled event that occurs when the custom ToolTip is being displayed. + /// + public event RoutedEventHandler TaskbarIconToolTipOpen + { + add { AddHandler(TaskbarIconToolTipOpenEvent, value); } + remove { RemoveHandler(TaskbarIconToolTipOpenEvent, value); } + } + + /// + /// A helper method to raise the TaskbarIconToolTipOpen event. + /// + protected RoutedEventArgs RaiseTaskbarIconToolTipOpenEvent() + { + return RaiseTaskbarIconToolTipOpenEvent(this); + } + + /// + /// A static helper method to raise the TaskbarIconToolTipOpen event on a target element. + /// + /// UIElement or ContentElement on which to raise the event + internal static RoutedEventArgs RaiseTaskbarIconToolTipOpenEvent(DependencyObject target) + { + if (target == null) return null; + + RoutedEventArgs args = new RoutedEventArgs(); + args.RoutedEvent = TaskbarIconToolTipOpenEvent; + RoutedEventHelper.RaiseEvent(target, args); + return args; + } + + /// + /// PreviewTaskbarIconToolTipOpen Routed Event + /// + public static readonly RoutedEvent PreviewTaskbarIconToolTipOpenEvent = EventManager.RegisterRoutedEvent("PreviewTaskbarIconToolTipOpen", + RoutingStrategy.Tunnel, typeof(RoutedEventHandler), typeof(TaskbarIcon)); + + /// + /// Tunneled event that occurs when the custom ToolTip is being displayed. + /// + public event RoutedEventHandler PreviewTaskbarIconToolTipOpen + { + add { AddHandler(PreviewTaskbarIconToolTipOpenEvent, value); } + remove { RemoveHandler(PreviewTaskbarIconToolTipOpenEvent, value); } + } + + /// + /// A helper method to raise the PreviewTaskbarIconToolTipOpen event. + /// + protected RoutedEventArgs RaisePreviewTaskbarIconToolTipOpenEvent() + { + return RaisePreviewTaskbarIconToolTipOpenEvent(this); + } + + /// + /// A static helper method to raise the PreviewTaskbarIconToolTipOpen event on a target element. + /// + /// UIElement or ContentElement on which to raise the event + internal static RoutedEventArgs RaisePreviewTaskbarIconToolTipOpenEvent(DependencyObject target) + { + if (target == null) return null; + + RoutedEventArgs args = new RoutedEventArgs(); + args.RoutedEvent = PreviewTaskbarIconToolTipOpenEvent; + RoutedEventHelper.RaiseEvent(target, args); + return args; + } + + #endregion + + #region TaskbarIconContextMenuOpen (and PreviewTaskbarIconContextMenuOpen) + + /// + /// TaskbarIconContextMenuOpen Routed Event + /// + public static readonly RoutedEvent TaskbarIconContextMenuOpenEvent = EventManager.RegisterRoutedEvent("TaskbarIconContextMenuOpen", + RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(TaskbarIcon)); + + /// + /// Bubbled event that occurs when the context menu of the taskbar icon is being displayed. + /// + public event RoutedEventHandler TaskbarIconContextMenuOpen + { + add { AddHandler(TaskbarIconContextMenuOpenEvent, value); } + remove { RemoveHandler(TaskbarIconContextMenuOpenEvent, value); } + } + + /// + /// A helper method to raise the TaskbarIconContextMenuOpen event. + /// + protected RoutedEventArgs RaiseTaskbarIconContextMenuOpenEvent() + { + return RaiseTaskbarIconContextMenuOpenEvent(this); + } + + /// + /// A static helper method to raise the TaskbarIconContextMenuOpen event on a target element. + /// + /// UIElement or ContentElement on which to raise the event + internal static RoutedEventArgs RaiseTaskbarIconContextMenuOpenEvent(DependencyObject target) + { + if (target == null) return null; + + RoutedEventArgs args = new RoutedEventArgs(); + args.RoutedEvent = TaskbarIconContextMenuOpenEvent; + RoutedEventHelper.RaiseEvent(target, args); + return args; + } + + /// + /// PreviewTaskbarIconContextMenuOpen Routed Event + /// + public static readonly RoutedEvent PreviewTaskbarIconContextMenuOpenEvent = EventManager.RegisterRoutedEvent("PreviewTaskbarIconContextMenuOpen", + RoutingStrategy.Tunnel, typeof(RoutedEventHandler), typeof(TaskbarIcon)); + + /// + /// Tunneled event that occurs when the context menu of the taskbar icon is being displayed. + /// + public event RoutedEventHandler PreviewTaskbarIconContextMenuOpen + { + add { AddHandler(PreviewTaskbarIconContextMenuOpenEvent, value); } + remove { RemoveHandler(PreviewTaskbarIconContextMenuOpenEvent, value); } + } + + /// + /// A helper method to raise the PreviewTaskbarIconContextMenuOpen event. + /// + protected RoutedEventArgs RaisePreviewTaskbarIconContextMenuOpenEvent() + { + return RaisePreviewTaskbarIconContextMenuOpenEvent(this); + } + + /// + /// A static helper method to raise the PreviewTaskbarIconContextMenuOpen event on a target element. + /// + /// UIElement or ContentElement on which to raise the event + internal static RoutedEventArgs RaisePreviewTaskbarIconContextMenuOpenEvent(DependencyObject target) + { + if (target == null) return null; + + RoutedEventArgs args = new RoutedEventArgs(); + args.RoutedEvent = PreviewTaskbarIconContextMenuOpenEvent; + RoutedEventHelper.RaiseEvent(target, args); + return args; + } + + #endregion + + + + + //CONSTRUCTOR DECLARATIONS + + /// + /// Registers properties. + /// + static TaskbarIcon() + { + //register change listener for the Visibility property + PropertyMetadata md = new PropertyMetadata(Visibility.Visible, VisibilityPropertyChanged); + VisibilityProperty.OverrideMetadata(typeof(TaskbarIcon), md); + + //register change listener for the ContextMenu property + md = new FrameworkPropertyMetadata(new PropertyChangedCallback(ContextMenuPropertyChanged)); + ContextMenuProperty.OverrideMetadata(typeof (TaskbarIcon), md); + + //register change listener for the ToolTip property + md = new FrameworkPropertyMetadata(new PropertyChangedCallback(ToolTipPropertyChanged)); + ToolTipProperty.OverrideMetadata(typeof(TaskbarIcon), md); + + } + } +} \ No newline at end of file diff --git a/Source/NotifyIconWpf/TaskbarIcon.Interop.cs b/Source/NotifyIconWpf/TaskbarIcon.Interop.cs new file mode 100644 index 0000000..4921a97 --- /dev/null +++ b/Source/NotifyIconWpf/TaskbarIcon.Interop.cs @@ -0,0 +1,250 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Threading; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Controls.Primitives; +using Hardcodet.Wpf.TaskbarNotification.Interop; + +namespace Hardcodet.Wpf.TaskbarNotification +{ + partial class TaskbarIcon + { + /// + /// An action that is being invoked if the + /// fires. + /// + private Action delayedTimerAction; + + /// + /// A timer that is used to differentiate between single + /// and double clicks. + /// + private readonly Timer singleClickTimer; + + + #region SetVersion + + /// + /// Sets the version flag for the . + /// + private void SetVersion() + { + iconData.VersionOrTimeout = (uint) NotifyIconVersion.Vista; + bool status = WinApi.Shell_NotifyIcon(NotifyCommand.SetVersion, ref iconData); + + if (!status) + { + iconData.VersionOrTimeout = (uint) NotifyIconVersion.Win2000; + status = Util.WriteIconData(ref iconData, NotifyCommand.SetVersion); + } + + if (!status) + { + iconData.VersionOrTimeout = (uint)NotifyIconVersion.Win95; + status = Util.WriteIconData(ref iconData, NotifyCommand.SetVersion); + } + + if (!status) + { + Debug.Fail("Could not set version"); + } + } + + #endregion + + + /// + /// Sets tooltip settings for the class. + /// + private void WriteToolTipSettings() + { + IconDataMembers flags = IconDataMembers.Tip; + iconData.ToolTipText = ToolTipText; + + if (messageSink.Version == NotifyIconVersion.Vista) + { + if (String.IsNullOrEmpty(ToolTipText) && ToolTip != null) + { + //if we have not tooltip text but a custom tooltip, we + //need to set a dummy value + iconData.ToolTipText = "ToolTip"; + } + else if (!String.IsNullOrEmpty(ToolTipText) && ToolTip == null) + { + //if a tooltip text was set but there is no custom tooltip, + //we need to fall back to legacy operations + flags |= IconDataMembers.UseLegacyToolTips; + } + } + + //just write the the tooltip + Util.WriteIconData(ref iconData, NotifyCommand.Modify, flags); + } + + + #region Show / Hide Balloon ToolTip + + /// + /// Displays a balloon tip with the specified title, + /// text, and icon in the taskbar for the specified time period. + /// + /// The title to display on the balloon tip. + /// The text to display on the balloon tip. + /// Indicates the severity. + public void ShowBalloonTip(string title, string message, BalloonIcon icon) + { + lock(this) + { + ShowBalloonTip(title, message, icon.GetBalloonFlag(), IntPtr.Zero); + } + } + + + /// + /// Displays a balloon tip with the specified title, + /// text, and a custom icon in the taskbar for the specified time period. + /// + /// The title to display on the balloon tip. + /// The text to display on the balloon tip. + /// A custom icon. + /// If + /// is a null reference. + public void ShowBalloonTip(string title, string message, Icon customIcon) + { + if (customIcon == null) throw new ArgumentNullException("customIcon"); + + lock(this) + { + ShowBalloonTip(title, message, BalloonFlags.User, customIcon.Handle); + } + } + + + + /// + /// Invokes in order to display + /// a given balloon ToolTip. + /// + /// The title to display on the balloon tip. + /// The text to display on the balloon tip. + /// Indicates what icon to use. + /// A handle to a custom icon, if any, or + /// . + private void ShowBalloonTip(string title, string message, BalloonFlags flags, IntPtr balloonIconHandle) + { + EnsureNotDisposed(); + + iconData.BalloonText = message; + iconData.BalloonTitle = title; + + iconData.BalloonFlags = flags; + iconData.CustomBalloonIconHandle = balloonIconHandle; + Util.WriteIconData(ref iconData, NotifyCommand.Modify, IconDataMembers.Info); + } + + + + /// + /// Hides a balloon ToolTip, if any is displayed. + /// + public void HideBalloonTip() + { + EnsureNotDisposed(); + + //reset balloon by just setting the info to an empty string + iconData.BalloonText = iconData.BalloonTitle = String.Empty; + Util.WriteIconData(ref iconData, NotifyCommand.Modify, IconDataMembers.Info); + } + + #endregion + + + #region Single Click Timer event + + /// + /// Performs a delayed action if the user requested an action + /// based on a single click of the left mouse.
+ /// This method is invoked by the . + ///
+ private void DoSingleClickAction(object state) + { + if (IsDisposed) return; + + //run action + Action action = delayedTimerAction; + if (action != null) + { + //cleanup action + delayedTimerAction = null; + + //switch to UI thread + Application.Current.Dispatcher.Invoke(action); + } + } + + #endregion + + + #region Show Tray Popup / Context Menu + + /// + /// Displays the control if + /// it was set. + /// + private void ShowTrayPopup() + { + if (IsDisposed) return; + + if (TaskbarIconPopup != null) + { + //raise preview event + var args = RaisePreviewTaskbarIconPopupOpenEvent(); + if (args.Handled) return; + + //open popup + TaskbarIconPopup.IsOpen = true; + + //activate the message window to track deactivation - otherwise, the context menu + //does not close if the user clicks somewhere else + WinApi.SetForegroundWindow(messageSink.MessageWindowHandle); + + //bubble event + RaiseTaskbarIconPopupOpenEvent(); + } + } + + + /// + /// Displays the if + /// it was set. + /// + private void ShowContextMenu() + { + if (IsDisposed) return; + + if (ContextMenu != null) + { + //raise preview event + var args = RaisePreviewTaskbarIconContextMenuOpenEvent(); + if (args.Handled) return; + + //CreateActivationWindow(); + ContextMenu.IsOpen = true; + + //activate the message window to track deactivation - otherwise, the context menu + //does not close if the user clicks somewhere else + WinApi.SetForegroundWindow(messageSink.MessageWindowHandle); + + //bubble event + RaiseTaskbarIconContextMenuOpenEvent(); + } + } + + #endregion + } +} diff --git a/Source/NotifyIconWpf/TaskbarIcon.cs b/Source/NotifyIconWpf/TaskbarIcon.cs new file mode 100644 index 0000000..d13005a --- /dev/null +++ b/Source/NotifyIconWpf/TaskbarIcon.cs @@ -0,0 +1,379 @@ +using System; +using System.ComponentModel; +using System.Threading; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Controls.Primitives; +using System.Windows.Media; +using Hardcodet.Wpf.TaskbarNotification.Interop; +using Rect=Hardcodet.Wpf.TaskbarNotification.Interop.Rect; + +namespace Hardcodet.Wpf.TaskbarNotification +{ + internal class MyClass + { + public void Test() + { + TaskbarIcon icon = new TaskbarIcon(); + icon.Icon = Properties.Resources.DefaultTrayIcon; + Console.Out.WriteLine("DISPLAY NOW..."); + Thread.CurrentThread.Join(1500); + //icon.ShowBalloonTip("some title", "hello world", Properties.Resources.DefaultTrayIcon); + //Console.Out.WriteLine("status = {0}", status); + Thread.CurrentThread.Join(5000); + } + + public void Test2() + { + var tbInfo = TrayLocator.GetTaskbarInformation(); + var w = new Window(); + w.Background = Brushes.Red; + w.WindowStyle = WindowStyle.None; + + Rect rect = tbInfo.Rectangle; + w.Width = Math.Max(20, rect.right - rect.left); + w.Height = Math.Max(20, rect.bottom - rect.top); + w.Left = rect.left; + w.Top = rect.top - 100; + w.ShowDialog(); + } + } + + /// + /// Represent a taskbar icon that sits in the system + /// tray. + /// + public partial class TaskbarIcon : FrameworkElement, IDisposable + { + /// + /// Represents the current icon data. + /// + private NotifyIconData iconData; + + /// + /// Receives messages from the taskbar icon. + /// + private readonly WindowMessageSink messageSink; + + /// + /// Indicates whether the taskbar icon has been created or not. + /// + public bool IsTaskbarIconCreated { get; set; } + + + /// + /// Indicates whether custom tooltips are supported. + /// + public bool SupportsCustomToolTips + { + get { return messageSink.Version == NotifyIconVersion.Vista; } + } + + + /// + /// Inits the taskbar icon and registers a message listener + /// in order to receive events from the taskbar area. + /// + public TaskbarIcon() + { + //do nothing if in design mode + if (Util.IsDesignMode) + { + messageSink = WindowMessageSink.CreateEmpty(); + } + else + { + //create message sink that receives window messages + messageSink = new WindowMessageSink(NotifyIconVersion.Win95); + } + + //init icon data structure + iconData = NotifyIconData.CreateDefault(messageSink.MessageWindowHandle); + + //create the taskbar icon + CreateTaskbarIcon(); + + //register event listeners + messageSink.MouseEventReceived += OnMouseEvent; + messageSink.TaskbarCreated += OnTaskbarCreated; + messageSink.ChangeToolTipStateRequest += OnToolTipChange; + messageSink.BallonToolTipChanged += OnBalloonToolTipChanged; + + //init single click timer + singleClickTimer = new Timer(DoSingleClickAction); + + + //register listener in order to get notified when the application closes + if (Application.Current != null) Application.Current.Exit += OnExit; + + } + + + + /// + /// Processes mouse events, which are bubbled + /// through the class' routed events, trigger + /// certain actions (e.g. show a popup), or + /// both. + /// + /// Event flag. + private void OnMouseEvent(MouseEvent me) + { + if (IsDisposed) return; + + switch(me) + { + case MouseEvent.MouseMove: + break; + case MouseEvent.IconRightMouseDown: + RaiseTaskbarIconRightMouseDownEvent(); + break; + case MouseEvent.IconLeftMouseDown: + RaiseTaskbarIconLeftMouseDownEvent(); + break; + case MouseEvent.IconRightMouseUp: + RaiseTaskbarIconRightMouseUpEvent(); + break; + case MouseEvent.IconLeftMouseUp: + RaiseTaskbarIconLeftMouseUpEvent(); + break; + case MouseEvent.IconMiddleMouseDown: + break; + case MouseEvent.IconMiddleMouseUp: + break; + case MouseEvent.IconDoubleClick: + + //cancel single click timer + singleClickTimer.Change(Timeout.Infinite, Timeout.Infinite); + + break; + case MouseEvent.BalloonToolTipClicked: + break; + default: + throw new ArgumentOutOfRangeException("me", "Missing handler for mouse event flag: " + me); + + } + + + //show popup, if requested + if (me.IsMatch(PopupActivation)) + { + if (me == MouseEvent.IconLeftMouseUp) + { + //show popup once we are sure it's not a double click + delayedTimerAction = ShowTrayPopup; + singleClickTimer.Change(WinApi.GetDoubleClickTime(), Timeout.Infinite); + } + else + { + //show popup immediately + ShowTrayPopup(); + } + } + + + //show context menu, if requested + if (me.IsMatch(MenuActivation)) + { + if (me == MouseEvent.IconLeftMouseUp) + { + //show context menu once we are sure it's not a double click + delayedTimerAction = ShowContextMenu; + singleClickTimer.Change(WinApi.GetDoubleClickTime(), Timeout.Infinite); + } + else + { + //show context menu immediately + ShowContextMenu(); + } + } + } + + + + private void OnToolTipChange(bool visible) + { + //if we have a custom tooltip, show it now + if (ToolTip == null) return; + + ToolTip tt = (ToolTip)ToolTip; + tt.IsOpen = visible; + } + + + private void OnBalloonToolTipChanged(bool visible) + { + //TODO just raise event + } + + + + /// + /// Recreates the taskbar icon if the whole taskbar was + /// recreated (e.g. because Explorer was shut down). + /// + private void OnTaskbarCreated() + { + IsTaskbarIconCreated = false; + CreateTaskbarIcon(); + } + + + + + + + #region create / remove taskbar icon + + /// + /// Creates the taskbar icon. This message is invoked during initialization, + /// if the taskbar is restarted, and whenever the icon is displayed. + /// + private void CreateTaskbarIcon() + { + lock (this) + { + if (!IsTaskbarIconCreated) + { + const IconDataMembers members = IconDataMembers.Message + | IconDataMembers.Icon + | IconDataMembers.Tip; + + //write initial configuration + var status = Util.WriteIconData(ref iconData, NotifyCommand.Add, members); + if (!status) + { + throw new Win32Exception("Could not create icon data"); + } + + //set to most recent version + SetVersion(); + messageSink.Version = (NotifyIconVersion) iconData.VersionOrTimeout; + + IsTaskbarIconCreated = true; + } + } + } + + + /// + /// Closes the taskbar icon if required. + /// + private void RemoveTaskbarIcon() + { + lock (this) + { + if (IsTaskbarIconCreated) + { + Util.WriteIconData(ref iconData, NotifyCommand.Delete, IconDataMembers.Message); + IsTaskbarIconCreated = false; + } + } + } + + #endregion + + + #region Dispose / Exit + + /// + /// Set to true as soon as + /// has been invoked. + /// + public bool IsDisposed { get; private set; } + + + /// + /// Checks if the object has been disposed and + /// raises a in case + /// the flag is true. + /// + private void EnsureNotDisposed() + { + if (IsDisposed) throw new ObjectDisposedException(Name ?? GetType().FullName); + } + + + /// + /// Disposes the class if the application exits. + /// + private void OnExit(object sender, EventArgs e) + { + Dispose(); + } + + + /// + /// This destructor will run only if the + /// method does not get called. This gives this base class the + /// opportunity to finalize. + /// + /// Important: Do not provide destructors in types derived from + /// this class. + /// + /// + ~TaskbarIcon() + { + Dispose(false); + } + + + /// + /// Disposes the object. + /// + /// This method is not virtual by design. Derived classes + /// should override . + /// + public void Dispose() + { + Dispose(true); + + // This object will be cleaned up by the Dispose method. + // Therefore, you should call GC.SupressFinalize to + // take this object off the finalization queue + // and prevent finalization code for this object + // from executing a second time. + GC.SuppressFinalize(this); + } + + + /// + /// Closes the tray and releases all resources. + /// + /// + /// Dispose(bool disposing) executes in two distinct scenarios. + /// If disposing equals true, the method has been called directly + /// or indirectly by a user's code. Managed and unmanaged resources + /// can be disposed. + /// + /// If disposing equals false, the method + /// has been called by the runtime from inside the finalizer and you + /// should not reference other objects. Only unmanaged resources can + /// be disposed. + /// Check the property to determine whether + /// the method has already been called. + private void Dispose(bool disposing) + { + //don't do anything if the component is already disposed + if (IsDisposed || !disposing) return; + + lock (this) + { + IsDisposed = true; + + //deregister application event listener + Application.Current.Exit -= OnExit; + + //stop timer + singleClickTimer.Dispose(); + + //dispose message sink + messageSink.Dispose(); + + RemoveTaskbarIcon(); + } + } + + #endregion + } +} diff --git a/Source/NotifyIconWpf/Util.cs b/Source/NotifyIconWpf/Util.cs new file mode 100644 index 0000000..55e773d --- /dev/null +++ b/Source/NotifyIconWpf/Util.cs @@ -0,0 +1,225 @@ +using System; +using System.ComponentModel; +using System.Drawing; +using System.Windows; +using System.Windows.Media; +using System.Windows.Resources; +using Hardcodet.Wpf.TaskbarNotification.Interop; + +namespace Hardcodet.Wpf.TaskbarNotification +{ + /// + /// Util and extension methods. + /// + internal static class Util + { + public static readonly object SyncRoot = new object(); + + + #region IsDesignMode + + private static readonly bool isDesignMode; + + /// + /// Checks whether the application is currently in design mode. + /// + public static bool IsDesignMode + { + get { return isDesignMode; } + } + + #endregion + + + #region construction + + static Util() + { + isDesignMode = + (bool)DependencyPropertyDescriptor.FromProperty(DesignerProperties.IsInDesignModeProperty, typeof(FrameworkElement)) + .Metadata.DefaultValue; + } + + #endregion + + + #region CreateHelperWindow + + /// + /// Creates an transparent window without dimension that + /// can be used to temporarily obtain focus and/or + /// be used as a window message sink. + /// + /// Empty window. + public static Window CreateHelperWindow() + { + return new Window + { + Width = 0, + Height = 0, + ShowInTaskbar = false, + WindowStyle = WindowStyle.None, + AllowsTransparency = true, + Opacity = 0 + }; + } + + #endregion + + + #region WriteIconData + + /// + /// Updates the taskbar icons with data provided by a given + /// instance. + /// + /// + /// + /// + public static bool WriteIconData(ref NotifyIconData data, NotifyCommand command) + { + return WriteIconData(ref data, command, data.ValidMembers); + } + + + /// + /// Updates the taskbar icons with data provided by a given + /// instance. + /// + /// + /// + /// + /// + public static bool WriteIconData(ref NotifyIconData data, NotifyCommand command, IconDataMembers flags) + { + //do nothing if in design mode + if (IsDesignMode) return true; + + data.ValidMembers = flags; + lock (SyncRoot) + { + return WinApi.Shell_NotifyIcon(command, ref data); + } + } + + #endregion + + + #region GetBalloonFlag + + /// + /// Gets a enum value that + /// matches a given . + /// + public static BalloonFlags GetBalloonFlag(this BalloonIcon icon) + { + switch (icon) + { + case BalloonIcon.None: + return BalloonFlags.None; + case BalloonIcon.Info: + return BalloonFlags.Info; + case BalloonIcon.Warning: + return BalloonFlags.Warning; + case BalloonIcon.Error: + return BalloonFlags.Error; + default: + throw new ArgumentOutOfRangeException("icon"); + } + } + + #endregion + + + #region ImageSource to Icon + + /// + /// Reads a given image resource into a WinForms icon. + /// + /// Image source pointing to + /// an icon file (*.ico). + /// An icon object that can be used with the + /// taskbar area. + public static Icon ToIcon(this ImageSource imageSource) + { + if (imageSource == null) return null; + + Uri uri = new Uri(imageSource.ToString()); + StreamResourceInfo streamInfo = Application.GetResourceStream(uri); + + if (streamInfo == null) + { + string msg = "The supplied image source '{0}' could not be resolved."; + msg = String.Format(msg, imageSource); + throw new ArgumentException(msg); + } + + return new Icon(streamInfo.Stream); + } + + #endregion + + + #region evaluate listings + + /// + /// Checks a list of candidates for equality to a given + /// reference value. + /// + /// + /// The evaluated value. + /// A liste of possible values that are + /// regarded valid. + /// True if one of the submitted + /// matches the evaluated value. If the + /// parameter itself is null, too, the method returns false as well, + /// which allows to check with null values, too. + /// If + /// is a null reference. + public static bool Is(this T value, params T[] candidates) + { + if (candidates == null) return false; + + foreach (var t in candidates) + { + if (value.Equals(t)) return true; + } + + return false; + } + + #endregion + + + #region match MouseEvent to PopupActivation + + /// + /// Checks if a given is a match for + /// an effectively pressed mouse button. + /// + public static bool IsMatch(this MouseEvent me, PopupActivationMode activationMode) + { + switch (activationMode) + { + case PopupActivationMode.LeftClick: + return me == MouseEvent.IconLeftMouseUp; + case PopupActivationMode.RightClick: + return me == MouseEvent.IconRightMouseUp; + case PopupActivationMode.LeftOrRightClick: + return me.Is(MouseEvent.IconLeftMouseUp, MouseEvent.IconRightMouseUp); + case PopupActivationMode.LeftOrDoubleClick: + return me.Is(MouseEvent.IconLeftMouseUp, MouseEvent.IconDoubleClick); + case PopupActivationMode.DoubleClick: + return me.Is(MouseEvent.IconDoubleClick); + case PopupActivationMode.MiddleClick: + return me == MouseEvent.IconMiddleMouseUp; + case PopupActivationMode.All: + return true; + default: + throw new ArgumentOutOfRangeException("activationMode"); + } + } + + #endregion + } +} diff --git a/Source/Sample Project/App.xaml b/Source/Sample Project/App.xaml new file mode 100644 index 0000000..0c9c882 --- /dev/null +++ b/Source/Sample Project/App.xaml @@ -0,0 +1,8 @@ + + + + + diff --git a/Source/Sample Project/App.xaml.cs b/Source/Sample Project/App.xaml.cs new file mode 100644 index 0000000..92b661a --- /dev/null +++ b/Source/Sample Project/App.xaml.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Configuration; +using System.Data; +using System.Linq; +using System.Windows; + +namespace Sample_Project +{ + /// + /// Interaction logic for App.xaml + /// + public partial class App : Application + { + } +} diff --git a/Source/Sample Project/Icons/Bulb.ico b/Source/Sample Project/Icons/Bulb.ico new file mode 100644 index 0000000000000000000000000000000000000000..e3ff3c4bf60174f7cc6140efc302f7098ae911fc GIT binary patch literal 1150 zcmZQzU<5(|0R|wcz>vYhz#zuJz@P!dKp~(AL>x$w1a$TERC983*Voq8Zn3qs)g?tU zCpWjSlZ)G?#N^cfwRLs>laiAU35$rx5o-pYfPk)xhv)0k%Ig0cHg5Xg)7$q!R7}j2 zSpDJ>68b^GA#ZbX^8PnBH~*hJY0@V(H8o3O^;;S1>%>QePs>V8Sec)dvZ^pUW4fc2 znK7~Y&jq(p@p5TifL-HtaeCEjKBj2Twl4W)iS958p* z46CxdEXNo>cYZ=<#0UErw>LGotX#I#a^duT&$WwZ2XEiJ(QVq~NluyZk%rz@hK#t( z2z9p?%TG)6S+aPs?T&3*y|!&!nYefRy4>SO4khi_zRk0$q|iAw$XgVb{wP0p^M=}L zk2R}TyB<5VzvTGgy$weX?y5U^^l<5cy}OgQZ{3p6*WO}}O}_#s17l8docFGsJ2K|Z zo$I<~!@7(;+cq_9-?YAH|BkJ#ySHrUx^?~9!i}p}W&`zO^+Rn*e!#i2XS%0Mnc_Wt z>eRrEtCqFyShsTV)-}slZd2l!QxvW<-EUOKI-h>D{fXmd>5oym{@a#*ORO zWp7wAf9;~lJ$IXmGPflMdCKC_4-(Ib4$`g5PoH;i*Y-1y9y~bt^vRP0XO11d(o~$Y zI@;5os63G1=PEI0Qcv=sJv*lzJGgJg%7t^%!rbj8vDy2_evRV~c})C)e~se@_BDV6vwa0qS=RKell^%l6~8Z8WhHh z(?u6GI!Fm(2gs;}wiJ*Ty#;!2OK;m-DB4n)w=cAX_SV+Y7WVD$skj7%otyvjJUKc4 zb54GjVHVN1bSXpU8_fQf7-kv6FmI74MVZ+=UH=zLwr|_^cB|&r!G0rqUa!~H^!D~N z3|jc6J|kP-+1XLurqfm^|Gc()>y|HGTd2ujD%H!;X!P|^DDs{w^x%faJ0ISt{ zU6Q1=kx1m7a459=?*|y$ZCK z;yFKOVm;*wM9SYoq+%r^=khRdVKpW%y^rWs1!7g}A=i9_c-wsop}Q*`rAJ|A-9#6CPOA@cSf>#mor~n;_V1wkn?E*oeLesm{Yl zbsxpl-6EuoKOM_xO1u=6q#U-K)sXml$l3!)G<}Y7bpb|cevZL^;dZ+}u&6H~R=XLpY762G z#ItD!66#$@wh&Jpv1}_Kp1Uz&*tekHs=G$~_aM>oE!FR(+I__4Af~#1K>E&Mr2CE$ znI5o%JN@gni{&WkbebtN6UImD%K}7v*XSdHawzdCq3<~ p{NVOe#k`$tPUH7FSepL_^Qz{gg+0 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 literal 0 HcmV?d00001 diff --git a/Source/Sample Project/Properties/AssemblyInfo.cs b/Source/Sample Project/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..c10547a --- /dev/null +++ b/Source/Sample Project/Properties/AssemblyInfo.cs @@ -0,0 +1,55 @@ +using System.Reflection; +using System.Resources; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Windows; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Sample Project")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Microsoft")] +[assembly: AssemblyProduct("Sample Project")] +[assembly: AssemblyCopyright("Copyright © Microsoft 2009")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +//In order to begin building localizable applications, set +//CultureYouAreCodingWith in your .csproj file +//inside a . For example, if you are using US english +//in your source files, set the to en-US. Then uncomment +//the NeutralResourceLanguage attribute below. Update the "en-US" in +//the line below to match the UICulture setting in the project file. + +//[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)] + + +[assembly: ThemeInfo( + ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located + //(used if a resource is not found in the page, + // or application resource dictionaries) + ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located + //(used if a resource is not found in the page, + // app, or any theme specific resource dictionaries) +)] + + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Source/Sample Project/Properties/Resources.Designer.cs b/Source/Sample Project/Properties/Resources.Designer.cs new file mode 100644 index 0000000..1646b08 --- /dev/null +++ b/Source/Sample Project/Properties/Resources.Designer.cs @@ -0,0 +1,84 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:2.0.50727.3053 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Sample_Project.Properties { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "2.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Sample_Project.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + internal static System.Drawing.Icon Bulb { + get { + object obj = ResourceManager.GetObject("Bulb", resourceCulture); + return ((System.Drawing.Icon)(obj)); + } + } + + internal static System.Drawing.Icon Computers { + get { + object obj = ResourceManager.GetObject("Computers", resourceCulture); + return ((System.Drawing.Icon)(obj)); + } + } + + internal static System.Drawing.Icon NetDrives { + get { + object obj = ResourceManager.GetObject("NetDrives", resourceCulture); + return ((System.Drawing.Icon)(obj)); + } + } + } +} diff --git a/Source/Sample Project/Properties/Resources.resx b/Source/Sample Project/Properties/Resources.resx new file mode 100644 index 0000000..98e1ee3 --- /dev/null +++ b/Source/Sample Project/Properties/Resources.resx @@ -0,0 +1,130 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + ..\icons\bulb.ico;System.Drawing.Icon, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\icons\computers.ico;System.Drawing.Icon, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\icons\netdrives.ico;System.Drawing.Icon, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + \ No newline at end of file diff --git a/Source/Sample Project/Properties/Settings.Designer.cs b/Source/Sample Project/Properties/Settings.Designer.cs new file mode 100644 index 0000000..4a1169a --- /dev/null +++ b/Source/Sample Project/Properties/Settings.Designer.cs @@ -0,0 +1,30 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:2.0.50727.3053 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Sample_Project.Properties +{ + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "9.0.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase + { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default + { + get + { + return defaultInstance; + } + } + } +} diff --git a/Source/Sample Project/Properties/Settings.settings b/Source/Sample Project/Properties/Settings.settings new file mode 100644 index 0000000..8f2fd95 --- /dev/null +++ b/Source/Sample Project/Properties/Settings.settings @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/Source/Sample Project/Sample Project.csproj b/Source/Sample Project/Sample Project.csproj new file mode 100644 index 0000000..6a3b392 --- /dev/null +++ b/Source/Sample Project/Sample Project.csproj @@ -0,0 +1,121 @@ + + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {71C74F29-F1C2-49C5-969F-C25AC4CDFCCC} + WinExe + Properties + Sample_Project + Sample Project + v3.5 + 512 + {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + 4 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + 3.5 + + + + 3.5 + + + 3.5 + + + + + + + + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + App.xaml + Code + + + Window1.xaml + Code + + + + + Code + + + True + True + Resources.resx + + + True + Settings.settings + True + + + ResXFileCodeGenerator + Resources.Designer.cs + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + + + + {7AC63864-7638-41C4-969C-D3197EF2BED9} + NotifyIconWpf + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Source/Sample Project/Window1.xaml b/Source/Sample Project/Window1.xaml new file mode 100644 index 0000000..8c449cf --- /dev/null +++ b/Source/Sample Project/Window1.xaml @@ -0,0 +1,147 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Source/Sample Project/Window1.xaml.cs b/Source/Sample Project/Window1.xaml.cs new file mode 100644 index 0000000..639bfe1 --- /dev/null +++ b/Source/Sample Project/Window1.xaml.cs @@ -0,0 +1,43 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; + +namespace Sample_Project +{ + /// + /// Interaction logic for Window1.xaml + /// + public partial class Window1 : Window + { + public Window1() + { + InitializeComponent(); + + //set icon in code - setting the IconSource in XAML + //works just fine but breaks the VS designer + tb.Icon = Properties.Resources.Computers; + } + + private void OnClick(object sender, RoutedEventArgs e) + { + if (tb.Visibility == System.Windows.Visibility.Visible) + { + tb.Visibility = System.Windows.Visibility.Collapsed; + } + else + { + tb.Visibility = Visibility.Visible; + } + } + } +}