////////////////////////////////////////////////
//// ApplicationDesktopToolbar ////
//// Version 1.0.1 13.06.04 ////
////////////////////////////////////////////////
// This class is based on http://www.codeproject.com/csharp/csdoesshell3.asp
// The class was extended by Daniel Grunwald (www.danielgrunwald.de)
// - fixed some bugs
// - added multi-monitor support
// - added Drag'n'Drop support
// Known bugs: (send fixes to daniel@danielgrunwald.de!)
// - The window isn't resizing back after it was undocked; I have no idea why...
// - It flickers when dragging the window at the border of the screen
// (so that it should stay docked)
using System;
using System.Runtime.InteropServices;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;
namespace ShellLib
{
public class ApplicationDesktopToolbar : Form
{
#region Enums
public enum AppBarMessages
{
///
/// Registers a new appbar and specifies the message identifier
/// that the system should use to send notification messages to
/// the appbar.
///
New = 0x00000000,
///
/// Unregisters an appbar, removing the bar from the system's
/// internal list.
///
Remove = 0x00000001,
///
/// Requests a size and screen position for an appbar.
///
QueryPos = 0x00000002,
///
/// Sets the size and screen position of an appbar.
///
SetPos = 0x00000003,
///
/// Retrieves the autohide and always-on-top states of the
/// Microsoft® Windows® taskbar.
///
GetState = 0x00000004,
///
/// Retrieves the bounding rectangle of the Windows taskbar.
///
GetTaskBarPos = 0x00000005,
///
/// Notifies the system that an appbar has been activated.
///
Activate = 0x00000006,
///
/// Retrieves the handle to the autohide appbar associated with
/// a particular edge of the screen.
///
GetAutoHideBar = 0x00000007,
///
/// Registers or unregisters an autohide appbar for an edge of
/// the screen.
///
SetAutoHideBar = 0x00000008,
///
/// Notifies the system when an appbar's position has changed.
///
WindowPosChanged = 0x00000009,
///
/// Sets the state of the appbar's autohide and always-on-top
/// attributes.
///
SetState = 0x0000000a
}
public enum AppBarNotifications
{
///
/// Notifies an appbar that the taskbar's autohide or
/// always-on-top state has changed—that is, the user has selected
/// or cleared the "Always on top" or "Auto hide" check box on the
/// taskbar's property sheet.
///
StateChange = 0x00000000,
///
/// Notifies an appbar when an event has occurred that may affect
/// the appbar's size and position. Events include changes in the
/// taskbar's size, position, and visibility state, as well as the
/// addition, removal, or resizing of another appbar on the same
/// side of the screen.
///
PosChanged = 0x00000001,
///
/// Notifies an appbar when a full-screen application is opening or
/// closing. This notification is sent in the form of an
/// application-defined message that is set by the ABM_NEW message.
///
FullScreenApp = 0x00000002,
///
/// Notifies an appbar that the user has selected the Cascade,
/// Tile Horizontally, or Tile Vertically command from the
/// taskbar's shortcut menu.
///
WindowArrange = 0x00000003
}
[Flags]
public enum AppBarStates
{
AutoHide = 0x00000001,
AlwaysOnTop = 0x00000002
}
public enum AppBarEdges
{
Left = 0,
Top = 1,
Right = 2,
Bottom = 3,
Float = 4
}
// Window Messages
public enum WM
{
ACTIVATE = 0x0006,
WINDOWPOSCHANGED = 0x0047,
NCHITTEST = 0x0084
}
public enum MousePositionCodes
{
HTERROR = (-2),
HTTRANSPARENT = (-1),
HTNOWHERE = 0,
HTCLIENT = 1,
HTCAPTION = 2,
HTSYSMENU = 3,
HTGROWBOX = 4,
HTSIZE = HTGROWBOX,
HTMENU = 5,
HTHSCROLL = 6,
HTVSCROLL = 7,
HTMINBUTTON = 8,
HTMAXBUTTON = 9,
HTLEFT = 10,
HTRIGHT = 11,
HTTOP = 12,
HTTOPLEFT = 13,
HTTOPRIGHT = 14,
HTBOTTOM = 15,
HTBOTTOMLEFT = 16,
HTBOTTOMRIGHT = 17,
HTBORDER = 18,
HTREDUCE = HTMINBUTTON,
HTZOOM = HTMAXBUTTON,
HTSIZEFIRST = HTLEFT,
HTSIZELAST = HTBOTTOMRIGHT,
HTOBJECT = 19,
HTCLOSE = 20,
HTHELP = 21
}
#endregion Enums
#region AppBar Functions
private Boolean AppbarNew()
{
if (CallbackMessageID == 0)
throw new Exception("CallbackMessageID is 0");
if (IsAppbarMode)
return true;
m_PrevSize = Size;
m_PrevLocation = Location;
Console.WriteLine("Saving height {0}", m_PrevSize.Height);
// prepare data structure of message
ShellApi.APPBARDATA msgData = new ShellApi.APPBARDATA();
msgData.cbSize = (UInt32)Marshal.SizeOf(msgData);
msgData.hWnd = Handle;
msgData.uCallbackMessage = CallbackMessageID;
// install new appbar
UInt32 retVal = ShellApi.SHAppBarMessage((UInt32)AppBarMessages.New,ref msgData);
IsAppbarMode = (retVal != 0);
SizeAppBar();
return IsAppbarMode;
}
private Boolean AppbarRemove()
{
if (!IsAppbarMode) return true;
// prepare data structure of message
ShellApi.APPBARDATA msgData = new ShellApi.APPBARDATA();
msgData.cbSize = (UInt32)Marshal.SizeOf(msgData);
msgData.hWnd = Handle;
// remove appbar
UInt32 retVal = ShellApi.SHAppBarMessage((UInt32)AppBarMessages.Remove,ref msgData);
IsAppbarMode = false;
DoResize(new Rectangle(m_PrevLocation, m_PrevSize));
Console.WriteLine("Restoring height {0}", m_PrevSize.Height);
return (retVal!=0) ? true : false;
}
private void AppbarQueryPos(ref ShellApi.RECT appRect)
{
// prepare data structure of message
ShellApi.APPBARDATA msgData = new ShellApi.APPBARDATA();
msgData.cbSize = (UInt32)Marshal.SizeOf(msgData);
msgData.hWnd = Handle;
msgData.uEdge = (UInt32)m_Edge;
msgData.rc = appRect;
// query postion for the appbar
ShellApi.SHAppBarMessage((UInt32)AppBarMessages.QueryPos, ref msgData);
appRect = msgData.rc;
}
private void AppbarSetPos(ref ShellApi.RECT appRect)
{
// prepare data structure of message
ShellApi.APPBARDATA msgData = new ShellApi.APPBARDATA();
msgData.cbSize = (UInt32)Marshal.SizeOf(msgData);
msgData.hWnd = Handle;
msgData.uEdge = (UInt32)m_Edge;
msgData.rc = appRect;
// set postion for the appbar
ShellApi.SHAppBarMessage((UInt32)AppBarMessages.SetPos, ref msgData);
appRect = msgData.rc;
}
private void AppbarActivate()
{
// prepare data structure of message
ShellApi.APPBARDATA msgData = new ShellApi.APPBARDATA();
msgData.cbSize = (UInt32)Marshal.SizeOf(msgData);
msgData.hWnd = Handle;
// send activate to the system
ShellApi.SHAppBarMessage((UInt32)AppBarMessages.Activate, ref msgData);
}
private void AppbarWindowPosChanged()
{
// prepare data structure of message
ShellApi.APPBARDATA msgData = new ShellApi.APPBARDATA();
msgData.cbSize = (UInt32)Marshal.SizeOf(msgData);
msgData.hWnd = Handle;
// send windowposchanged to the system
ShellApi.SHAppBarMessage((UInt32)AppBarMessages.WindowPosChanged, ref msgData);
}
private Boolean AppbarSetAutoHideBar(Boolean hideValue)
{
// prepare data structure of message
ShellApi.APPBARDATA msgData = new ShellApi.APPBARDATA();
msgData.cbSize = (UInt32)Marshal.SizeOf(msgData);
msgData.hWnd = Handle;
msgData.uEdge = (UInt32)m_Edge;
msgData.lParam = (hideValue) ? 1 : 0;
// set auto hide
UInt32 retVal = ShellApi.SHAppBarMessage((UInt32)AppBarMessages.SetAutoHideBar,ref msgData);
return (retVal!=0) ? true : false;
}
private IntPtr AppbarGetAutoHideBar(AppBarEdges edge)
{
// prepare data structure of message
ShellApi.APPBARDATA msgData = new ShellApi.APPBARDATA();
msgData.cbSize = (UInt32)Marshal.SizeOf(msgData);
msgData.uEdge = (UInt32)edge;
// get auto hide
IntPtr retVal = (IntPtr)ShellApi.SHAppBarMessage((UInt32)AppBarMessages.GetAutoHideBar,ref msgData);
return retVal;
}
private AppBarStates AppbarGetTaskbarState()
{
// prepare data structure of message
ShellApi.APPBARDATA msgData = new ShellApi.APPBARDATA();
msgData.cbSize = (UInt32)Marshal.SizeOf(msgData);
// get taskbar state
UInt32 retVal = ShellApi.SHAppBarMessage((UInt32)AppBarMessages.GetState, ref msgData);
return (AppBarStates)retVal;
}
private void AppbarSetTaskbarState(AppBarStates state)
{
// prepare data structure of message
ShellApi.APPBARDATA msgData = new ShellApi.APPBARDATA();
msgData.cbSize = (UInt32)Marshal.SizeOf(msgData);
msgData.lParam = (Int32)state;
// set taskbar state
ShellApi.SHAppBarMessage((UInt32)AppBarMessages.SetState, ref msgData);
}
private void AppbarGetTaskbarPos(out ShellApi.RECT taskRect)
{
// prepare data structure of message
ShellApi.APPBARDATA msgData = new ShellApi.APPBARDATA();
msgData.cbSize = (UInt32)Marshal.SizeOf(msgData);
// get taskbar position
ShellApi.SHAppBarMessage((UInt32)AppBarMessages.GetTaskBarPos, ref msgData);
taskRect = msgData.rc;
}
#endregion AppBar Functions
#region Private Variables
// saves the current edge
private AppBarEdges m_Edge = AppBarEdges.Float;
// saves the callback message id
private UInt32 CallbackMessageID = 0;
// are we in dock mode?
private Boolean IsAppbarMode = false;
// save the floating size and location
private Size m_PrevSize;
private Point m_PrevLocation;
#endregion Private Variables
public ApplicationDesktopToolbar()
{
FormBorderStyle = FormBorderStyle.SizableToolWindow;
// Register a unique message as our callback message
CallbackMessageID = RegisterCallbackMessage();
if (CallbackMessageID == 0)
throw new Exception("RegisterCallbackMessage failed");
}
private UInt32 RegisterCallbackMessage()
{
String uniqueMessageString = Guid.NewGuid().ToString();
return ShellApi.RegisterWindowMessage(uniqueMessageString);
}
protected override void OnMove(EventArgs e)
{
if (!internalResizing)
{
Point mousePos = Control.MousePosition;
Screen screen = Screen.FromPoint(mousePos);
targetScreen = screen;
if (mousePos.X >= screen.Bounds.X && mousePos.X < screen.Bounds.X + 15)
Edge = AppBarEdges.Left;
else if (mousePos.Y >= screen.Bounds.Y && mousePos.Y < screen.Bounds.Y + 15)
Edge = AppBarEdges.Top;
else if (mousePos.X <= screen.Bounds.Right && mousePos.X > screen.Bounds.Right - 15)
Edge = AppBarEdges.Right;
else if (mousePos.Y <= screen.Bounds.Bottom && mousePos.Y > screen.Bounds.Bottom - 15)
Edge = AppBarEdges.Bottom;
else
Edge = AppBarEdges.Float;
}
if (IsAppbarMode && !internalResizing)
{
internalResizing = true;
Location = viewLocation;
internalResizing = false;
}
else
{
base.OnMove (e);
}
}
private void SizeAppBar()
{
if (!IsAppbarMode) return;
ShellApi.RECT rt = new ShellApi.RECT();
if ((m_Edge == AppBarEdges.Left) ||
(m_Edge == AppBarEdges.Right))
{
rt.top = targetScreen.Bounds.Top;
rt.bottom = targetScreen.Bounds.Bottom;
if (m_Edge == AppBarEdges.Left)
{
rt.left = targetScreen.Bounds.Left;
rt.right = m_PrevSize.Width + rt.left;
}
else
{
rt.right = targetScreen.Bounds.Right;
rt.left = rt.right - m_PrevSize.Width;
}
}
else
{
rt.left = targetScreen.Bounds.Left;
rt.right = targetScreen.Bounds.Right;
if (m_Edge == AppBarEdges.Top)
{
rt.top = targetScreen.Bounds.Top;
rt.bottom = m_PrevSize.Height + targetScreen.Bounds.Top;
}
else
{
rt.bottom = targetScreen.Bounds.Bottom;
rt.top = rt.bottom - m_PrevSize.Height;
}
}
AppbarQueryPos(ref rt);
switch (m_Edge)
{
case AppBarEdges.Left:
rt.right = rt.left + m_PrevSize.Width;
break;
case AppBarEdges.Right:
rt.left= rt.right - m_PrevSize.Width;
break;
case AppBarEdges.Top:
rt.bottom = rt.top + m_PrevSize.Height;
break;
case AppBarEdges.Bottom:
rt.top = rt.bottom - m_PrevSize.Height;
break;
}
AppbarSetPos(ref rt);
if (!IsAppbarMode) return;
viewLocation = new Point(rt.left,rt.top);
DoResize(new Rectangle(viewLocation, new Size(rt.right - rt.left, rt.bottom - rt.top)));
Console.WriteLine("Setting size (AppbarMode = {0}, Edge = {1})", IsAppbarMode, m_Edge);
}
Point viewLocation;
void DoResize(Rectangle bounds)
{
if (internalResizing) throw new Exception("Double call of DoResize!");
internalResizing = true;
Bounds = bounds;
internalResizing = false;
}
bool internalResizing;
#region Message Handlers
void OnAppbarNotification(ref Message msg)
{
AppBarStates state;
AppBarNotifications msgType = (AppBarNotifications)(Int32)msg.WParam;
switch (msgType)
{
case AppBarNotifications.PosChanged:
SizeAppBar();
break;
case AppBarNotifications.StateChange:
state = AppbarGetTaskbarState();
if ((state & AppBarStates.AlwaysOnTop) !=0)
{
TopMost = true;
BringToFront();
}
else
{
TopMost = false;
SendToBack();
}
break;
case AppBarNotifications.FullScreenApp:
if ((int)msg.LParam !=0)
{
TopMost = false;
SendToBack();
}
else
{
state = AppbarGetTaskbarState();
if ((state & AppBarStates.AlwaysOnTop) !=0)
{
TopMost = true;
BringToFront();
}
else
{
TopMost = false;
SendToBack();
}
}
break;
case AppBarNotifications.WindowArrange:
if ((int)msg.LParam != 0) // before
Visible = false;
else // after
Visible = true;
break;
}
}
#endregion Message Handlers
#region Window Procedure
protected override void WndProc(ref Message msg)
{
if (IsAppbarMode)
{
if (msg.Msg == CallbackMessageID)
{
OnAppbarNotification(ref msg);
}
else if (msg.Msg == (int)WM.ACTIVATE)
{
AppbarActivate();
}
else if (msg.Msg == (int)WM.WINDOWPOSCHANGED)
{
AppbarWindowPosChanged();
}
else if (msg.Msg == (int)WM.NCHITTEST)
{
OnNcHitTest(ref msg);
return;
}
}
base.WndProc(ref msg);
}
#endregion Window Procedure
void OnNcHitTest(ref Message msg)
{
DefWndProc(ref msg);
if ((m_Edge == AppBarEdges.Top) && ((int)msg.Result == (int)MousePositionCodes.HTBOTTOM))
0.ToString();
else if ((m_Edge == AppBarEdges.Bottom) && ((int)msg.Result == (int)MousePositionCodes.HTTOP))
0.ToString();
else if ((m_Edge == AppBarEdges.Left) && ((int)msg.Result == (int)MousePositionCodes.HTRIGHT))
0.ToString();
else if ((m_Edge == AppBarEdges.Right) && ((int)msg.Result == (int)MousePositionCodes.HTLEFT))
0.ToString();
else if ((int)msg.Result == (int)MousePositionCodes.HTCLOSE)
0.ToString();
else if ((int)msg.Result == (int)MousePositionCodes.HTCAPTION)
0.ToString();
else
{
msg.Result = (IntPtr)MousePositionCodes.HTCLIENT;
return;
}
base.WndProc(ref msg);
}
protected override void OnLoad(EventArgs e)
{
m_PrevSize = Size;
m_PrevLocation = Location;
base.OnLoad(e);
}
protected override void OnClosing(CancelEventArgs e)
{
AppbarRemove();
base.OnClosing(e);
}
protected override void OnSizeChanged(EventArgs e)
{
if (IsAppbarMode && !internalResizing)
{
if (m_Edge == AppBarEdges.Top || m_Edge == AppBarEdges.Bottom)
m_PrevSize.Height = Size.Height;
else
m_PrevSize.Width = Size.Width;
SizeAppBar();
}
base.OnSizeChanged(e);
}
Screen targetScreen = Screen.PrimaryScreen;
public AppBarEdges Edge
{
get
{
return m_Edge;
}
set
{
if (m_Edge == value) return;
if (internalResizing) throw new Exception("Edge may not be changed while the Toolbar is resizing");
m_Edge = value;
Console.WriteLine("Edge = {0}", value);
if (value == AppBarEdges.Float)
AppbarRemove();
else
AppbarNew();
if (IsAppbarMode)
SizeAppBar();
}
}
}
}