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 0000000..8a8bbaf Binary files /dev/null and b/Source/Sample Project/Icons/Error.ico differ diff --git a/Source/Sample Project/Icons/Inactive.ico b/Source/Sample Project/Icons/Inactive.ico new file mode 100644 index 0000000..8d3a7e9 Binary files /dev/null and b/Source/Sample Project/Icons/Inactive.ico differ diff --git a/Source/Sample Project/Images/Add.png b/Source/Sample Project/Images/Add.png new file mode 100644 index 0000000..269aaa6 Binary files /dev/null and b/Source/Sample Project/Images/Add.png differ diff --git a/Source/Sample Project/Images/Info.png b/Source/Sample Project/Images/Info.png new file mode 100644 index 0000000..351dbac Binary files /dev/null and b/Source/Sample Project/Images/Info.png differ diff --git a/Source/Sample Project/Images/Remove.png b/Source/Sample Project/Images/Remove.png new file mode 100644 index 0000000..2c8b19d Binary files /dev/null and b/Source/Sample Project/Images/Remove.png differ diff --git a/Source/Sample Project/Images/preferences.png b/Source/Sample Project/Images/preferences.png new file mode 100644 index 0000000..cd420d7 Binary files /dev/null and b/Source/Sample Project/Images/preferences.png differ diff --git a/Source/Sample Project/Sample Project.csproj b/Source/Sample Project/Sample Project.csproj index 6a3b392..3e5b6fc 100644 --- a/Source/Sample Project/Sample Project.csproj +++ b/Source/Sample Project/Sample Project.csproj @@ -33,6 +33,7 @@ 4 + 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)