mirror of
https://github.com/ckaczor/wpf-notifyicon.git
synced 2026-02-16 11:08:30 -05:00
Work in progress on interactive tooltips as well as tooltip/popup unification. This is currently a bit of a mess...
This commit is contained in:
@@ -25,7 +25,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
namespace Hardcodet.Wpf.TaskbarNotification.Interop
|
namespace Hardcodet.Wpf.TaskbarNotification.Interop
|
||||||
{
|
{
|
||||||
@@ -290,10 +289,12 @@ namespace Hardcodet.Wpf.TaskbarNotification.Interop
|
|||||||
MouseEventReceived(MouseEvent.BalloonToolTipClicked);
|
MouseEventReceived(MouseEvent.BalloonToolTipClicked);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
//show ToolTip
|
||||||
case 0x406:
|
case 0x406:
|
||||||
ChangeToolTipStateRequest(true);
|
ChangeToolTipStateRequest(true);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
//hide ToolTip
|
||||||
case 0x407:
|
case 0x407:
|
||||||
ChangeToolTipStateRequest(false);
|
ChangeToolTipStateRequest(false);
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -53,6 +53,7 @@
|
|||||||
<Compile Include="Interop\Point.cs" />
|
<Compile Include="Interop\Point.cs" />
|
||||||
<Compile Include="Interop\WindowClass.cs" />
|
<Compile Include="Interop\WindowClass.cs" />
|
||||||
<Compile Include="PopupActivationMode.cs" />
|
<Compile Include="PopupActivationMode.cs" />
|
||||||
|
<Compile Include="PopupHandler.cs" />
|
||||||
<Compile Include="RoutedEventHelper.cs" />
|
<Compile Include="RoutedEventHelper.cs" />
|
||||||
<Compile Include="Interop\WinApi.cs" />
|
<Compile Include="Interop\WinApi.cs" />
|
||||||
<Compile Include="Interop\MouseEvent.cs" />
|
<Compile Include="Interop\MouseEvent.cs" />
|
||||||
@@ -68,6 +69,7 @@
|
|||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="TaskbarIcon.cs" />
|
<Compile Include="TaskbarIcon.cs" />
|
||||||
<Compile Include="TaskbarIcon.Declarations.cs" />
|
<Compile Include="TaskbarIcon.Declarations.cs" />
|
||||||
|
<Compile Include="ToolTipObserver.cs" />
|
||||||
<Compile Include="Util.cs" />
|
<Compile Include="Util.cs" />
|
||||||
<None Include="Diagrams\TaskbarIcon Overview.cd" />
|
<None Include="Diagrams\TaskbarIcon Overview.cd" />
|
||||||
<AppDesigner Include="Properties\" />
|
<AppDesigner Include="Properties\" />
|
||||||
|
|||||||
@@ -70,6 +70,17 @@ namespace Hardcodet.Wpf.TaskbarNotification
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The item is displayed whenever a click occurs.
|
/// The item is displayed whenever a click occurs.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
AnyClick,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The mouse is hovering over the Notify Icon (ToolTip behavior).
|
||||||
|
/// </summary>
|
||||||
|
Hover,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The item is displayed whenever a click occurs, or the mouse hovers
|
||||||
|
/// over the icon (ToolTip behavior).
|
||||||
|
/// </summary>
|
||||||
All
|
All
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
344
Hardcodet.NotifyIcon.Wpf/Source/NotifyIconWpf/PopupHandler.cs
Normal file
344
Hardcodet.NotifyIcon.Wpf/Source/NotifyIconWpf/PopupHandler.cs
Normal file
@@ -0,0 +1,344 @@
|
|||||||
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Windows;
|
||||||
|
using System.Windows.Controls.Primitives;
|
||||||
|
using System.Windows.Data;
|
||||||
|
using System.Windows.Input;
|
||||||
|
using System.Windows.Interop;
|
||||||
|
using System.Windows.Threading;
|
||||||
|
using Hardcodet.Wpf.TaskbarNotification.Interop;
|
||||||
|
|
||||||
|
namespace Hardcodet.Wpf.TaskbarNotification
|
||||||
|
{
|
||||||
|
|
||||||
|
public class PopupSetting : DependencyObject
|
||||||
|
{
|
||||||
|
#region Trigger
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Trigger Dependency Property
|
||||||
|
/// </summary>
|
||||||
|
public static readonly DependencyProperty TriggerProperty =
|
||||||
|
DependencyProperty.Register("Trigger", typeof(PopupActivationMode), typeof(PopupSetting),
|
||||||
|
new FrameworkPropertyMetadata((PopupActivationMode)PopupActivationMode.AnyClick));
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the Trigger property. This dependency property
|
||||||
|
/// indicates ....
|
||||||
|
/// </summary>
|
||||||
|
public PopupActivationMode Trigger
|
||||||
|
{
|
||||||
|
get { return (PopupActivationMode)GetValue(TriggerProperty); }
|
||||||
|
set { SetValue(TriggerProperty, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#region PreviewOpen
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// PreviewOpen Routed Event
|
||||||
|
/// </summary>
|
||||||
|
public static readonly RoutedEvent PreviewOpenEvent = EventManager.RegisterRoutedEvent("PreviewOpen",
|
||||||
|
RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(PopupSetting));
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Occurs when ...
|
||||||
|
/// </summary>
|
||||||
|
public event RoutedEventHandler PreviewOpen
|
||||||
|
{
|
||||||
|
add { RoutedEventHelper.AddHandler(this, PreviewOpenEvent, value); }
|
||||||
|
remove { RoutedEventHelper.RemoveHandler(this, PreviewOpenEvent, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A helper method to raise the PreviewOpen event.
|
||||||
|
/// </summary>
|
||||||
|
protected RoutedEventArgs RaisePreviewOpenEvent()
|
||||||
|
{
|
||||||
|
return RaisePreviewOpenEvent(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A static helper method to raise the PreviewOpen event on a target element.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="target">UIElement or ContentElement on which to raise the event</param>
|
||||||
|
internal static RoutedEventArgs RaisePreviewOpenEvent(DependencyObject target)
|
||||||
|
{
|
||||||
|
if (target == null) return null;
|
||||||
|
|
||||||
|
RoutedEventArgs args = new RoutedEventArgs();
|
||||||
|
args.RoutedEvent = PreviewOpenEvent;
|
||||||
|
RoutedEventHelper.RaiseEvent(target, args);
|
||||||
|
return args;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Maintains a given popup, and optionally tracks activation / closing.
|
||||||
|
/// </summary>
|
||||||
|
public class PopupHandler
|
||||||
|
{
|
||||||
|
public FrameworkElement Parent { get; private set; }
|
||||||
|
public DispatcherTimer Timer { get; private set; }
|
||||||
|
|
||||||
|
private Action scheduledTimerAction;
|
||||||
|
private Popup managedPopup;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Indicates whether the popup is being activated if the
|
||||||
|
/// user enters the mouse.
|
||||||
|
/// </summary>
|
||||||
|
public bool ActivateOnMouseEnterPopup { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Indicates whether the popup is being deactivated if the
|
||||||
|
/// user leaves the popup. Can be set to false in order to
|
||||||
|
/// keep a popup open indefinitely once the user hovered
|
||||||
|
/// over it. Defaults to <c>true</c>.
|
||||||
|
/// </summary>
|
||||||
|
public bool CloseOnMouseLeavePopup { get; set; }
|
||||||
|
|
||||||
|
public Popup ManagedPopup
|
||||||
|
{
|
||||||
|
get { return managedPopup; }
|
||||||
|
set
|
||||||
|
{
|
||||||
|
ResetSchedule();
|
||||||
|
|
||||||
|
var oldPopup = managedPopup;
|
||||||
|
managedPopup = value;
|
||||||
|
InitPopup(oldPopup);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Func<bool> PreviewOpenFunc { get; set; }
|
||||||
|
public Action PostOpenAction { get; set; }
|
||||||
|
|
||||||
|
public Func<bool> PreviewCloseFunc { get; set; }
|
||||||
|
public Action PostCloseAction { get; set; }
|
||||||
|
|
||||||
|
public int OpenPopupDelay { get; set; }
|
||||||
|
public int ClosePopupDelay { get; set; }
|
||||||
|
|
||||||
|
public PopupHandler(FrameworkElement parent)
|
||||||
|
{
|
||||||
|
Parent = parent;
|
||||||
|
Timer = new DispatcherTimer(DispatcherPriority.Normal, parent.Dispatcher);
|
||||||
|
Timer.Tick += OnTimerElapsed;
|
||||||
|
CloseOnMouseLeavePopup = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void InitPopup(Popup oldPopup)
|
||||||
|
{
|
||||||
|
if (oldPopup != null)
|
||||||
|
{
|
||||||
|
oldPopup.MouseEnter -= OnPopupMouseEnter;
|
||||||
|
oldPopup.MouseLeave -= OnPopupMouseLeave;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ManagedPopup == null) return;
|
||||||
|
|
||||||
|
ManagedPopup.MouseEnter += OnPopupMouseEnter;
|
||||||
|
ManagedPopup.MouseLeave += OnPopupMouseLeave;
|
||||||
|
|
||||||
|
//hook up the popup with the data context of it's associated object
|
||||||
|
//in case we have content with bindings
|
||||||
|
var binding = new Binding
|
||||||
|
{
|
||||||
|
Path = new PropertyPath(FrameworkElement.DataContextProperty),
|
||||||
|
Mode = BindingMode.OneWay,
|
||||||
|
Source = Parent
|
||||||
|
};
|
||||||
|
BindingOperations.SetBinding(ManagedPopup, FrameworkElement.DataContextProperty, binding);
|
||||||
|
|
||||||
|
//force template application so we can switch visual states
|
||||||
|
var fe = ManagedPopup.Child as FrameworkElement;
|
||||||
|
if (fe != null)
|
||||||
|
{
|
||||||
|
fe.ApplyTemplate();
|
||||||
|
GoToState("Closed", false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void ShowPopup()
|
||||||
|
{
|
||||||
|
Debug.WriteLine("show popup request");
|
||||||
|
|
||||||
|
if (ManagedPopup == null) return;
|
||||||
|
|
||||||
|
//if the popup is still open, open it immediatly
|
||||||
|
if (ManagedPopup.IsOpen)
|
||||||
|
{
|
||||||
|
//the popup is already open (maybe was scheduled to close)
|
||||||
|
//-> abort schedule, set back to active state
|
||||||
|
ResetSchedule();
|
||||||
|
GoToState("Showing");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//validate whether we can open
|
||||||
|
bool isHandled = PreviewOpenFunc();
|
||||||
|
if (isHandled) return;
|
||||||
|
|
||||||
|
Schedule(OpenPopupDelay, () =>
|
||||||
|
{
|
||||||
|
Debug.WriteLine("showing popup");
|
||||||
|
|
||||||
|
//Open the popup, then start transition
|
||||||
|
ManagedPopup.IsOpen = true;
|
||||||
|
GoToState("Showing");
|
||||||
|
|
||||||
|
FocusPopup();
|
||||||
|
|
||||||
|
PostOpenAction();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Schedules closing the maintained popup, if it is currently open, using
|
||||||
|
/// the configured <see cref="ClosePopupDelay"/>, if set.
|
||||||
|
/// </summary>
|
||||||
|
public void ScheduleClosePopup()
|
||||||
|
{
|
||||||
|
Debug.WriteLine("close request");
|
||||||
|
|
||||||
|
if (ManagedPopup == null || !ManagedPopup.IsOpen) return;
|
||||||
|
|
||||||
|
//validate whether we can close
|
||||||
|
bool isHandled = PreviewCloseFunc();
|
||||||
|
if (isHandled) return;
|
||||||
|
|
||||||
|
//start hiding immediately
|
||||||
|
GoToState("Hiding");
|
||||||
|
|
||||||
|
Schedule(ClosePopupDelay, () =>
|
||||||
|
{
|
||||||
|
Debug.WriteLine("Close request scheduled");
|
||||||
|
GoToState("Closed");
|
||||||
|
ManagedPopup.IsOpen = false;
|
||||||
|
PostCloseAction();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Suppresses a scheduled hiding of the popup, and transitions the content
|
||||||
|
/// back into active state.
|
||||||
|
/// </summary>
|
||||||
|
private void OnPopupMouseEnter(object sender, MouseEventArgs e)
|
||||||
|
{
|
||||||
|
if (!ActivateOnMouseEnterPopup) return;
|
||||||
|
|
||||||
|
Debug.WriteLine("popup enter");
|
||||||
|
|
||||||
|
//the popup is still open - just supress any scheduled action
|
||||||
|
ResetSchedule();
|
||||||
|
GoToState("Active");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Schedules hiding of the control if the user moves away from an interactive popup.
|
||||||
|
/// </summary>
|
||||||
|
private void OnPopupMouseLeave(object sender, MouseEventArgs e)
|
||||||
|
{
|
||||||
|
if (!CloseOnMouseLeavePopup) return;
|
||||||
|
|
||||||
|
Debug.WriteLine("mouse leave");
|
||||||
|
ScheduleClosePopup();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Performs a pending action.
|
||||||
|
/// </summary>
|
||||||
|
private void OnTimerElapsed(object sender, EventArgs eventArgs)
|
||||||
|
{
|
||||||
|
lock (Timer)
|
||||||
|
{
|
||||||
|
Timer.Stop();
|
||||||
|
|
||||||
|
var action = scheduledTimerAction;
|
||||||
|
scheduledTimerAction = null;
|
||||||
|
|
||||||
|
//cache action and set timer action to null before invoking it, not the other way around
|
||||||
|
//(that action could cause the timer action to be reassigned)
|
||||||
|
if (action != null)
|
||||||
|
{
|
||||||
|
action();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ResetSchedule()
|
||||||
|
{
|
||||||
|
lock (Timer)
|
||||||
|
{
|
||||||
|
Timer.Stop();
|
||||||
|
scheduledTimerAction = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Schedules an action to be executed on the next timer tick. Resets the timer
|
||||||
|
/// and replaces any other pending action.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Customize this if you need custom delays to show / hide / fade tooltips by
|
||||||
|
/// simply changing the timer interval depending on the state change.
|
||||||
|
/// </remarks>
|
||||||
|
private void Schedule(int delay, Action action)
|
||||||
|
{
|
||||||
|
lock (Timer)
|
||||||
|
{
|
||||||
|
Timer.Stop();
|
||||||
|
|
||||||
|
if (delay == 0)
|
||||||
|
{
|
||||||
|
//if there is no delay, execute action immediately
|
||||||
|
scheduledTimerAction = null;
|
||||||
|
action();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Timer.Interval = TimeSpan.FromMilliseconds(delay);
|
||||||
|
scheduledTimerAction = action;
|
||||||
|
Timer.Start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GoToState(string stateName, bool useTransitions = true)
|
||||||
|
{
|
||||||
|
var fe = ManagedPopup.Child as FrameworkElement;
|
||||||
|
if (fe == null) return;
|
||||||
|
|
||||||
|
VisualStateManager.GoToState(fe, stateName, useTransitions);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void FocusPopup()
|
||||||
|
{
|
||||||
|
IntPtr handle = IntPtr.Zero;
|
||||||
|
if (ManagedPopup.Child != null)
|
||||||
|
{
|
||||||
|
//try to get a handle on the popup itself (via its child)
|
||||||
|
HwndSource source = (HwndSource)PresentationSource.FromVisual(ManagedPopup.Child);
|
||||||
|
if (source != null) handle = source.Handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO if we don't have a handle for the popup, fall back to the message sink
|
||||||
|
//if (handle == IntPtr.Zero) handle = notifyIcon.messageSink.MessageWindowHandle;
|
||||||
|
|
||||||
|
//activate either popup or message sink to track deactivation.
|
||||||
|
//otherwise, the popup does not close if the user clicks somewhere else
|
||||||
|
WinApi.SetForegroundWindow(handle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -96,7 +96,7 @@ namespace Hardcodet.Wpf.TaskbarNotification
|
|||||||
/// TrayToolTipResolved Read-Only Dependency Property
|
/// TrayToolTipResolved Read-Only Dependency Property
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private static readonly DependencyPropertyKey TrayToolTipResolvedPropertyKey
|
private static readonly DependencyPropertyKey TrayToolTipResolvedPropertyKey
|
||||||
= DependencyProperty.RegisterReadOnly("TrayToolTipResolved", typeof (ToolTip), typeof (TaskbarIcon),
|
= DependencyProperty.RegisterReadOnly("TrayToolTipResolved", typeof(Popup), typeof(TaskbarIcon),
|
||||||
new FrameworkPropertyMetadata(null));
|
new FrameworkPropertyMetadata(null));
|
||||||
|
|
||||||
|
|
||||||
@@ -116,9 +116,9 @@ namespace Hardcodet.Wpf.TaskbarNotification
|
|||||||
[Category(CategoryName)]
|
[Category(CategoryName)]
|
||||||
[Browsable(true)]
|
[Browsable(true)]
|
||||||
[Bindable(true)]
|
[Bindable(true)]
|
||||||
public ToolTip TrayToolTipResolved
|
public Popup TrayToolTipResolved
|
||||||
{
|
{
|
||||||
get { return (ToolTip) GetValue(TrayToolTipResolvedProperty); }
|
get { return (Popup)GetValue(TrayToolTipResolvedProperty); }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -126,7 +126,7 @@ namespace Hardcodet.Wpf.TaskbarNotification
|
|||||||
/// property.
|
/// property.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="value">The new value for the property.</param>
|
/// <param name="value">The new value for the property.</param>
|
||||||
protected void SetTrayToolTipResolved(ToolTip value)
|
protected void SetTrayToolTipResolved(Popup value)
|
||||||
{
|
{
|
||||||
SetValue(TrayToolTipResolvedPropertyKey, value);
|
SetValue(TrayToolTipResolvedPropertyKey, value);
|
||||||
}
|
}
|
||||||
@@ -304,7 +304,7 @@ namespace Hardcodet.Wpf.TaskbarNotification
|
|||||||
//do not touch tooltips if we have a custom tooltip element
|
//do not touch tooltips if we have a custom tooltip element
|
||||||
if (TrayToolTip == null)
|
if (TrayToolTip == null)
|
||||||
{
|
{
|
||||||
ToolTip currentToolTip = TrayToolTipResolved;
|
Popup currentToolTip = TrayToolTipResolved;
|
||||||
if (currentToolTip == null)
|
if (currentToolTip == null)
|
||||||
{
|
{
|
||||||
//if we don't have a wrapper tooltip for the tooltip text, create it now
|
//if we don't have a wrapper tooltip for the tooltip text, create it now
|
||||||
@@ -313,7 +313,7 @@ namespace Hardcodet.Wpf.TaskbarNotification
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
//if we have a wrapper tooltip that shows the old tooltip text, just update content
|
//if we have a wrapper tooltip that shows the old tooltip text, just update content
|
||||||
currentToolTip.Content = e.NewValue;
|
currentToolTip.Child = new ContentControl {Content = e.NewValue}; //TODO hackery
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -375,7 +375,7 @@ namespace Hardcodet.Wpf.TaskbarNotification
|
|||||||
/// <param name="e">Provides information about the updated property.</param>
|
/// <param name="e">Provides information about the updated property.</param>
|
||||||
private void OnTrayToolTipPropertyChanged(DependencyPropertyChangedEventArgs e)
|
private void OnTrayToolTipPropertyChanged(DependencyPropertyChangedEventArgs e)
|
||||||
{
|
{
|
||||||
//recreate tooltip control
|
//recreate tooltip popup
|
||||||
CreateCustomToolTip();
|
CreateCustomToolTip();
|
||||||
|
|
||||||
if (e.OldValue != null)
|
if (e.OldValue != null)
|
||||||
@@ -388,6 +388,8 @@ namespace Hardcodet.Wpf.TaskbarNotification
|
|||||||
{
|
{
|
||||||
//set this taskbar icon as a reference to the new tooltip element
|
//set this taskbar icon as a reference to the new tooltip element
|
||||||
SetParentTaskbarIcon((DependencyObject) e.NewValue, this);
|
SetParentTaskbarIcon((DependencyObject) e.NewValue, this);
|
||||||
|
|
||||||
|
TrayToolTip.IsHitTestVisible = IsToolTipInteractive;
|
||||||
}
|
}
|
||||||
|
|
||||||
//update tooltip settings - needed to make sure a string is set, even
|
//update tooltip settings - needed to make sure a string is set, even
|
||||||
@@ -398,6 +400,37 @@ namespace Hardcodet.Wpf.TaskbarNotification
|
|||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
#region IsToolTipInteractive
|
||||||
|
|
||||||
|
public static readonly DependencyProperty IsToolTipInteractiveProperty =
|
||||||
|
DependencyProperty.Register("IsToolTipInteractive", typeof(bool), typeof(TaskbarIcon),
|
||||||
|
new FrameworkPropertyMetadata(true, OnIsToolTipInteractiveChanged));
|
||||||
|
|
||||||
|
[Bindable(true)]
|
||||||
|
[Category(CategoryName)]
|
||||||
|
[DisplayName("Clickable ToolTip")]
|
||||||
|
[Description("Whether the ToolTip can be selected / clicked on.")]
|
||||||
|
public bool IsToolTipInteractive
|
||||||
|
{
|
||||||
|
get { return (bool)GetValue(IsToolTipInteractiveProperty); }
|
||||||
|
set { SetValue(IsToolTipInteractiveProperty, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handles changes to the IsToolTipInteractive property.
|
||||||
|
/// </summary>
|
||||||
|
private static void OnIsToolTipInteractiveChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||||
|
{
|
||||||
|
var target = (TaskbarIcon)d;
|
||||||
|
|
||||||
|
if (target.TrayToolTip != null)
|
||||||
|
{
|
||||||
|
target.TrayToolTip.IsHitTestVisible = target.IsToolTipInteractive;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
#region TrayPopup dependency property
|
#region TrayPopup dependency property
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ using System.Threading;
|
|||||||
using System.Windows;
|
using System.Windows;
|
||||||
using System.Windows.Controls;
|
using System.Windows.Controls;
|
||||||
using System.Windows.Controls.Primitives;
|
using System.Windows.Controls.Primitives;
|
||||||
|
using System.Windows.Data;
|
||||||
using System.Windows.Interop;
|
using System.Windows.Interop;
|
||||||
using System.Windows.Threading;
|
using System.Windows.Threading;
|
||||||
using Hardcodet.Wpf.TaskbarNotification.Interop;
|
using Hardcodet.Wpf.TaskbarNotification.Interop;
|
||||||
@@ -67,6 +68,11 @@ namespace Hardcodet.Wpf.TaskbarNotification
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private readonly Timer singleClickTimer;
|
private readonly Timer singleClickTimer;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Maintains opened tooltip popups.
|
||||||
|
/// </summary>
|
||||||
|
private ToolTipObserver toolTipObserver;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A timer that is used to close open balloon tooltips.
|
/// A timer that is used to close open balloon tooltips.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -466,21 +472,23 @@ namespace Hardcodet.Wpf.TaskbarNotification
|
|||||||
private void OnToolTipChange(bool visible)
|
private void OnToolTipChange(bool visible)
|
||||||
{
|
{
|
||||||
//if we don't have a tooltip, there's nothing to do here...
|
//if we don't have a tooltip, there's nothing to do here...
|
||||||
if (TrayToolTipResolved == null) return;
|
//if (TrayToolTipResolved == null) return;
|
||||||
|
|
||||||
if (visible)
|
if (visible)
|
||||||
{
|
{
|
||||||
if (IsPopupOpen)
|
if (IsPopupOpen) //TODO return if IsEnabled is false
|
||||||
{
|
{
|
||||||
//ignore if we are already displaying something down there
|
//ignore if we are already displaying something down there
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var args = RaisePreviewTrayToolTipOpenEvent();
|
var args = RaisePreviewTrayToolTipOpenEvent();
|
||||||
|
|
||||||
|
//if the user handled the event by herself, we're done
|
||||||
if (args.Handled) return;
|
if (args.Handled) return;
|
||||||
|
|
||||||
TrayToolTipResolved.IsOpen = true;
|
toolTipObserver.ShowToolTip();
|
||||||
|
|
||||||
//raise attached event first
|
//raise attached event first
|
||||||
if (TrayToolTip != null) RaiseToolTipOpenedEvent(TrayToolTip);
|
if (TrayToolTip != null) RaiseToolTipOpenedEvent(TrayToolTip);
|
||||||
|
|
||||||
@@ -493,11 +501,16 @@ namespace Hardcodet.Wpf.TaskbarNotification
|
|||||||
if (args.Handled) return;
|
if (args.Handled) return;
|
||||||
|
|
||||||
//raise attached event first
|
//raise attached event first
|
||||||
if (TrayToolTip != null) RaiseToolTipCloseEvent(TrayToolTip);
|
if (TrayToolTip != null) RaiseToolTipCloseEvent(TrayToolTip); //TODO this must be fired with a delay once the observer really closed it!
|
||||||
|
|
||||||
TrayToolTipResolved.IsOpen = false;
|
//TrayToolTipResolved.IsOpen = false;
|
||||||
|
if (!toolTipObserver.IsMouseOverToolTip)
|
||||||
|
{
|
||||||
|
toolTipObserver.BeginCloseToolTip();
|
||||||
|
}
|
||||||
|
|
||||||
//bubble event
|
//bubble event
|
||||||
|
//TODO this must be fired with a delay once the observer really closed it!
|
||||||
RaiseTrayToolTipCloseEvent();
|
RaiseTrayToolTipCloseEvent();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -519,44 +532,64 @@ namespace Hardcodet.Wpf.TaskbarNotification
|
|||||||
private void CreateCustomToolTip()
|
private void CreateCustomToolTip()
|
||||||
{
|
{
|
||||||
//check if the item itself is a tooltip
|
//check if the item itself is a tooltip
|
||||||
ToolTip tt = TrayToolTip as ToolTip;
|
var popup = TrayToolTip as Popup;
|
||||||
|
|
||||||
if (tt == null && TrayToolTip != null)
|
if (popup == null && TrayToolTip != null)
|
||||||
{
|
{
|
||||||
//create an invisible wrapper tooltip that hosts the UIElement
|
//create an invisible popup that hosts the UIElement
|
||||||
tt = new ToolTip();
|
popup = new Popup();
|
||||||
tt.Placement = PlacementMode.Mouse;
|
popup.AllowsTransparency = true;
|
||||||
|
|
||||||
|
//don't animate by default - devs can use attached
|
||||||
|
//events or override
|
||||||
|
popup.PopupAnimation = PopupAnimation.None; //TODO make this configurable
|
||||||
|
|
||||||
|
//the CreateRootPopup method outputs binding errors in the debug window because
|
||||||
|
//it tries to bind to "Popup-specific" properties in case they are provided by the child.
|
||||||
|
//We don't need that so just assign the control as the child.
|
||||||
|
popup.Child = TrayToolTip;
|
||||||
|
|
||||||
//do *not* set the placement target, as it causes the popup to become hidden if the
|
//do *not* set the placement target, as it causes the popup to become hidden if the
|
||||||
//TaskbarIcon's parent is hidden, too. At runtime, the parent can be resolved through
|
//TaskbarIcon's parent is hidden, too. At runtime, the parent can be resolved through
|
||||||
//the ParentTaskbarIcon attached dependency property:
|
//the ParentTaskbarIcon attached dependency property:
|
||||||
//tt.PlacementTarget = this;
|
//popup.PlacementTarget = this;
|
||||||
|
|
||||||
//make sure the tooltip is invisible
|
popup.Placement = PlacementMode.Mouse;
|
||||||
tt.HasDropShadow = false;
|
|
||||||
tt.BorderThickness = new Thickness(0);
|
|
||||||
tt.Background = System.Windows.Media.Brushes.Transparent;
|
|
||||||
|
|
||||||
//setting the
|
|
||||||
tt.StaysOpen = true;
|
|
||||||
tt.Content = TrayToolTip;
|
|
||||||
}
|
}
|
||||||
else if (tt == null && !String.IsNullOrEmpty(ToolTipText))
|
else if (popup == null && !String.IsNullOrEmpty(ToolTipText))
|
||||||
{
|
{
|
||||||
//create a simple tooltip for the ToolTipText string
|
//TODO create means to show a regular tooltip instead of this hackery
|
||||||
tt = new ToolTip();
|
var toolTip = new ToolTip();
|
||||||
tt.Content = ToolTipText;
|
popup = new Popup(); //TODO hack
|
||||||
|
popup.ToolTip = toolTip;
|
||||||
|
toolTip.Content = ToolTipText;
|
||||||
|
|
||||||
|
//wire max width
|
||||||
|
var binding = new Binding
|
||||||
|
{
|
||||||
|
Path = new PropertyPath(Popup.IsOpenProperty),
|
||||||
|
Mode = BindingMode.OneWay,
|
||||||
|
Source = popup
|
||||||
|
};
|
||||||
|
BindingOperations.SetBinding(toolTip, System.Windows.Controls.ToolTip.IsOpenProperty, binding);
|
||||||
}
|
}
|
||||||
|
|
||||||
//the tooltip explicitly gets the DataContext of this instance.
|
//the tooltip explicitly gets the DataContext of this instance.
|
||||||
//If there is no DataContext, the TaskbarIcon assigns itself
|
//If there is no DataContext, the TaskbarIcon assigns itself
|
||||||
if (tt != null)
|
if (popup != null)
|
||||||
{
|
{
|
||||||
UpdateDataContext(tt, null, DataContext);
|
UpdateDataContext(popup, null, DataContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
//store a reference to the used tooltip
|
//store a reference to the used tooltip
|
||||||
SetTrayToolTipResolved(tt);
|
SetTrayToolTipResolved(popup);
|
||||||
|
|
||||||
|
if (popup != null)
|
||||||
|
{
|
||||||
|
//TODO that should just become a property setter on the observer, with null allowed
|
||||||
|
if(toolTipObserver == null) toolTipObserver = new ToolTipObserver(this, 500);
|
||||||
|
toolTipObserver.Popup = popup;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -666,20 +699,8 @@ namespace Hardcodet.Wpf.TaskbarNotification
|
|||||||
//open popup
|
//open popup
|
||||||
TrayPopupResolved.IsOpen = true;
|
TrayPopupResolved.IsOpen = true;
|
||||||
|
|
||||||
IntPtr handle = IntPtr.Zero;
|
//activate element
|
||||||
if (TrayPopupResolved.Child != null)
|
FocusElement(TrayPopupResolved);
|
||||||
{
|
|
||||||
//try to get a handle on the popup itself (via its child)
|
|
||||||
HwndSource source = (HwndSource) PresentationSource.FromVisual(TrayPopupResolved.Child);
|
|
||||||
if (source != null) handle = source.Handle;
|
|
||||||
}
|
|
||||||
|
|
||||||
//if we don't have a handle for the popup, fall back to the message sink
|
|
||||||
if (handle == IntPtr.Zero) handle = messageSink.MessageWindowHandle;
|
|
||||||
|
|
||||||
//activate either popup or message sink to track deactivation.
|
|
||||||
//otherwise, the popup does not close if the user clicks somewhere else
|
|
||||||
WinApi.SetForegroundWindow(handle);
|
|
||||||
|
|
||||||
//raise attached event - item should never be null unless developers
|
//raise attached event - item should never be null unless developers
|
||||||
//changed the CustomPopup directly...
|
//changed the CustomPopup directly...
|
||||||
@@ -690,6 +711,29 @@ namespace Hardcodet.Wpf.TaskbarNotification
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Activates a given UI element.
|
||||||
|
/// </summary>
|
||||||
|
private void FocusElement(Popup popup)
|
||||||
|
{
|
||||||
|
IntPtr handle = IntPtr.Zero;
|
||||||
|
if (popup.Child != null)
|
||||||
|
{
|
||||||
|
//try to get a handle on the popup itself (via its child)
|
||||||
|
HwndSource source = (HwndSource)PresentationSource.FromVisual(popup.Child);
|
||||||
|
if (source != null) handle = source.Handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
//if we don't have a handle for the popup, fall back to the message sink
|
||||||
|
if (handle == IntPtr.Zero) handle = messageSink.MessageWindowHandle;
|
||||||
|
|
||||||
|
//activate either popup or message sink to track deactivation.
|
||||||
|
//otherwise, the popup does not close if the user clicks somewhere else
|
||||||
|
WinApi.SetForegroundWindow(handle);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Context Menu
|
#region Context Menu
|
||||||
|
|||||||
266
Hardcodet.NotifyIcon.Wpf/Source/NotifyIconWpf/ToolTipObserver.cs
Normal file
266
Hardcodet.NotifyIcon.Wpf/Source/NotifyIconWpf/ToolTipObserver.cs
Normal file
@@ -0,0 +1,266 @@
|
|||||||
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Windows;
|
||||||
|
using System.Windows.Controls.Primitives;
|
||||||
|
using System.Windows.Data;
|
||||||
|
using System.Windows.Input;
|
||||||
|
using System.Windows.Interop;
|
||||||
|
using System.Windows.Threading;
|
||||||
|
using Hardcodet.Wpf.TaskbarNotification.Interop;
|
||||||
|
|
||||||
|
namespace Hardcodet.Wpf.TaskbarNotification
|
||||||
|
{
|
||||||
|
public enum CloseMode
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Popup is not closed manually.
|
||||||
|
/// </summary>
|
||||||
|
None,
|
||||||
|
|
||||||
|
OnLeavePopup,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Maintains a currently displayed, optionally interactive "ToolTip" (which is infact a popup).
|
||||||
|
/// </summary>
|
||||||
|
internal class ToolTipObserver
|
||||||
|
{
|
||||||
|
private readonly TaskbarIcon notifyIcon;
|
||||||
|
private readonly DispatcherTimer timer;
|
||||||
|
private Action timerAction;
|
||||||
|
|
||||||
|
public Popup Popup
|
||||||
|
{
|
||||||
|
get { return popup; }
|
||||||
|
set
|
||||||
|
{
|
||||||
|
popup = value;
|
||||||
|
InitControls();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Caches whether the mouse is currently over the popup. In that case, ignore
|
||||||
|
/// the notify's request to close the popup (leads into an endless open/close cycle).
|
||||||
|
/// </summary>
|
||||||
|
private bool isMouseOverPopup;
|
||||||
|
|
||||||
|
private Popup popup;
|
||||||
|
|
||||||
|
public bool IsMouseOverToolTip
|
||||||
|
{
|
||||||
|
//TODO replace with better call
|
||||||
|
get { return popup != null && popup.IsMouseOver; }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public ToolTipObserver(TaskbarIcon notifyIcon, int delay)
|
||||||
|
{
|
||||||
|
this.notifyIcon = notifyIcon;
|
||||||
|
|
||||||
|
timer = new DispatcherTimer(TimeSpan.FromMilliseconds(delay), DispatcherPriority.Normal, OnTimerElapsed, notifyIcon.Dispatcher);
|
||||||
|
timer.IsEnabled = false;
|
||||||
|
|
||||||
|
//EventManager.RegisterClassHandler(type, Mouse.MouseEnterEvent, (Delegate)new MouseEventHandler(UIElement.OnMouseEnterThunk), false);
|
||||||
|
//EventManager.RegisterClassHandler(type, Mouse.MouseLeaveEvent, (Delegate)new MouseEventHandler(UIElement.OnMouseLeaveThunk), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Shows the popup. Typically invoked if the user hovers over the NotifyIcon.
|
||||||
|
/// </summary>
|
||||||
|
public void ShowToolTip()
|
||||||
|
{
|
||||||
|
Debug.WriteLine("show request from NI");
|
||||||
|
|
||||||
|
//don't do anything
|
||||||
|
if (!notifyIcon.IsEnabled) return; //TODO should move into the control, also for other operations
|
||||||
|
|
||||||
|
//if the popup is still open, open it immediatly
|
||||||
|
if (Popup != null && Popup.IsOpen)
|
||||||
|
{
|
||||||
|
Debug.WriteLine("show request scheduled");
|
||||||
|
Schedule(() => { }); //reset schedule
|
||||||
|
GoToState("Showing");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Schedule(() =>
|
||||||
|
{
|
||||||
|
Debug.WriteLine("showing popup");
|
||||||
|
Popup.IsOpen = true; //show, then transition
|
||||||
|
GoToState("Showing");
|
||||||
|
|
||||||
|
IntPtr handle = IntPtr.Zero;
|
||||||
|
Popup ttPopup = notifyIcon.TrayToolTipResolved;
|
||||||
|
if (ttPopup.Child != null)
|
||||||
|
{
|
||||||
|
//try to get a handle on the popup itself (via its child)
|
||||||
|
HwndSource source = (HwndSource)PresentationSource.FromVisual(ttPopup.Child);
|
||||||
|
if (source != null) handle = source.Handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
//if we don't have a handle for the popup, fall back to the message sink
|
||||||
|
//if (handle == IntPtr.Zero) handle = notifyIcon.messageSink.MessageWindowHandle;
|
||||||
|
|
||||||
|
//activate either popup or message sink to track deactivation.
|
||||||
|
//otherwise, the popup does not close if the user clicks somewhere else
|
||||||
|
WinApi.SetForegroundWindow(handle);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Transitions the popup into a closed state.
|
||||||
|
/// </summary>
|
||||||
|
public void BeginCloseToolTip()
|
||||||
|
{
|
||||||
|
Debug.WriteLine("close request from notify icon");
|
||||||
|
|
||||||
|
|
||||||
|
//Popup.InputHitTest(p == null)
|
||||||
|
|
||||||
|
if (Popup.IsMouseOver)
|
||||||
|
{
|
||||||
|
Debug.WriteLine("ignoring close request since mouse is over tooltip");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Popup == null) return;
|
||||||
|
|
||||||
|
//start fading immediately if animation is programmed that way
|
||||||
|
GoToState("Hiding");
|
||||||
|
|
||||||
|
Schedule(() =>
|
||||||
|
{
|
||||||
|
Debug.WriteLine("Close request scheduled");
|
||||||
|
GoToState("Closed");
|
||||||
|
Popup.IsOpen = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Suppresses a scheduled hiding of the popup, and transitions the content
|
||||||
|
/// back into active state.
|
||||||
|
/// </summary>
|
||||||
|
private void OnPopupMouseEnter(object sender, MouseEventArgs e)
|
||||||
|
{
|
||||||
|
Debug.WriteLine("popup enter");
|
||||||
|
|
||||||
|
this.isMouseOverPopup = true;
|
||||||
|
|
||||||
|
timer.Stop(); //suppress any other actions
|
||||||
|
GoToState("Active"); //the popup is still open
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Schedules hiding of the control if the user moves away from an interactive popup.
|
||||||
|
/// </summary>
|
||||||
|
private void OnPopupMouseLeave(object sender, MouseEventArgs e)
|
||||||
|
{
|
||||||
|
Debug.WriteLine("mouse leave");
|
||||||
|
this.isMouseOverPopup = false;
|
||||||
|
|
||||||
|
Schedule(() =>
|
||||||
|
{
|
||||||
|
Debug.WriteLine("mouse leave scheduler");
|
||||||
|
|
||||||
|
//start hiding immediately
|
||||||
|
GoToState("Hiding"); //switch with a delay when leaving the popup
|
||||||
|
|
||||||
|
Schedule(() =>
|
||||||
|
{
|
||||||
|
Popup.IsOpen = false;
|
||||||
|
}); //close with yet another delay
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Inits the helper popup and tooltip controls.
|
||||||
|
/// </summary>
|
||||||
|
private void InitControls()
|
||||||
|
{
|
||||||
|
Popup.MouseEnter += OnPopupMouseEnter;
|
||||||
|
Popup.MouseLeave += OnPopupMouseLeave;
|
||||||
|
|
||||||
|
//hook up the popup with the data context of it's associated object
|
||||||
|
//in case we have content with bindings
|
||||||
|
var binding = new Binding
|
||||||
|
{
|
||||||
|
Path = new PropertyPath(FrameworkElement.DataContextProperty),
|
||||||
|
Mode = BindingMode.OneWay,
|
||||||
|
Source = notifyIcon
|
||||||
|
};
|
||||||
|
BindingOperations.SetBinding(Popup, FrameworkElement.DataContextProperty, binding);
|
||||||
|
|
||||||
|
//force template application so we can switch visual states
|
||||||
|
var fe = notifyIcon.TrayToolTip as FrameworkElement;
|
||||||
|
if (fe != null)
|
||||||
|
{
|
||||||
|
fe.ApplyTemplate();
|
||||||
|
GoToState("Closed", false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Schedules an action to be executed on the next timer tick. Resets the timer
|
||||||
|
/// and replaces any other pending action.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Customize this if you need custom delays to show / hide / fade tooltips by
|
||||||
|
/// simply changing the timer interval depending on the state change.
|
||||||
|
/// </remarks>
|
||||||
|
private void Schedule(Action action)
|
||||||
|
{
|
||||||
|
lock (timer)
|
||||||
|
{
|
||||||
|
//close whatever was scheduled
|
||||||
|
timer.Stop();
|
||||||
|
|
||||||
|
timerAction = action;
|
||||||
|
timer.Start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Performs a pending action.
|
||||||
|
/// </summary>
|
||||||
|
private void OnTimerElapsed(object sender, EventArgs eventArgs)
|
||||||
|
{
|
||||||
|
lock (timer)
|
||||||
|
{
|
||||||
|
timer.Stop();
|
||||||
|
|
||||||
|
var action = timerAction;
|
||||||
|
timerAction = null;
|
||||||
|
|
||||||
|
//cache action and set timer action to null before invoking
|
||||||
|
//(that action could cause the timeraction to be reassigned)
|
||||||
|
if (action != null)
|
||||||
|
{
|
||||||
|
action();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void GoToState(string stateName, bool useTransitions = true)
|
||||||
|
{
|
||||||
|
var fe = notifyIcon.TrayToolTip as FrameworkElement;
|
||||||
|
if (fe == null) return;
|
||||||
|
|
||||||
|
VisualStateManager.GoToState(fe, stateName, useTransitions);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -25,6 +25,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
|
using System.Linq;
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
using System.Windows.Input;
|
using System.Windows.Input;
|
||||||
using System.Windows.Media;
|
using System.Windows.Media;
|
||||||
@@ -202,14 +203,7 @@ namespace Hardcodet.Wpf.TaskbarNotification
|
|||||||
/// is a null reference.</exception>
|
/// is a null reference.</exception>
|
||||||
public static bool Is<T>(this T value, params T[] candidates)
|
public static bool Is<T>(this T value, params T[] candidates)
|
||||||
{
|
{
|
||||||
if (candidates == null) return false;
|
return candidates != null && candidates.Contains(value);
|
||||||
|
|
||||||
foreach (var t in candidates)
|
|
||||||
{
|
|
||||||
if (value.Equals(t)) return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
@@ -236,9 +230,11 @@ namespace Hardcodet.Wpf.TaskbarNotification
|
|||||||
return me.Is(MouseEvent.IconDoubleClick);
|
return me.Is(MouseEvent.IconDoubleClick);
|
||||||
case PopupActivationMode.MiddleClick:
|
case PopupActivationMode.MiddleClick:
|
||||||
return me == MouseEvent.IconMiddleMouseUp;
|
return me == MouseEvent.IconMiddleMouseUp;
|
||||||
case PopupActivationMode.All:
|
case PopupActivationMode.AnyClick:
|
||||||
//return true for everything except mouse movements
|
//return true for everything except mouse movements
|
||||||
return me != MouseEvent.MouseMove;
|
return me != MouseEvent.MouseMove;
|
||||||
|
case PopupActivationMode.All:
|
||||||
|
return true;
|
||||||
default:
|
default:
|
||||||
throw new ArgumentOutOfRangeException("activationMode");
|
throw new ArgumentOutOfRangeException("activationMode");
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user