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">
+
-
-
-
+
-
-
-
-
-
-
-
+
+
+
+
+
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+ Margin="0,70,68,0"
+ VerticalAlignment="Top"
+ Width="73"
+ HorizontalAlignment="Right" />
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ Height="549"
+ d:IsHidden="True"
+ d:IsLocked="True">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Source/Sample Project/Window1.xaml.cs b/Source/Sample Project/Window1.xaml.cs
index 639bfe1..aec2bb3 100644
--- a/Source/Sample Project/Window1.xaml.cs
+++ b/Source/Sample Project/Window1.xaml.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using System.IO;
using System.Linq;
using System.Text;
using System.Windows;
@@ -19,13 +20,11 @@ namespace Sample_Project
///
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)