From 354ba1ca43a492594ed5196dd89179d427c6f0a6 Mon Sep 17 00:00:00 2001 From: Philipp Sumi Date: Tue, 31 Mar 2009 22:20:07 +0000 Subject: [PATCH] NotifyWPF --------- CHG Lot of plumbing, some fixes CHG Work on sample project. CHG Popups and ContextMenu now store coordinates - otherwise delayed opending may happen elsewhere. git-svn-id: https://svn.evolvesoftware.ch/repos/evolve.net/WPF/NotifyIcon@55 9f600761-6f11-4665-b6dc-0185e9171623 --- Source/NotifyIconWpf/Interop/Point.cs | 14 + Source/NotifyIconWpf/Interop/WinApi.cs | 9 +- .../Interop/WindowMessageSink.cs | 11 +- Source/NotifyIconWpf/NotifyIconWpf.csproj | 1 + .../NotifyIconWpf/TaskbarIcon.Declarations.cs | 252 ++++---- Source/NotifyIconWpf/TaskbarIcon.Interop.cs | 197 ++++++- Source/NotifyIconWpf/TaskbarIcon.cs | 63 +- Source/NotifyIconWpf/Util.cs | 3 +- Source/Sample Project/App.xaml | 6 + .../Commands/HideMainWindowCommand.cs | 39 ++ .../Commands/ShowMainWindowCommand.cs | 38 ++ .../Commands/TaskbarIconCommands.cs | 27 + Source/Sample Project/FancyPopup.xaml | 92 +++ Source/Sample Project/FancyPopup.xaml.cs | 46 ++ Source/Sample Project/FancyToolTip.xaml | 68 +++ Source/Sample Project/FancyToolTip.xaml.cs | 80 +++ Source/Sample Project/Icons/Error.ico | Bin 0 -> 1150 bytes Source/Sample Project/Icons/Inactive.ico | Bin 0 -> 1150 bytes Source/Sample Project/Images/Add.png | Bin 0 -> 502 bytes Source/Sample Project/Images/Info.png | Bin 0 -> 7030 bytes Source/Sample Project/Images/Remove.png | Bin 0 -> 237 bytes Source/Sample Project/Images/preferences.png | Bin 0 -> 7825 bytes Source/Sample Project/Sample Project.csproj | 51 +- .../Sample Project/TaskbarIconResources.xaml | 142 +++++ Source/Sample Project/Window1.xaml | 541 ++++++++++++++---- Source/Sample Project/Window1.xaml.cs | 7 +- 26 files changed, 1379 insertions(+), 308 deletions(-) create mode 100644 Source/NotifyIconWpf/Interop/Point.cs create mode 100644 Source/Sample Project/Commands/HideMainWindowCommand.cs create mode 100644 Source/Sample Project/Commands/ShowMainWindowCommand.cs create mode 100644 Source/Sample Project/Commands/TaskbarIconCommands.cs create mode 100644 Source/Sample Project/FancyPopup.xaml create mode 100644 Source/Sample Project/FancyPopup.xaml.cs create mode 100644 Source/Sample Project/FancyToolTip.xaml create mode 100644 Source/Sample Project/FancyToolTip.xaml.cs create mode 100644 Source/Sample Project/Icons/Error.ico create mode 100644 Source/Sample Project/Icons/Inactive.ico create mode 100644 Source/Sample Project/Images/Add.png create mode 100644 Source/Sample Project/Images/Info.png create mode 100644 Source/Sample Project/Images/Remove.png create mode 100644 Source/Sample Project/Images/preferences.png create mode 100644 Source/Sample Project/TaskbarIconResources.xaml diff --git a/Source/NotifyIconWpf/Interop/Point.cs b/Source/NotifyIconWpf/Interop/Point.cs new file mode 100644 index 0000000..219adcb --- /dev/null +++ b/Source/NotifyIconWpf/Interop/Point.cs @@ -0,0 +1,14 @@ +using System.Runtime.InteropServices; + +namespace Hardcodet.Wpf.TaskbarNotification.Interop +{ + /// + /// Win API struct providing coordinates for a single point. + /// + [StructLayout(LayoutKind.Sequential)] + public struct Point + { + public int X; + public int Y; + } +} \ No newline at end of file diff --git a/Source/NotifyIconWpf/Interop/WinApi.cs b/Source/NotifyIconWpf/Interop/WinApi.cs index 7df8f20..e0d61ba 100644 --- a/Source/NotifyIconWpf/Interop/WinApi.cs +++ b/Source/NotifyIconWpf/Interop/WinApi.cs @@ -74,7 +74,14 @@ namespace Hardcodet.Wpf.TaskbarNotification.Interop /// consider the mouse action a double-click. [DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)] public static extern int GetDoubleClickTime(); - + + /// + /// Gets the screen coordinates of the current mouse position. + /// + /// + /// + [DllImport("USER32.DLL", SetLastError = true)] + public static extern bool GetCursorPos(ref Point lpPoint); } } \ No newline at end of file diff --git a/Source/NotifyIconWpf/Interop/WindowMessageSink.cs b/Source/NotifyIconWpf/Interop/WindowMessageSink.cs index 9a42b85..9aa2a2f 100644 --- a/Source/NotifyIconWpf/Interop/WindowMessageSink.cs +++ b/Source/NotifyIconWpf/Interop/WindowMessageSink.cs @@ -8,7 +8,7 @@ namespace Hardcodet.Wpf.TaskbarNotification.Interop /// Receives messages from the taskbar icon through /// window messages of an underlying helper window. /// - public partial class WindowMessageSink : IDisposable + public class WindowMessageSink : IDisposable { #region members @@ -196,10 +196,11 @@ namespace Hardcodet.Wpf.TaskbarNotification.Interop /// /// Processes incoming system messages. /// - /// - /// - /// - /// + /// Callback ID. + /// If the version is + /// or higher, this parameter can be used to resolve mouse coordinates. + /// Currently not in use. + /// Provides information about the event. private void ProcessWindowMessage(uint msg, uint wParam, uint lParam) { if (msg != CallbackMessageId) return; diff --git a/Source/NotifyIconWpf/NotifyIconWpf.csproj b/Source/NotifyIconWpf/NotifyIconWpf.csproj index f0afef6..255b6c1 100644 --- a/Source/NotifyIconWpf/NotifyIconWpf.csproj +++ b/Source/NotifyIconWpf/NotifyIconWpf.csproj @@ -53,6 +53,7 @@ + diff --git a/Source/NotifyIconWpf/TaskbarIcon.Declarations.cs b/Source/NotifyIconWpf/TaskbarIcon.Declarations.cs index 21aead1..937bbc7 100644 --- a/Source/NotifyIconWpf/TaskbarIcon.Declarations.cs +++ b/Source/NotifyIconWpf/TaskbarIcon.Declarations.cs @@ -1,4 +1,5 @@ using System; +using System.ComponentModel; using System.Drawing; using System.Windows; using System.Windows.Controls; @@ -14,8 +15,107 @@ namespace Hardcodet.Wpf.TaskbarNotification /// partial class TaskbarIcon { + /// + /// Category name that is set on designer properties. + /// + public const string CategoryName = "NotifyIcon"; + + /// + /// A control that was created + /// in order to display either + /// or . + /// + internal ToolTip CustomToolTip { get; private set; } + + /// + /// A which is either the + /// control itself or a + /// that wraps it. + /// + internal Popup CustomPopup { get; private set; } + + //DEPENDENCY PROPERTIES + #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. + /// + [Browsable(false)] + 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. + ///
+ [Category(CategoryName)] + [Description("Sets the displayed taskbar icon.")] + 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; + + //resolving the ImageSource at design time probably won't work + if (!Util.IsDesignMode) Icon = newValue.ToIcon(); + } + + #endregion + #region ToolTipText dependency property /// @@ -35,6 +135,8 @@ namespace Hardcodet.Wpf.TaskbarNotification /// A tooltip text that is being displayed if no custom /// was set or if custom tooltips are not supported. /// + [Category(CategoryName)] + [Description("Alternative to a fully blown ToolTip, which is only displayed on Vista and above.")] public string ToolTipText { get { return (string) GetValue(ToolTipTextProperty); } @@ -66,128 +168,77 @@ namespace Hardcodet.Wpf.TaskbarNotification /// Provides information about the updated property. private void OnToolTipTextPropertyChanged(DependencyPropertyChangedEventArgs e) { - string newValue = (string) e.NewValue; - - iconData.ToolTipText = newValue ?? String.Empty; + //only recreate tooltip if we're not using a custom control + if (CustomToolTip == null || CustomToolTip.Content is string) + { + CreateCustomToolTip(); + } + WriteToolTipSettings(); } #endregion - #region ToolTip dependency property override + #region TaskbarIconToolTip dependency property /// - /// A static callback listener which is being invoked if the - /// dependency property has - /// been changed. Invokes the - /// instance method of the changed instance. + /// A custom UI element that is displayed as a tooltip if the user hovers over the taskbar icon. + /// Works only with Vista and above. Accordingly, you should make sure that + /// the property is set as well. /// - /// 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), + public static readonly DependencyProperty TaskbarIconToolTipProperty = + DependencyProperty.Register("TaskbarIconToolTip", + typeof (UIElement), typeof (TaskbarIcon), - new FrameworkPropertyMetadata(null, IconSourcePropertyChanged)); + new FrameworkPropertyMetadata(null, TaskbarIconToolTipPropertyChanged)); /// - /// A property wrapper for the + /// A property wrapper for the /// dependency property:
- /// Resolves an image source and updates the property accordingly. + /// A custom UI element that is displayed as a tooltip if the user hovers over the taskbar icon. + /// Works only with Vista and above. Accordingly, you should make sure that + /// the property is set as well. ///
- public ImageSource IconSource + [Category(CategoryName)] + [Description("Custom UI element that is displayed as a tooltip. Only on Vista and above")] + public UIElement TaskbarIconToolTip { - get { return (ImageSource) GetValue(IconSourceProperty); } - set { SetValue(IconSourceProperty, value); } + get { return (UIElement) GetValue(TaskbarIconToolTipProperty); } + set { SetValue(TaskbarIconToolTipProperty, value); } } /// /// A static callback listener which is being invoked if the - /// dependency property has - /// been changed. Invokes 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) + private static void TaskbarIconToolTipPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { TaskbarIcon owner = (TaskbarIcon) d; - owner.OnIconSourcePropertyChanged(e); + owner.OnTaskbarIconToolTipPropertyChanged(e); } /// - /// Handles changes of the dependency property. As + /// 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 + /// property wrapper, updates of the property's value /// should be handled here. /// Provides information about the updated property. - private void OnIconSourcePropertyChanged(DependencyPropertyChangedEventArgs e) + private void OnTaskbarIconToolTipPropertyChanged(DependencyPropertyChangedEventArgs e) { - ImageSource newValue = (ImageSource) e.NewValue; - Icon = newValue.ToIcon(); + //recreate tooltip control + CreateCustomToolTip(); + + //udpate tooltip settings - needed to make sure a string is set, even + //if the ToolTipText property is not set. Otherwise, the event that + //triggers tooltip display is never fired. + WriteToolTipSettings(); } #endregion @@ -195,22 +246,24 @@ namespace Hardcodet.Wpf.TaskbarNotification #region TaskbarIconPopup dependency property /// - /// A custom popup that is displayed when the taskbar icon is clicked. + /// A control that is displayed as a popup when the taskbar icon is clicked. /// public static readonly DependencyProperty TaskbarIconPopupProperty = DependencyProperty.Register("TaskbarIconPopup", - typeof (Popup), + typeof(UIElement), 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. + /// A control that is displayed as a popup when the taskbar icon is clicked. ///
- public Popup TaskbarIconPopup + [Category(CategoryName)] + [Description("Displayed as a Popup if the user clicks on the taskbar icon.")] + public UIElement TaskbarIconPopup { - get { return (Popup) GetValue(TaskbarIconPopupProperty); } + get { return (UIElement)GetValue(TaskbarIconPopupProperty); } set { SetValue(TaskbarIconPopupProperty, value); } } @@ -240,10 +293,12 @@ namespace Hardcodet.Wpf.TaskbarNotification private void OnTaskbarIconPopupPropertyChanged(DependencyPropertyChangedEventArgs e) { //currently not needed + CreatePopup(); } #endregion + #region MenuActivation dependency property /// @@ -262,6 +317,8 @@ namespace Hardcodet.Wpf.TaskbarNotification /// Defines what mouse events display the context menu. /// Defaults to . /// + [Category(CategoryName)] + [Description("Defines what mouse events display the context menu.")] public PopupActivationMode MenuActivation { get { return (PopupActivationMode) GetValue(MenuActivationProperty); } @@ -316,6 +373,8 @@ namespace Hardcodet.Wpf.TaskbarNotification /// Defines what mouse events trigger the . /// Default is . /// + [Category(CategoryName)] + [Description("Defines what mouse events display the TaskbarIconPopup.")] public PopupActivationMode PopupActivation { get { return (PopupActivationMode) GetValue(PopupActivationProperty); } @@ -352,6 +411,7 @@ namespace Hardcodet.Wpf.TaskbarNotification #endregion + #region Visibility dependency property override /// @@ -439,6 +499,7 @@ namespace Hardcodet.Wpf.TaskbarNotification /// /// Occurs when the user presses the left mouse button. /// + [Category(CategoryName)] public event RoutedEventHandler TaskbarIconLeftMouseDown { add { AddHandler(TaskbarIconLeftMouseDownEvent, value); } @@ -1212,11 +1273,6 @@ namespace Hardcodet.Wpf.TaskbarNotification //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 index 43ef54d..57c07dc 100644 --- a/Source/NotifyIconWpf/TaskbarIcon.Interop.cs +++ b/Source/NotifyIconWpf/TaskbarIcon.Interop.cs @@ -1,10 +1,12 @@ using System; -using System.Diagnostics; using System.Drawing; using System.Threading; using System.Windows; using System.Windows.Controls; +using System.Windows.Controls.Primitives; using Hardcodet.Wpf.TaskbarNotification.Interop; +using Point=Hardcodet.Wpf.TaskbarNotification.Interop.Point; + namespace Hardcodet.Wpf.TaskbarNotification { @@ -23,7 +25,94 @@ namespace Hardcodet.Wpf.TaskbarNotification private readonly Timer singleClickTimer; - #region Update ToolTip Settings + #region ToolTip + + /// + /// Displays a custom tooltip, if available. This method is only + /// invoked for Windows Vista and above. + /// + /// Whether to show or hide the tooltip. + private void OnToolTipChange(bool visible) + { + //if we don't have a tooltip, there's nothing to do here... + if (CustomToolTip == null) return; + + if (visible) + { + if (ContextMenu != null && ContextMenu.IsOpen || + CustomPopup != null && CustomPopup.IsOpen) + { + //ignore if we have an open context menu or popup + return; + } + + var args = RaisePreviewTaskbarIconToolTipOpenEvent(); + if (args.Handled) return; + + CustomToolTip.IsOpen = true; + RaiseTaskbarIconToolTipOpenEvent(); + } + else + { + var args = RaisePreviewTaskbarIconToolTipCloseEvent(); + if (args.Handled) return; + + CustomToolTip.IsOpen = false; + RaiseTaskbarIconToolTipCloseEvent(); + } + } + + /// + /// Creates a control that either + /// wraps the currently set + /// control or the string.
+ /// If itself is already + /// a instance, it will be used directly. + ///
+ /// We use a rather than + /// because there was no way to prevent a + /// popup from causing cyclic open/close commands if it was + /// placed under the mouse. ToolTip internally uses a Popup of + /// its own, but takes advance of Popup's internal + /// property which prevents this issue. + private void CreateCustomToolTip() + { + //check if the item itself is a tooltip + ToolTip tt = TaskbarIconToolTip as ToolTip; + + if (tt == null && TaskbarIconToolTip != null) + { + //create an invisible tooltip that hosts the UIElement + tt = new ToolTip(); + tt.Placement = PlacementMode.Mouse; + tt.PlacementTarget = this; + + //the tooltip (and implicitly its context) explicitly gets + //the DataContext of this instance. If there is no DataContext, + //the TaskbarIcon sets itself + tt.DataContext = DataContext ?? this; + + //make sure the tooltip is invisible + tt.HasDropShadow = false; + tt.BorderThickness = new Thickness(0); + tt.Background = System.Windows.Media.Brushes.Transparent; + + //setting the + tt.StaysOpen = true; + + tt.Content = TaskbarIconToolTip; + } + else if (tt == null && !String.IsNullOrEmpty(ToolTipText)) + { + //create a simple tooltip for the string + tt = new ToolTip(); + tt.Content = ToolTipText; + } + + //store a reference to the used tooltip + CustomToolTip = tt; + } + /// /// Sets tooltip settings for the class depending on defined @@ -31,32 +120,28 @@ namespace Hardcodet.Wpf.TaskbarNotification /// private void WriteToolTipSettings() { - IconDataMembers flags = IconDataMembers.Tip; + const IconDataMembers flags = IconDataMembers.Tip; iconData.ToolTipText = ToolTipText; if (messageSink.Version == NotifyIconVersion.Vista) { - if (String.IsNullOrEmpty(ToolTipText) && ToolTip != null) + //we need to set a tooltip text to get tooltip events from the + //taskbar icon + if (String.IsNullOrEmpty(iconData.ToolTipText) && CustomToolTip != null) { //if we have not tooltip text but a custom tooltip, we - //need to set a dummy value + //need to set a dummy value (we're displaying the ToolTip control, not the string) 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 + //update the tooltip text Util.WriteIconData(ref iconData, NotifyCommand.Modify, flags); } #endregion - #region Show / Hide Balloon ToolTip + #region Show / Hide Balloon Tip /// /// Displays a balloon tip with the specified title, @@ -64,12 +149,12 @@ namespace Hardcodet.Wpf.TaskbarNotification /// /// 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) + /// A symbol that indicates the severity. + public void ShowBalloonTip(string title, string message, BalloonIcon symbol) { lock (this) { - ShowBalloonTip(title, message, icon.GetBalloonFlag(), IntPtr.Zero); + ShowBalloonTip(title, message, symbol.GetBalloonFlag(), IntPtr.Zero); } } @@ -157,24 +242,78 @@ namespace Hardcodet.Wpf.TaskbarNotification #endregion + #region Create Popup + + /// + /// Creates a control that either + /// wraps the currently set + /// control or the string.
+ /// If itself is already + /// a instance, it will be used directly. + ///
+ /// We use a rather than + /// because there was no way to prevent a + /// popup from causing cyclic open/close commands if it was + /// placed under the mouse. ToolTip internally uses a Popup of + /// its own, but takes advance of Popup's internal + /// property which prevents this issue. + private void CreatePopup() + { + //no popup is available + if (TaskbarIconPopup == null) return; + + //check if the item itself is a popup + Popup popup = TaskbarIconPopup as Popup; + + if (popup == null) + { + //create an invisible popup that hosts the UIElement + popup = new Popup(); + popup.AllowsTransparency = true; + popup.PopupAnimation = PopupAnimation.Fade; + + //the tooltip (and implicitly its context) explicitly gets + //the DataContext of this instance. If there is no DataContext, + //the TaskbarIcon assigns itself + popup.DataContext = DataContext ?? this; + + Popup.CreateRootPopup(popup, TaskbarIconPopup); + + popup.PlacementTarget = this; + popup.Placement = PlacementMode.AbsolutePoint; + popup.StaysOpen = false; + } + + //store a reference to the used tooltip + CustomPopup = popup; + } + + #endregion + #region Show Tray Popup / Context Menu /// /// Displays the control if /// it was set. /// - private void ShowTrayPopup() + private void ShowTrayPopup(Point cursorPosition) { if (IsDisposed) return; + //raise preview event no matter whether popup is currently set + //or not (enables client to set it on demand) + var args = RaisePreviewTaskbarIconPopupOpenEvent(); + if (args.Handled) return; + if (TaskbarIconPopup != null) { - //raise preview event - var args = RaisePreviewTaskbarIconPopupOpenEvent(); - if (args.Handled) return; + //use absolute position, but place the popup centered above the icon + CustomPopup.Placement = PlacementMode.AbsolutePoint; + CustomPopup.HorizontalOffset = cursorPosition.X; //+ TaskbarIconPopup.ActualWidth/2; + CustomPopup.VerticalOffset = cursorPosition.Y; //open popup - TaskbarIconPopup.IsOpen = true; + CustomPopup.IsOpen = true; //activate the message window to track deactivation - otherwise, the context menu //does not close if the user clicks somewhere else @@ -190,17 +329,21 @@ namespace Hardcodet.Wpf.TaskbarNotification /// Displays the if /// it was set. ///
- private void ShowContextMenu() + private void ShowContextMenu(Point cursorPosition) { if (IsDisposed) return; + //raise preview event no matter whether context menu is currently set + //or not (enables client to set it on demand) + var args = RaisePreviewTaskbarIconContextMenuOpenEvent(); + if (args.Handled) return; + if (ContextMenu != null) { - //raise preview event - var args = RaisePreviewTaskbarIconContextMenuOpenEvent(); - if (args.Handled) return; - - //CreateActivationWindow(); + //use absolute position + ContextMenu.Placement = PlacementMode.AbsolutePoint; + ContextMenu.HorizontalOffset = cursorPosition.X; + ContextMenu.VerticalOffset = cursorPosition.Y; ContextMenu.IsOpen = true; //activate the message window to track deactivation - otherwise, the context menu diff --git a/Source/NotifyIconWpf/TaskbarIcon.cs b/Source/NotifyIconWpf/TaskbarIcon.cs index b8b711d..1e6bb1d 100644 --- a/Source/NotifyIconWpf/TaskbarIcon.cs +++ b/Source/NotifyIconWpf/TaskbarIcon.cs @@ -1,11 +1,15 @@ using System; using System.ComponentModel; using System.Diagnostics; +using System.Reflection; using System.Threading; using System.Windows; using System.Windows.Controls; +using System.Windows.Controls.Primitives; +using System.Windows.Input; +using System.Windows.Media; using Hardcodet.Wpf.TaskbarNotification.Interop; - +using Point=Hardcodet.Wpf.TaskbarNotification.Interop.Point; namespace Hardcodet.Wpf.TaskbarNotification @@ -43,6 +47,8 @@ namespace Hardcodet.Wpf.TaskbarNotification } + #region Construction + /// /// Inits the taskbar icon and registers a message listener /// in order to receive events from the taskbar area. @@ -79,8 +85,11 @@ namespace Hardcodet.Wpf.TaskbarNotification if (Application.Current != null) Application.Current.Exit += OnExit; } + #endregion + #region Handle Mouse Events + /// /// Processes mouse events, which are bubbled /// through the class' routed events, trigger @@ -96,7 +105,8 @@ namespace Hardcodet.Wpf.TaskbarNotification { case MouseEvent.MouseMove: RaiseTaskbarIconMouseMoveEvent(); - break; + //immediately return - there's nothing left to evaluate + return; case MouseEvent.IconRightMouseDown: RaiseTaskbarIconRightMouseDownEvent(); break; @@ -130,19 +140,23 @@ namespace Hardcodet.Wpf.TaskbarNotification } + //get mouse coordinates + Point cursorPosition = new Point(); + WinApi.GetCursorPos(ref cursorPosition); + //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; + delayedTimerAction = () => ShowTrayPopup(cursorPosition); singleClickTimer.Change(WinApi.GetDoubleClickTime(), Timeout.Infinite); } else { //show popup immediately - ShowTrayPopup(); + ShowTrayPopup(cursorPosition); } } @@ -153,51 +167,18 @@ namespace Hardcodet.Wpf.TaskbarNotification if (me == MouseEvent.IconLeftMouseUp) { //show context menu once we are sure it's not a double click - delayedTimerAction = ShowContextMenu; + delayedTimerAction = () => ShowContextMenu(cursorPosition); singleClickTimer.Change(WinApi.GetDoubleClickTime(), Timeout.Infinite); } else { //show context menu immediately - ShowContextMenu(); + ShowContextMenu(cursorPosition); } } } - - /// - /// Displays a custom tooltip, if available. This functionality - /// is only available for Windows Vista and above. - /// - /// Whether to show or hide the tooltip. - private void OnToolTipChange(bool visible) - { - //if we have a custom tooltip, show it now - if (ToolTip == null) return; - - - ToolTip tt = (ToolTip)ToolTip; - - if (visible) - { - var args = RaisePreviewTaskbarIconToolTipOpenEvent(); - if (args.Handled) return; - - tt.IsOpen = true; - RaiseTaskbarIconToolTipOpenEvent(); - } - else - { - var args = RaisePreviewTaskbarIconToolTipCloseEvent(); - if (args.Handled) return; - - tt.IsOpen = false; - RaiseTaskbarIconToolTipCloseEvent(); - } - } - - /// /// Bubbles events if a balloon ToolTip was displayed /// or removed. @@ -208,7 +189,7 @@ namespace Hardcodet.Wpf.TaskbarNotification { if (visible) { - RaiseTaskbarIconBalloonTipShownEvent(); + RaiseTaskbarIconBalloonTipShownEvent(); } else { @@ -216,7 +197,7 @@ namespace Hardcodet.Wpf.TaskbarNotification } } - + #endregion #region SetVersion diff --git a/Source/NotifyIconWpf/Util.cs b/Source/NotifyIconWpf/Util.cs index 55e773d..4b2acc4 100644 --- a/Source/NotifyIconWpf/Util.cs +++ b/Source/NotifyIconWpf/Util.cs @@ -214,7 +214,8 @@ namespace Hardcodet.Wpf.TaskbarNotification case PopupActivationMode.MiddleClick: return me == MouseEvent.IconMiddleMouseUp; case PopupActivationMode.All: - return true; + //return true for everything except mouse movements + return me != MouseEvent.MouseMove; default: throw new ArgumentOutOfRangeException("activationMode"); } diff --git a/Source/Sample Project/App.xaml b/Source/Sample Project/App.xaml index 0c9c882..04fb858 100644 --- a/Source/Sample Project/App.xaml +++ b/Source/Sample Project/App.xaml @@ -4,5 +4,11 @@ StartupUri="Window1.xaml"> + + + + + + diff --git a/Source/Sample Project/Commands/HideMainWindowCommand.cs b/Source/Sample Project/Commands/HideMainWindowCommand.cs new file mode 100644 index 0000000..c91b4b8 --- /dev/null +++ b/Source/Sample Project/Commands/HideMainWindowCommand.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows; +using System.Windows.Input; + +namespace Sample_Project.Commands +{ + /// + /// Hides the main window. + /// + public class HideMainWindowCommand : ICommand + { + public event EventHandler CanExecuteChanged; + + public void Execute(object parameter) + { + Application.Current.MainWindow.Hide(); + TaskbarIconCommands.RefreshCommands(); + } + + + public bool CanExecute(object parameter) + { + return Application.Current.MainWindow.IsVisible; + } + + + /// + /// Raises the event. + /// + internal void RaiseCanExcecuteChanged() + { + if (CanExecuteChanged != null) CanExecuteChanged(this, EventArgs.Empty); + } + + } +} diff --git a/Source/Sample Project/Commands/ShowMainWindowCommand.cs b/Source/Sample Project/Commands/ShowMainWindowCommand.cs new file mode 100644 index 0000000..8367ccf --- /dev/null +++ b/Source/Sample Project/Commands/ShowMainWindowCommand.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows; +using System.Windows.Input; + +namespace Sample_Project.Commands +{ + /// + /// Shows the main window. + /// + public class ShowMainWindowCommand : ICommand + { + public event EventHandler CanExecuteChanged; + + public void Execute(object parameter) + { + Application.Current.MainWindow.Show(); + TaskbarIconCommands.RefreshCommands(); + } + + + public bool CanExecute(object parameter) + { + return Application.Current.MainWindow.IsVisible == false; + } + + /// + /// Raises the event. + /// + internal void RaiseCanExcecuteChanged() + { + if (CanExecuteChanged != null) CanExecuteChanged(this, EventArgs.Empty); + } + + } +} diff --git a/Source/Sample Project/Commands/TaskbarIconCommands.cs b/Source/Sample Project/Commands/TaskbarIconCommands.cs new file mode 100644 index 0000000..abcd757 --- /dev/null +++ b/Source/Sample Project/Commands/TaskbarIconCommands.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows.Input; + +namespace Sample_Project.Commands +{ + public static class TaskbarIconCommands + { + public static HideMainWindowCommand HideMain { get; set; } + public static ShowMainWindowCommand ShowMain { get; set; } + + + static TaskbarIconCommands() + { + HideMain = new HideMainWindowCommand(); + ShowMain =new ShowMainWindowCommand(); + } + + public static void RefreshCommands() + { + HideMain.RaiseCanExcecuteChanged(); + ShowMain.RaiseCanExcecuteChanged(); + } + } +} diff --git a/Source/Sample Project/FancyPopup.xaml b/Source/Sample Project/FancyPopup.xaml new file mode 100644 index 0000000..cd1804c --- /dev/null +++ b/Source/Sample Project/FancyPopup.xaml @@ -0,0 +1,92 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Source/Sample Project/FancyPopup.xaml.cs b/Source/Sample Project/FancyPopup.xaml.cs new file mode 100644 index 0000000..7d5d028 --- /dev/null +++ b/Source/Sample Project/FancyPopup.xaml.cs @@ -0,0 +1,46 @@ +using System.Windows; +using System.Windows.Controls; + +namespace Sample_Project +{ + /// + /// Interaction logic for FancyPopup.xaml + /// + public partial class FancyPopup : UserControl + { + #region ClickCount dependency property + + /// + /// The number of clicks on the popup button. + /// + public static readonly DependencyProperty ClickCountProperty = + DependencyProperty.Register("ClickCount", + typeof (int), + typeof (FancyPopup), + new FrameworkPropertyMetadata(0)); + + /// + /// A property wrapper for the + /// dependency property:
+ /// The number of clicks on the popup button. + ///
+ public int ClickCount + { + get { return (int) GetValue(ClickCountProperty); } + set { SetValue(ClickCountProperty, value); } + } + + #endregion + + public FancyPopup() + { + InitializeComponent(); + } + + private void OnButtonClick(object sender, RoutedEventArgs e) + { + //just increment a counter - will be displayed on screen + ClickCount++; + } + } +} \ No newline at end of file diff --git a/Source/Sample Project/FancyToolTip.xaml b/Source/Sample Project/FancyToolTip.xaml new file mode 100644 index 0000000..8afe84c --- /dev/null +++ b/Source/Sample Project/FancyToolTip.xaml @@ -0,0 +1,68 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Source/Sample Project/FancyToolTip.xaml.cs b/Source/Sample Project/FancyToolTip.xaml.cs new file mode 100644 index 0000000..d3c9e2a --- /dev/null +++ b/Source/Sample Project/FancyToolTip.xaml.cs @@ -0,0 +1,80 @@ +using System; +using System.Collections.Generic; +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 FancyToolTip.xaml + /// + public partial class FancyToolTip + { + #region InfoText dependency property + + /// + /// The tooltip details. + /// + public static readonly DependencyProperty InfoTextProperty = + DependencyProperty.Register("InfoText", + typeof (string), + typeof (FancyToolTip), + new FrameworkPropertyMetadata("", InfoTextPropertyChanged)); + + /// + /// A property wrapper for the + /// dependency property:
+ /// The tooltip details. + ///
+ public string InfoText + { + get { return (string) GetValue(InfoTextProperty); } + set { SetValue(InfoTextProperty, 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 InfoTextPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + FancyToolTip owner = (FancyToolTip) d; + owner.OnInfoTextPropertyChanged(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 OnInfoTextPropertyChanged(DependencyPropertyChangedEventArgs e) + { +// string newValue = (string) e.NewValue; + } + + #endregion + + + + public FancyToolTip() + { + this.InitializeComponent(); + } + } +} \ No newline at end of file diff --git a/Source/Sample Project/Icons/Error.ico b/Source/Sample Project/Icons/Error.ico new file mode 100644 index 0000000000000000000000000000000000000000..8a8bbafa87909fb28ea0375bf2347fa3d3768d26 GIT binary patch literal 1150 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 e_Z%*j3ugJkm?&CG8KkMMtA4 + 3.5 @@ -46,29 +47,63 @@ - - - + + 3.0 + + + 3.0 + + + 3.0 + + + 3.0 + + + + + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + FancyToolTip.xaml + MSBuild:Compile Designer + + Designer + MSBuild:Compile + MSBuild:Compile Designer - App.xaml Code + App.xaml - Window1.xaml Code + Window1.xaml + + + + + FancyPopup.xaml + Code @@ -79,8 +114,8 @@ True - Settings.settings True + Settings.settings ResXFileCodeGenerator @@ -107,6 +142,10 @@ + + + + diff --git a/Source/Sample Project/TaskbarIconResources.xaml b/Source/Sample Project/TaskbarIconResources.xaml new file mode 100644 index 0000000..bb31716 --- /dev/null +++ b/Source/Sample Project/TaskbarIconResources.xaml @@ -0,0 +1,142 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Source/Sample Project/Window1.xaml b/Source/Sample Project/Window1.xaml index d373d1e..f7b8f30 100644 --- a/Source/Sample Project/Window1.xaml +++ b/Source/Sample Project/Window1.xaml @@ -2,146 +2,437 @@ x:Class="Sample_Project.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" - xmlns:tb="http://www.hardcodet.net/taskbar" -Title="Window1" Height="480" Width="579"> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + xmlns:tb="http://www.hardcodet.net/taskbar" + xmlns:sys="clr-namespace:System;assembly=mscorlib" + Title="Window1" + Height="808" + Width="769" + xmlns:d="http://schemas.microsoft.com/expression/blend/2008" + xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + mc:Ignorable="d" + xmlns:local="clr-namespace:Sample_Project"> + - - - + - - - - - - - - - - - - - - - - - - - - - + Height="549" + d:IsHidden="True" + d:IsLocked="True"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
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)