initial checkin of yaulw (locally)
This commit is contained in:
408
WinForms/SysTray.cs
Normal file
408
WinForms/SysTray.cs
Normal file
@@ -0,0 +1,408 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Drawing;
|
||||
using System.Windows.Forms;
|
||||
using Yaulw.Thread;
|
||||
using System.Timers;
|
||||
|
||||
namespace Yaulw.WinForms
|
||||
{
|
||||
/// <remarks>
|
||||
/// Wrapper Class around .Net NotifyIcon, to make it easier to work with an System Tray Icon.
|
||||
/// Use the InitializeContextMenu Callback to create the ContextMenu.
|
||||
/// Subscripe to the MouseLeftClick, and MouseLeftDoubleClick event to get accurate events to handle,
|
||||
/// Call Show()/Hide() to show/hide the System Tray Icon, respectively.
|
||||
/// </remarks>
|
||||
public class SysTray : IDisposable
|
||||
{
|
||||
#region Public States
|
||||
|
||||
/// <summary>
|
||||
/// Allow caller to specify multipe states to show for the tray icon
|
||||
/// </summary>
|
||||
public struct TrayState
|
||||
{
|
||||
string nameOfState;
|
||||
string toolTip;
|
||||
Icon icon;
|
||||
}
|
||||
|
||||
// keep track of added States
|
||||
private List<TrayState> _states = new List<TrayState>();
|
||||
|
||||
/// <summary>
|
||||
/// Add a possible state to the collection of tray icon states
|
||||
/// </summary>
|
||||
/// <param name="state"></param>
|
||||
public void AddState(TrayState state)
|
||||
{
|
||||
_states.Add(state);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Properties
|
||||
|
||||
/// <summary>
|
||||
/// Get/Set the Icon to show on the System Tray Notification
|
||||
/// </summary>
|
||||
public Icon Icon { get { return trayNotify.Icon; } set { trayNotify.Icon = value; } }
|
||||
|
||||
/// <summary>
|
||||
/// Get/Set ToolTip to show over System Tray Notification
|
||||
/// </summary>
|
||||
public string toolTip { get { return trayNotify.Text; } set { trayNotify.Text = value; } }
|
||||
|
||||
/// <summary>
|
||||
/// Is System Tray Notification Visible?
|
||||
/// </summary>
|
||||
public bool IsVisible { get { return trayNotify.Visible; } }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Properties
|
||||
|
||||
private NotifyIcon trayNotify { get; set; }
|
||||
private ContextMenu trayMenu { get { return trayNotify.ContextMenu; } set { trayNotify.ContextMenu = value; } }
|
||||
private ContextMenuStrip trayMenuStrip { get { return trayNotify.ContextMenuStrip; } set { trayNotify.ContextMenuStrip = value; } }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Events
|
||||
|
||||
/// <summary>
|
||||
/// System Tray Left Mouse Click Delegate
|
||||
/// </summary>
|
||||
/// <param name="e">MouseEventArgs</param>
|
||||
public delegate void SingleLeftMouseClick(MouseEventArgs e);
|
||||
|
||||
/// <summary>
|
||||
/// Subscribe to get the Left Mouse Single Click Event
|
||||
/// </summary>
|
||||
public event SingleLeftMouseClick LeftMouseClick;
|
||||
|
||||
/// <summary>
|
||||
/// System Tray Left Mouse Double Click Delegate
|
||||
/// </summary>
|
||||
/// <param name="e">MouseEventArgs</param>
|
||||
public delegate void DoubleLeftMouseClick(MouseEventArgs e);
|
||||
|
||||
/// <summary>
|
||||
/// Subscribe to get the Left Mouse Double Click Event
|
||||
/// </summary>
|
||||
public event DoubleLeftMouseClick LeftMouseDoubleClick;
|
||||
|
||||
/// <summary>
|
||||
/// Initialize Context Menu Dynamically Call-back * i.e. On every Right Click *
|
||||
/// Allows the Context Menu to change depending on application state
|
||||
/// </summary>
|
||||
/// <param name="trayMenuStrip">Expected a new ContextMenuStrip to Display</param>
|
||||
/// <param name="MenuClickEventHandler">Event Handle to handle Menu Click Events</param>
|
||||
public delegate void InitializeContextMenu(out ContextMenuStrip trayMenuStrip, out EventHandler MenuClickEventHandler);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Members
|
||||
|
||||
private InitializeContextMenu _ContextMenuInitializer = null;
|
||||
private TTimerDisp SingleClickDetectTimer = null;
|
||||
private TimeSpan _LastFiredEvent = new TimeSpan(DateTime.Now.Ticks);
|
||||
private const int _MILISECONDS_FOR_SINGLEMOUSE_CLICKEVENT_TOCOUNT = 350;
|
||||
private const int _N_SECONDS_TOIGNORE_NEXT_SIGNLEMOUSE_CLICKEVENT = 2; // to avoid tripple clicks, etc... (only sends one double click)
|
||||
private bool _disposed = false;
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if enough time since _LastFiredEvent has passed
|
||||
/// </summary>
|
||||
private bool EnoughTimeSinceLastEventHasElapsed
|
||||
{
|
||||
get
|
||||
{
|
||||
return (DateTime.Now.Ticks - _LastFiredEvent.Ticks) >= (TimeSpan.FromSeconds(_N_SECONDS_TOIGNORE_NEXT_SIGNLEMOUSE_CLICKEVENT).Ticks);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Construction
|
||||
|
||||
/// <summary>
|
||||
/// Construct a System Tray Icon (use public properties like ContextMenu,Icon,toolTip to customize further)
|
||||
/// </summary>
|
||||
/// <param name="ContextMenuInitializer">Callback to initialize the Context Menu (can't be null)</param>
|
||||
/// <param name="toolTip">pass in an initial ToolTip to display, defaults to ""</param>
|
||||
/// <param name="icon">if null, defaults to systemIcons.Application</param>
|
||||
public SysTray(InitializeContextMenu ContextMenuInitializer, string toolTip = "", Icon icon = null)
|
||||
{
|
||||
if (ContextMenuInitializer == null)
|
||||
throw new ArgumentNullException("ContextMenuInitializer can not be Null");
|
||||
|
||||
// Imp, in order to initialize the Context Menu Everytime it is needed
|
||||
_ContextMenuInitializer = ContextMenuInitializer;
|
||||
|
||||
// Create internal objects
|
||||
this.trayNotify = new NotifyIcon();
|
||||
this.SingleClickDetectTimer = new TTimerDisp(new ElapsedEventHandler(RealSingleClickDetectTimer_ElapsedEventHandler), _MILISECONDS_FOR_SINGLEMOUSE_CLICKEVENT_TOCOUNT);
|
||||
|
||||
// Add Single / Double-Click Event Handlers
|
||||
trayNotify.Click += new EventHandler(trayNotify_Click);
|
||||
trayNotify.DoubleClick += new EventHandler(trayNotify_DoubleClick);
|
||||
trayNotify.MouseDown += new MouseEventHandler(trayNotify_MouseDown);
|
||||
|
||||
// Set ToolTip
|
||||
if (!String.IsNullOrEmpty(toolTip))
|
||||
this.toolTip = toolTip;
|
||||
|
||||
// Set Icon
|
||||
if (icon == null)
|
||||
this.Icon = new Icon(SystemIcons.Application, 40, 40);
|
||||
else
|
||||
this.Icon = icon;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finalizer
|
||||
/// </summary>
|
||||
~SysTray()
|
||||
{
|
||||
Dispose(true);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Click Event Handlers
|
||||
|
||||
/// <summary>
|
||||
/// Called by NotifyIcon DoubleClick Event, We filter for only the left mouse double-click,
|
||||
/// event and fire event when neccessary
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private void trayNotify_DoubleClick(object sender, EventArgs e)
|
||||
{
|
||||
MouseEventArgs args = (MouseEventArgs)e;
|
||||
if (args.Button == MouseButtons.Left)
|
||||
{
|
||||
SingleClickDetectTimer.Stop();
|
||||
if (LeftMouseDoubleClick != null && EnoughTimeSinceLastEventHasElapsed)
|
||||
{
|
||||
_LastFiredEvent = new TimeSpan(DateTime.Now.Ticks);
|
||||
LeftMouseDoubleClick(new MouseEventArgs(MouseButtons.Left, 2, 0, 0, 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
// Called by NotifyIcon Click Event, We filter for only the left mouse click,
|
||||
/// event and fire event when neccessary
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private void trayNotify_Click(object sender, EventArgs e)
|
||||
{
|
||||
MouseEventArgs args = (MouseEventArgs) e;
|
||||
if (args.Button == MouseButtons.Left && EnoughTimeSinceLastEventHasElapsed)
|
||||
SingleClickDetectTimer.Start(); // Start Single Click Detect Timer
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// In order to accurately re-do a context menu, we handle MouseDown for the
|
||||
/// Right-Mouse click. Mouse Down comes in before the click event, which gives
|
||||
/// the caller an opportunity to handle/recreate the context menu dynamically, if needed
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
void trayNotify_MouseDown(object sender, MouseEventArgs e)
|
||||
{
|
||||
MouseEventArgs args = (MouseEventArgs)e;
|
||||
if (args.Button == MouseButtons.Right)
|
||||
{
|
||||
// Dynamically re-create Menu on every Right-Click
|
||||
InitializeContextMenuFromScratchUsingInitializer();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used to detect ONLY Single Clicks, since a single-click and then a double-click fires,
|
||||
/// we want to ignore the first click,and first see if a double-click comes in, if so, ignore
|
||||
/// the single click, otherwise send it. (this is done by trayNotify_Click & transNotify_DoubleClick)
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private void RealSingleClickDetectTimer_ElapsedEventHandler(object sender, ElapsedEventArgs e)
|
||||
{
|
||||
SingleClickDetectTimer.Stop();
|
||||
if (LeftMouseClick != null)
|
||||
{
|
||||
_LastFiredEvent = new TimeSpan(DateTime.Now.Ticks);
|
||||
LeftMouseClick(new MouseEventArgs(MouseButtons.Left, 1, 0, 0, 0));
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Helpers
|
||||
|
||||
/// <summary>
|
||||
/// (Re)Initialize Context Menu from scratch, allows us to build different menu's
|
||||
/// depending on the application state * External Caller responsible for passing in
|
||||
/// the context Menu and Click Event Handler *
|
||||
/// </summary>
|
||||
private void InitializeContextMenuFromScratchUsingInitializer()
|
||||
{
|
||||
if (_ContextMenuInitializer != null)
|
||||
{
|
||||
ContextMenuStrip menuStrip = null;
|
||||
EventHandler clickHandler = null;
|
||||
|
||||
// Call Outside Caller's Initialize Context menu Function
|
||||
_ContextMenuInitializer(out menuStrip, out clickHandler);
|
||||
|
||||
// Set up the Click Event
|
||||
if (menuStrip != null && clickHandler != null)
|
||||
AssignClickEventHandlerToAllMenuItems(menuStrip, clickHandler);
|
||||
|
||||
// Assign New Context Menu
|
||||
trayMenuStrip = menuStrip;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calls AssignClickEventHandlerToMenuItem() for each Root Menu Item
|
||||
/// </summary>
|
||||
/// <param name="menuStrip"></param>
|
||||
/// <param name="clickHandler"></param>
|
||||
private void AssignClickEventHandlerToAllMenuItems(ContextMenuStrip menuStrip, EventHandler clickHandler)
|
||||
{
|
||||
if (menuStrip != null)
|
||||
{
|
||||
foreach (ToolStripMenuItem item in menuStrip.Items.OfType<ToolStripMenuItem>())
|
||||
AssignClickEventHandlerToMenuItem(item, clickHandler);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Recursive Function, in order to assign all MenuItems as well as
|
||||
/// all subsequent MenuItems, in the DropDownList, the ToolStripMI_Click
|
||||
/// event Handler
|
||||
/// </summary>
|
||||
/// <param name="item">pass in a ToolStripMenuItem to asign the click handler to</param>
|
||||
/// <param name="clickHandler"></param>
|
||||
private void AssignClickEventHandlerToMenuItem(ToolStripMenuItem item, EventHandler clickHandler)
|
||||
{
|
||||
if (item.DropDownItems == null || item.DropDownItems.Count == 0)
|
||||
{
|
||||
if(clickHandler != null)
|
||||
item.Click += clickHandler;
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (ToolStripMenuItem _item in item.DropDownItems.OfType<ToolStripMenuItem>())
|
||||
AssignClickEventHandlerToMenuItem(_item, clickHandler);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Show N' Hide
|
||||
|
||||
/// <summary>
|
||||
/// Show the System Tray Icon
|
||||
/// </summary>
|
||||
public void Show()
|
||||
{
|
||||
// Create Context Menu
|
||||
InitializeContextMenuFromScratchUsingInitializer();
|
||||
trayNotify.Visible = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Hide the System Tray Icon
|
||||
/// </summary>
|
||||
public void Hide()
|
||||
{
|
||||
trayNotify.Visible = false;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public ShowBallon
|
||||
|
||||
/// <summary>
|
||||
/// Type of Icon to show over Ballon
|
||||
/// </summary>
|
||||
public enum BallonIcon
|
||||
{
|
||||
None,
|
||||
Error,
|
||||
Warning,
|
||||
Info
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Pops up a Ballon over the System Tray Icon
|
||||
/// </summary>
|
||||
/// <param name="BallonTipTitle">Title to show on the Ballon Tip</param>
|
||||
/// <param name="BallonTipText">Text to show on the Ballon Tip</param>
|
||||
/// <param name="tipIcon">Icon to show on the Ballon tip</param>
|
||||
/// <param name="nTimeoutInSeconds">Specify the Timeout in Seconds (System mininimum is 10 seconds)</param>
|
||||
public void ShowBallon(string BallonTipTitle, string BallonTipText, BallonIcon tipIcon = BallonIcon.None, int nTimeoutInSeconds = 10)
|
||||
{
|
||||
ToolTipIcon _tipIcon = ToolTipIcon.None;
|
||||
switch (tipIcon)
|
||||
{
|
||||
case BallonIcon.Error:
|
||||
_tipIcon = ToolTipIcon.Error;
|
||||
break;
|
||||
case BallonIcon.Info:
|
||||
_tipIcon = ToolTipIcon.Info;
|
||||
break;
|
||||
case BallonIcon.Warning:
|
||||
_tipIcon = ToolTipIcon.Warning;
|
||||
break;
|
||||
}
|
||||
trayNotify.ShowBalloonTip((int)TimeSpan.FromSeconds(nTimeoutInSeconds).TotalMilliseconds, BallonTipTitle, BallonTipText, _tipIcon);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IDisposable Members
|
||||
|
||||
/// <summary>
|
||||
/// Dispose the Registry Handle
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
|
||||
// Use SupressFinalize in case a subclass
|
||||
// of this type implements a finalizer
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dispose the Registry Handle
|
||||
/// </summary>
|
||||
/// <param name="disposing">true, if called from within</param>
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (!_disposed)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
if (trayNotify != null)
|
||||
trayNotify.Dispose();
|
||||
}
|
||||
|
||||
// Indicate that the instance has been disposed.
|
||||
trayNotify = null;
|
||||
_disposed = true;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user