diff --git a/Source/NotifyIconWpf/NotifyIconWpf.csproj b/Source/NotifyIconWpf/NotifyIconWpf.csproj
index 255b6c1..b78f889 100644
--- a/Source/NotifyIconWpf/NotifyIconWpf.csproj
+++ b/Source/NotifyIconWpf/NotifyIconWpf.csproj
@@ -58,7 +58,6 @@
-
diff --git a/Source/NotifyIconWpf/TaskbarIcon.Declarations.cs b/Source/NotifyIconWpf/TaskbarIcon.Declarations.cs
index 937bbc7..cc115d0 100644
--- a/Source/NotifyIconWpf/TaskbarIcon.Declarations.cs
+++ b/Source/NotifyIconWpf/TaskbarIcon.Declarations.cs
@@ -25,14 +25,98 @@ namespace Hardcodet.Wpf.TaskbarNotification
/// in order to display either
/// or .
///
- internal ToolTip CustomToolTip { get; private set; }
+ internal ToolTip CustomToolTip
+ {
+ get { return IconToolTipResolved; }
+ private set { SetIconToolTipResolved(value); }
+ }
///
/// A which is either the
/// control itself or a
/// that wraps it.
///
- internal Popup CustomPopup { get; private set; }
+ internal Popup CustomPopup
+ {
+ get { return IconPopupResolved; }
+ private set { SetIconPopupResolved(value); }
+ }
+
+
+
+ //RESOLVED POPUPS CONTROLS
+ #region IconPopupResolved
+
+ ///
+ /// IconPopupResolved Read-Only Dependency Property
+ ///
+ private static readonly DependencyPropertyKey IconPopupResolvedPropertyKey
+ = DependencyProperty.RegisterReadOnly("IconPopupResolved", typeof(Popup), typeof(TaskbarIcon),
+ new FrameworkPropertyMetadata(null));
+
+ public static readonly DependencyProperty IconPopupResolvedProperty
+ = IconPopupResolvedPropertyKey.DependencyProperty;
+
+ ///
+ /// Gets the IconPopupResolved property. This dependency property
+ /// indicates ....
+ ///
+ [Category(CategoryName)]
+ public Popup IconPopupResolved
+ {
+ get { return (Popup)GetValue(IconPopupResolvedProperty); }
+ }
+
+ ///
+ /// Provides a secure method for setting the IconPopupResolved property.
+ /// This dependency property indicates ....
+ ///
+ /// The new value for the property.
+ protected void SetIconPopupResolved(Popup value)
+ {
+ SetValue(IconPopupResolvedPropertyKey, value);
+ }
+
+ #endregion
+
+ #region IconToolTipResolved
+
+ ///
+ /// IconToolTipResolved Read-Only Dependency Property
+ ///
+ private static readonly DependencyPropertyKey IconToolTipResolvedPropertyKey
+ = DependencyProperty.RegisterReadOnly("IconToolTipResolved", typeof(ToolTip), typeof(TaskbarIcon),
+ new FrameworkPropertyMetadata(null ));
+
+ public static readonly DependencyProperty IconToolTipResolvedProperty
+ = IconToolTipResolvedPropertyKey.DependencyProperty;
+
+ ///
+ /// Gets the IconToolTipResolved property. This dependency property
+ /// indicates ....
+ ///
+ [Category(CategoryName)]
+ [Browsable(true)]
+ [Bindable(true)]
+ public ToolTip IconToolTipResolved
+ {
+ get { return (ToolTip)GetValue(IconToolTipResolvedProperty); }
+ }
+
+ ///
+ /// Provides a secure method for setting the IconToolTipResolved property.
+ /// This dependency property indicates ....
+ ///
+ /// The new value for the property.
+ protected void SetIconToolTipResolved(ToolTip value)
+ {
+ SetValue(IconToolTipResolvedPropertyKey, value);
+ }
+
+ #endregion
+
+
+
//DEPENDENCY PROPERTIES
@@ -292,7 +376,7 @@ namespace Hardcodet.Wpf.TaskbarNotification
/// Provides information about the updated property.
private void OnTaskbarIconPopupPropertyChanged(DependencyPropertyChangedEventArgs e)
{
- //currently not needed
+ //create a pop
CreatePopup();
}
@@ -453,38 +537,6 @@ namespace Hardcodet.Wpf.TaskbarNotification
#endregion
- #region ContextMenu dependency property override
-
- ///
- /// A static callback listener which is being invoked if the
- /// dependency property has
- /// been changed. Invokes the
- /// instance method of the changed instance.
- ///
- /// The currently processed owner of the property.
- /// Provides information about the updated property.
- private static void ContextMenuPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
- {
- TaskbarIcon owner = (TaskbarIcon) d;
- owner.OnContextMenuPropertyChanged(e);
- }
-
-
- ///
- /// Handles changes of the dependency property. As
- /// WPF internally uses the dependency property system and bypasses the
- /// property wrapper, updates of the property's value
- /// should be handled here.
- /// Provides information about the updated property.
- private void OnContextMenuPropertyChanged(DependencyPropertyChangedEventArgs e)
- {
- //currently not needed
- }
-
- #endregion
-
-
//EVENTS
@@ -1258,6 +1310,148 @@ namespace Hardcodet.Wpf.TaskbarNotification
#endregion
+ //ATTACHED EVENTS
+
+ #region PopupOpened
+
+ ///
+ /// PopupOpened Attached Routed Event
+ ///
+ public static readonly RoutedEvent PopupOpenedEvent = EventManager.RegisterRoutedEvent("PopupOpened",
+ RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(TaskbarIcon));
+
+ ///
+ /// Adds a handler for the PopupOpened attached event
+ ///
+ /// UIElement or ContentElement that listens to the event
+ /// Event handler to be added
+ public static void AddPopupOpenedHandler(DependencyObject element, RoutedEventHandler handler)
+ {
+ RoutedEventHelper.AddHandler(element, PopupOpenedEvent, handler);
+ }
+
+ ///
+ /// Removes a handler for the PopupOpened attached event
+ ///
+ /// UIElement or ContentElement that listens to the event
+ /// Event handler to be removed
+ public static void RemovePopupOpenedHandler(DependencyObject element, RoutedEventHandler handler)
+ {
+ RoutedEventHelper.RemoveHandler(element, PopupOpenedEvent, handler);
+ }
+
+ ///
+ /// A static helper method to raise the PopupOpened event on a target element.
+ ///
+ /// UIElement or ContentElement on which to raise the event
+ internal static RoutedEventArgs RaisePopupOpenedEvent(DependencyObject target)
+ {
+ if (target == null) return null;
+
+ RoutedEventArgs args = new RoutedEventArgs();
+ args.RoutedEvent = PopupOpenedEvent;
+ RoutedEventHelper.RaiseEvent(target, args);
+ return args;
+ }
+
+ #endregion
+
+
+
+ #region ToolTipOpened
+
+ ///
+ /// ToolTipOpened Attached Routed Event
+ ///
+ public static readonly RoutedEvent ToolTipOpenedEvent = EventManager.RegisterRoutedEvent("ToolTipOpened",
+ RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(TaskbarIcon));
+
+ ///
+ /// Adds a handler for the ToolTipOpened attached event
+ ///
+ /// UIElement or ContentElement that listens to the event
+ /// Event handler to be added
+ public static void AddToolTipOpenedHandler(DependencyObject element, RoutedEventHandler handler)
+ {
+ RoutedEventHelper.AddHandler(element, ToolTipOpenedEvent, handler);
+ }
+
+ ///
+ /// Removes a handler for the ToolTipOpened attached event
+ ///
+ /// UIElement or ContentElement that listens to the event
+ /// Event handler to be removed
+ public static void RemoveToolTipOpenedHandler(DependencyObject element, RoutedEventHandler handler)
+ {
+ RoutedEventHelper.RemoveHandler(element, ToolTipOpenedEvent, handler);
+ }
+
+ ///
+ /// A static helper method to raise the ToolTipOpened event on a target element.
+ ///
+ /// UIElement or ContentElement on which to raise the event
+ internal static RoutedEventArgs RaiseToolTipOpenedEvent(DependencyObject target)
+ {
+ if (target == null) return null;
+
+ RoutedEventArgs args = new RoutedEventArgs();
+ args.RoutedEvent = ToolTipOpenedEvent;
+ RoutedEventHelper.RaiseEvent(target, args);
+ return args;
+ }
+
+ #endregion
+
+ #region ToolTipClose
+
+ ///
+ /// ToolTipClose Attached Routed Event
+ ///
+ public static readonly RoutedEvent ToolTipCloseEvent = EventManager.RegisterRoutedEvent("ToolTipClose",
+ RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(TaskbarIcon));
+
+ ///
+ /// Adds a handler for the ToolTipClose attached event
+ ///
+ /// UIElement or ContentElement that listens to the event
+ /// Event handler to be added
+ public static void AddToolTipCloseHandler(DependencyObject element, RoutedEventHandler handler)
+ {
+ RoutedEventHelper.AddHandler(element, ToolTipCloseEvent, handler);
+ }
+
+ ///
+ /// Removes a handler for the ToolTipClose attached event
+ ///
+ /// UIElement or ContentElement that listens to the event
+ /// Event handler to be removed
+ public static void RemoveToolTipCloseHandler(DependencyObject element, RoutedEventHandler handler)
+ {
+ RoutedEventHelper.RemoveHandler(element, ToolTipCloseEvent, handler);
+ }
+
+ ///
+ /// A static helper method to raise the ToolTipClose event on a target element.
+ ///
+ /// UIElement or ContentElement on which to raise the event
+ internal static RoutedEventArgs RaiseToolTipCloseEvent(DependencyObject target)
+ {
+ if (target == null) return null;
+
+ RoutedEventArgs args = new RoutedEventArgs();
+ args.RoutedEvent = ToolTipCloseEvent;
+ RoutedEventHelper.RaiseEvent(target, args);
+ return args;
+ }
+
+ #endregion
+
+
+
+
+
+
+
//BASE CLASS PROPERTY OVERRIDES
@@ -1269,10 +1463,6 @@ namespace Hardcodet.Wpf.TaskbarNotification
//register change listener for the Visibility property
PropertyMetadata md = new PropertyMetadata(Visibility.Visible, VisibilityPropertyChanged);
VisibilityProperty.OverrideMetadata(typeof(TaskbarIcon), md);
-
- //register change listener for the ContextMenu property
- md = new FrameworkPropertyMetadata(new PropertyChangedCallback(ContextMenuPropertyChanged));
- ContextMenuProperty.OverrideMetadata(typeof (TaskbarIcon), md);
}
}
}
\ No newline at end of file
diff --git a/Source/NotifyIconWpf/TaskbarIcon.Interop.cs b/Source/NotifyIconWpf/TaskbarIcon.Interop.cs
deleted file mode 100644
index 57c07dc..0000000
--- a/Source/NotifyIconWpf/TaskbarIcon.Interop.cs
+++ /dev/null
@@ -1,360 +0,0 @@
-using System;
-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
-{
- partial class TaskbarIcon
- {
- ///
- /// An action that is being invoked if the
- /// fires.
- ///
- private Action delayedTimerAction;
-
- ///
- /// A timer that is used to differentiate between single
- /// and double clicks.
- ///
- private readonly Timer singleClickTimer;
-
-
- #region 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
- /// dependency properties and OS support.
- ///
- private void WriteToolTipSettings()
- {
- const IconDataMembers flags = IconDataMembers.Tip;
- iconData.ToolTipText = ToolTipText;
-
- if (messageSink.Version == NotifyIconVersion.Vista)
- {
- //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 (we're displaying the ToolTip control, not the string)
- iconData.ToolTipText = "ToolTip";
- }
- }
-
- //update the tooltip text
- Util.WriteIconData(ref iconData, NotifyCommand.Modify, flags);
- }
-
- #endregion
-
- #region Show / Hide Balloon Tip
-
- ///
- /// Displays a balloon tip with the specified title,
- /// text, and icon in the taskbar for the specified time period.
- ///
- /// The title to display on the balloon tip.
- /// The text to display on the balloon tip.
- /// A symbol that indicates the severity.
- public void ShowBalloonTip(string title, string message, BalloonIcon symbol)
- {
- lock (this)
- {
- ShowBalloonTip(title, message, symbol.GetBalloonFlag(), IntPtr.Zero);
- }
- }
-
-
- ///
- /// Displays a balloon tip with the specified title,
- /// text, and a custom icon in the taskbar for the specified time period.
- ///
- /// The title to display on the balloon tip.
- /// The text to display on the balloon tip.
- /// A custom icon.
- /// If
- /// is a null reference.
- public void ShowBalloonTip(string title, string message, Icon customIcon)
- {
- if (customIcon == null) throw new ArgumentNullException("customIcon");
-
- lock (this)
- {
- ShowBalloonTip(title, message, BalloonFlags.User, customIcon.Handle);
- }
- }
-
-
- ///
- /// Invokes in order to display
- /// a given balloon ToolTip.
- ///
- /// The title to display on the balloon tip.
- /// The text to display on the balloon tip.
- /// Indicates what icon to use.
- /// A handle to a custom icon, if any, or
- /// .
- private void ShowBalloonTip(string title, string message, BalloonFlags flags, IntPtr balloonIconHandle)
- {
- EnsureNotDisposed();
-
- iconData.BalloonText = message;
- iconData.BalloonTitle = title;
-
- iconData.BalloonFlags = flags;
- iconData.CustomBalloonIconHandle = balloonIconHandle;
- Util.WriteIconData(ref iconData, NotifyCommand.Modify, IconDataMembers.Info);
- }
-
-
- ///
- /// Hides a balloon ToolTip, if any is displayed.
- ///
- public void HideBalloonTip()
- {
- EnsureNotDisposed();
-
- //reset balloon by just setting the info to an empty string
- iconData.BalloonText = iconData.BalloonTitle = String.Empty;
- Util.WriteIconData(ref iconData, NotifyCommand.Modify, IconDataMembers.Info);
- }
-
- #endregion
-
- #region Single Click Timer event
-
- ///
- /// Performs a delayed action if the user requested an action
- /// based on a single click of the left mouse.
- /// This method is invoked by the .
- ///
- private void DoSingleClickAction(object state)
- {
- if (IsDisposed) return;
-
- Console.Out.WriteLine("TIMER EVENT");
-
- //run action
- Action action = delayedTimerAction;
- if (action != null)
- {
- //cleanup action
- delayedTimerAction = null;
-
- //switch to UI thread
- Application.Current.Dispatcher.Invoke(action);
- }
- }
-
- #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(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)
- {
- //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
- CustomPopup.IsOpen = true;
-
- //activate the message window to track deactivation - otherwise, the context menu
- //does not close if the user clicks somewhere else
- WinApi.SetForegroundWindow(messageSink.MessageWindowHandle);
-
- //bubble event
- RaiseTaskbarIconPopupOpenEvent();
- }
- }
-
-
- ///
- /// Displays the if
- /// it was set.
- ///
- private void ShowContextMenu(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)
- {
- //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
- //does not close if the user clicks somewhere else
- WinApi.SetForegroundWindow(messageSink.MessageWindowHandle);
-
- //bubble event
- RaiseTaskbarIconContextMenuOpenEvent();
- }
- }
-
- #endregion
- }
-}
\ No newline at end of file
diff --git a/Source/NotifyIconWpf/TaskbarIcon.cs b/Source/NotifyIconWpf/TaskbarIcon.cs
index 1e6bb1d..4872628 100644
--- a/Source/NotifyIconWpf/TaskbarIcon.cs
+++ b/Source/NotifyIconWpf/TaskbarIcon.cs
@@ -1,17 +1,14 @@
using System;
using System.ComponentModel;
using System.Diagnostics;
-using System.Reflection;
+using System.Drawing;
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
{
///
@@ -30,11 +27,22 @@ namespace Hardcodet.Wpf.TaskbarNotification
///
private readonly WindowMessageSink messageSink;
+ ///
+ /// An action that is being invoked if the
+ /// fires.
+ ///
+ private Action delayedTimerAction;
+
+ ///
+ /// A timer that is used to differentiate between single
+ /// and double clicks.
+ ///
+ private readonly Timer singleClickTimer;
+
///
/// Indicates whether the taskbar icon has been created or not.
///
- public bool IsTaskbarIconCreated { get; set; }
-
+ public bool IsTaskbarIconCreated { get; private set; }
///
/// Indicates whether custom tooltips are supported, which depends
@@ -46,7 +54,6 @@ namespace Hardcodet.Wpf.TaskbarNotification
get { return messageSink.Version == NotifyIconVersion.Vista; }
}
-
#region Construction
///
@@ -55,16 +62,10 @@ namespace Hardcodet.Wpf.TaskbarNotification
///
public TaskbarIcon()
{
- //do nothing if in design mode
- if (Util.IsDesignMode)
- {
- messageSink = WindowMessageSink.CreateEmpty();
- }
- else
- {
- //create message sink that receives window messages
- messageSink = new WindowMessageSink(NotifyIconVersion.Win95);
- }
+ //using dummy sink in design mode
+ messageSink = Util.IsDesignMode
+ ? WindowMessageSink.CreateEmpty()
+ : new WindowMessageSink(NotifyIconVersion.Win95);
//init icon data structure
iconData = NotifyIconData.CreateDefault(messageSink.MessageWindowHandle);
@@ -87,8 +88,7 @@ namespace Hardcodet.Wpf.TaskbarNotification
#endregion
-
- #region Handle Mouse Events
+ #region Process Incoming Mouse Events
///
/// Processes mouse events, which are bubbled
@@ -101,7 +101,7 @@ namespace Hardcodet.Wpf.TaskbarNotification
{
if (IsDisposed) return;
- switch(me)
+ switch (me)
{
case MouseEvent.MouseMove:
RaiseTaskbarIconMouseMoveEvent();
@@ -136,7 +136,6 @@ namespace Hardcodet.Wpf.TaskbarNotification
break;
default:
throw new ArgumentOutOfRangeException("me", "Missing handler for mouse event flag: " + me);
-
}
@@ -178,6 +177,252 @@ namespace Hardcodet.Wpf.TaskbarNotification
}
}
+ #endregion
+
+ #region ToolTips
+
+ ///
+ /// 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;
+
+ //raise attached event first
+ if (TaskbarIconToolTip != null) RaiseToolTipOpenedEvent(TaskbarIconToolTip);
+
+ //bubble routed event
+ RaiseTaskbarIconToolTipOpenEvent();
+ }
+ else
+ {
+ var args = RaisePreviewTaskbarIconToolTipCloseEvent();
+ if (args.Handled) return;
+
+ //raise attached event first
+ if (TaskbarIconToolTip != null) RaiseToolTipCloseEvent(TaskbarIconToolTip);
+
+ //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
+ /// dependency properties and OS support.
+ ///
+ private void WriteToolTipSettings()
+ {
+ const IconDataMembers flags = IconDataMembers.Tip;
+ iconData.ToolTipText = ToolTipText;
+
+ if (messageSink.Version == NotifyIconVersion.Vista)
+ {
+ //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 (we're displaying the ToolTip control, not the string)
+ iconData.ToolTipText = "ToolTip";
+ }
+ }
+
+ //update the tooltip text
+ Util.WriteIconData(ref iconData, NotifyCommand.Modify, flags);
+ }
+
+ #endregion
+
+ #region Custom 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;
+ }
+
+ ///
+ /// Displays the control if
+ /// it was set.
+ ///
+ 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)
+ {
+ //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
+ CustomPopup.IsOpen = true;
+
+ //activate the message window to track deactivation - otherwise, the context menu
+ //does not close if the user clicks somewhere else
+ WinApi.SetForegroundWindow(messageSink.MessageWindowHandle);
+
+ //raise attached event - item should never be null unless developers
+ //changed the CustomPopup directly...
+ if (TaskbarIconPopup != null) RaisePopupOpenedEvent(TaskbarIconPopup);
+
+ //bubble routed event
+ RaiseTaskbarIconPopupOpenEvent();
+ }
+ }
+
+ #endregion
+
+ #region Context Menu
+
+ ///
+ /// Displays the if
+ /// it was set.
+ ///
+ 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)
+ {
+ //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
+ //does not close if the user clicks somewhere else
+ WinApi.SetForegroundWindow(messageSink.MessageWindowHandle);
+
+ //bubble event
+ RaiseTaskbarIconContextMenuOpenEvent();
+ }
+ }
+
+ #endregion
+
+ #region Balloon Tips
///
/// Bubbles events if a balloon ToolTip was displayed
@@ -197,28 +442,122 @@ namespace Hardcodet.Wpf.TaskbarNotification
}
}
+ ///
+ /// Displays a balloon tip with the specified title,
+ /// text, and icon in the taskbar for the specified time period.
+ ///
+ /// The title to display on the balloon tip.
+ /// The text to display on the balloon tip.
+ /// A symbol that indicates the severity.
+ public void ShowBalloonTip(string title, string message, BalloonIcon symbol)
+ {
+ lock (this)
+ {
+ ShowBalloonTip(title, message, symbol.GetBalloonFlag(), IntPtr.Zero);
+ }
+ }
+
+
+ ///
+ /// Displays a balloon tip with the specified title,
+ /// text, and a custom icon in the taskbar for the specified time period.
+ ///
+ /// The title to display on the balloon tip.
+ /// The text to display on the balloon tip.
+ /// A custom icon.
+ /// If
+ /// is a null reference.
+ public void ShowBalloonTip(string title, string message, Icon customIcon)
+ {
+ if (customIcon == null) throw new ArgumentNullException("customIcon");
+
+ lock (this)
+ {
+ ShowBalloonTip(title, message, BalloonFlags.User, customIcon.Handle);
+ }
+ }
+
+
+ ///
+ /// Invokes in order to display
+ /// a given balloon ToolTip.
+ ///
+ /// The title to display on the balloon tip.
+ /// The text to display on the balloon tip.
+ /// Indicates what icon to use.
+ /// A handle to a custom icon, if any, or
+ /// .
+ private void ShowBalloonTip(string title, string message, BalloonFlags flags, IntPtr balloonIconHandle)
+ {
+ EnsureNotDisposed();
+
+ iconData.BalloonText = message;
+ iconData.BalloonTitle = title;
+
+ iconData.BalloonFlags = flags;
+ iconData.CustomBalloonIconHandle = balloonIconHandle;
+ Util.WriteIconData(ref iconData, NotifyCommand.Modify, IconDataMembers.Info);
+ }
+
+
+ ///
+ /// Hides a balloon ToolTip, if any is displayed.
+ ///
+ public void HideBalloonTip()
+ {
+ EnsureNotDisposed();
+
+ //reset balloon by just setting the info to an empty string
+ iconData.BalloonText = iconData.BalloonTitle = String.Empty;
+ Util.WriteIconData(ref iconData, NotifyCommand.Modify, IconDataMembers.Info);
+ }
+
#endregion
+ #region Single Click Timer event
- #region SetVersion
+ ///
+ /// Performs a delayed action if the user requested an action
+ /// based on a single click of the left mouse.
+ /// This method is invoked by the .
+ ///
+ private void DoSingleClickAction(object state)
+ {
+ if (IsDisposed) return;
+
+ //run action
+ Action action = delayedTimerAction;
+ if (action != null)
+ {
+ //cleanup action
+ delayedTimerAction = null;
+
+ //switch to UI thread
+ Application.Current.Dispatcher.Invoke(action);
+ }
+ }
+
+ #endregion
+
+ #region Set Version (API)
///
/// Sets the version flag for the .
///
private void SetVersion()
{
- iconData.VersionOrTimeout = (uint)NotifyIconVersion.Vista;
+ iconData.VersionOrTimeout = (uint) NotifyIconVersion.Vista;
bool status = WinApi.Shell_NotifyIcon(NotifyCommand.SetVersion, ref iconData);
if (!status)
{
- iconData.VersionOrTimeout = (uint)NotifyIconVersion.Win2000;
+ iconData.VersionOrTimeout = (uint) NotifyIconVersion.Win2000;
status = Util.WriteIconData(ref iconData, NotifyCommand.SetVersion);
}
if (!status)
{
- iconData.VersionOrTimeout = (uint)NotifyIconVersion.Win95;
+ iconData.VersionOrTimeout = (uint) NotifyIconVersion.Win95;
status = Util.WriteIconData(ref iconData, NotifyCommand.SetVersion);
}
@@ -230,7 +569,6 @@ namespace Hardcodet.Wpf.TaskbarNotification
#endregion
-
#region Create / Remove Taskbar Icon
///
@@ -292,7 +630,6 @@ namespace Hardcodet.Wpf.TaskbarNotification
#endregion
-
#region Dispose / Exit
///
@@ -311,7 +648,7 @@ namespace Hardcodet.Wpf.TaskbarNotification
{
if (IsDisposed) throw new ObjectDisposedException(Name ?? GetType().FullName);
}
-
+
///
/// Disposes the class if the application exits.
@@ -320,7 +657,7 @@ namespace Hardcodet.Wpf.TaskbarNotification
{
Dispose();
}
-
+
///
/// This destructor will run only if the
@@ -372,7 +709,7 @@ namespace Hardcodet.Wpf.TaskbarNotification
/// Check the property to determine whether
/// the method has already been called.
private void Dispose(bool disposing)
- {
+ {
//don't do anything if the component is already disposed
if (IsDisposed || !disposing) return;
@@ -389,10 +726,11 @@ namespace Hardcodet.Wpf.TaskbarNotification
//dispose message sink
messageSink.Dispose();
+ //remove icon
RemoveTaskbarIcon();
}
}
#endregion
}
-}
+}
\ No newline at end of file
diff --git a/Source/Sample Project/FancyPopup.xaml b/Source/Sample Project/FancyPopup.xaml
index cd1804c..c6cddc8 100644
--- a/Source/Sample Project/FancyPopup.xaml
+++ b/Source/Sample Project/FancyPopup.xaml
@@ -2,8 +2,22 @@
x:Class="Sample_Project.FancyPopup"
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"
Height="215"
Width="300" x:Name="me">
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Stretch="Fill" x:Name="image" RenderTransformOrigin="0.5,0.5" >
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -19,7 +49,7 @@
Height="Auto"
CornerRadius="6,6,6,6"
BorderThickness="3,3,3,3"
- Margin="0,0,5,5">
+ Margin="0,0,5,5" x:Name="border">
@@ -44,7 +74,16 @@
Source="Images\Info.png"
Stretch="Fill"
VerticalAlignment="Top"
- RenderTransformOrigin="0.792,0.486" />
+ RenderTransformOrigin="0.792,0.486" x:Name="image" >
+
+
+
+
+
+
+
+
+
/// A property wrapper for the
@@ -40,34 +41,6 @@ namespace Sample_Project
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
@@ -76,5 +49,6 @@ namespace Sample_Project
{
this.InitializeComponent();
}
+
}
}
\ No newline at end of file
diff --git a/Source/Sample Project/Window1.xaml b/Source/Sample Project/Window1.xaml
index f7b8f30..1938cd5 100644
--- a/Source/Sample Project/Window1.xaml
+++ b/Source/Sample Project/Window1.xaml
@@ -25,117 +25,25 @@
TypeName="tb:PopupActivationMode" />
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
+
+
+
+
+
@@ -148,6 +56,13 @@
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ FontSize="14">