Ok,
I ran across this AppBar class that works extremely well. Unfortunately, I am having a difficult time trying to figure out how to make things happen the way I'd like.
My goal is to place a toolbar control on the AppBar form and have the orientation of the toolbar changed based on the horizontal / vertical screen alignment of the AppBar. It doesn't seem like this should be that difficult but I have been unable to figure it out as of yet. I have spent many hours on MSDN reading through the APPBAR docs and still have had no luck.
How can I note which edge of the screen the appbar successfully docs to and then perform some action? The action I perform I can take care. My issue up to this point has been with following exactly where the code goes and determining how to trigger based on this event. (event = which edge did the appbar just doc to?)
The code I found is listed below.. (long)
------------------------------------------------------------------------------------------------------------
// Contributed by Dean Cleaver - [
[email protected]]
// a base class for implementing a dockable Windows App-Bar
// Much of the code ported from a C/C++ implementation by Jeffrey Richter
#region Copyright © 2002-2004 The Genghis Group
/*
* This software is provided 'as-is', without any express or implied warranty.
* In no event will the authors be held liable for any damages arising from the
* use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not claim
* that you wrote the original software. If you use this software in a product,
* an acknowledgment in the product documentation is IsRequired, as shown here:
*
* Portions Copyright © 2002-2004 The Genghis Group (http://www.genghisgroup.com/).
*
* 2. No substantial portion of the source code of this library may be redistributed
* without the express written permission of the copyright holders, where
* "substantial" is defined as enough code to be recognizably from this library.
*/
#endregion
#region History
// 4/15/2003 - Initial Version
#endregion
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Design;
using System.Globalization;
using System.Runtime.InteropServices;
using System.Security.Permissions;
using System.Windows.Forms;
using System.Windows.Forms.Design;
namespace Genghis.Windows.Forms
{
///
/// Summary description for AppBar.
///
public class AppBarForm : System.Windows.Forms.Form
{
// The header from the original cpp file this was generated from.
// Most of the credit belongs to Jeffrey Richter
/******************************************************************************
Module name: AppBar.cpp
Written by: Jeffrey Richter
Purpose: AppBar base class implementation file.
******************************************************************************/
#region Private Structures
[StructLayout(LayoutKind.Sequential)]
private struct POINT
{
public int X;
public int Y;
public POINT(int X, int Y)
{
this.X = X;
this.Y = Y;
}
public POINT(int pos)
{
this.X = pos & 0xFFFF;
this.Y = (pos >> 16) & 0xFFFF;
}
}
[StructLayout(LayoutKind.Sequential)]
private struct RECT
{
public int left;
public int top;
public int right;
public int bottom;
public RECT(int left,
int top,
int right,
int bottom)
{
this.left = left;
this.right = right;
this.top = top;
this.bottom = bottom;
}
public int Width
{
get {return right - left;}
}
public int Height
{
get {return bottom - top;}
}
public static bool operator != (RECT o1, RECT o2)
{
if (o1.left != o2.left)
return true;
if (o1.right != o2.right)
return true;
if (o1.top != o2.top)
return true;
if (o1.bottom != o2.bottom)
return true;
return false;
}
public static bool operator == (RECT o1, RECT o2)
{
if (o1.left != o2.left)
return false;
if (o1.right != o2.right)
return false;
if (o1.top != o2.top)
return false;
if (o1.bottom != o2.bottom)
return false;
return false;
}
public override bool Equals(object o)
{
try
{
RECT o2 = (RECT) o;
return this == o2;
}
catch
{
return false;
}
}
public override int GetHashCode()
{
return base.GetHashCode();
}
}
[StructLayout(LayoutKind.Sequential)]
private struct APPBARSTATE
{
public int size; // Size of this structure
public int state; // ABE_UNKNOWN, ABE_FLOAT, or ABE_edge
public bool autoHide; // Should AppBar be auto-hidden when docked?
public bool alwaysOnTop; // Should AppBar always be on top?
public int dockSizeHorizontal;
public int dockSizeVertical;
public RECT floatRect; // Floating rectangle (in screen coordinates)
public int taskBarState;
public int dockFlags; // See the ABF_* flags above
public SIZE sizeInc; // Descrete width/height size increments
public int proposedState;
public bool fullScreenAppOpen;
public bool autoHideIsVisible;
}
[StructLayout(LayoutKind.Sequential)]
private struct APPBARDATA
{
public int size;
public int hWnd;
public int callbackMessage;
public int edge;
public RECT position;
public int lParam; // message specific
}
[StructLayout(LayoutKind.Sequential)]
private struct SIZE
{
public int cx;
public int cy;
public SIZE(int cx, int cy)
{
this.cx = cx;
this.cy = cy;
}
}
#endregion
#region Private Constants
private const int ABE_LEFT = 0;
private const int ABE_TOP = 1;
private const int ABE_RIGHT = 2;
private const int ABE_BOTTOM = 3;
private const int ABE_UNKNOWN = 4;
private const int ABE_FLOAT = 5;
private const int ABF_ALLOWLEFT = 0x00000001;
private const int ABF_ALLOWRIGHT = 0x00000004;
private const int ABF_ALLOWTOP = 0x00000002;
private const int ABF_ALLOWBOTTOM = 0x00000008;
private const int ABF_ALLOWFLOAT = 0x00000010;
private const int ABF_ALLOWLEFTRIGHT = (ABF_ALLOWLEFT | ABF_ALLOWRIGHT);
private const int ABF_ALLOWTOPBOTTOM = (ABF_ALLOWTOP | ABF_ALLOWBOTTOM);
private const int ABF_ALLOWANYEDGE = (ABF_ALLOWLEFTRIGHT | ABF_ALLOWTOPBOTTOM);
private const int ABF_ALLOWANYWHERE = (ABF_ALLOWANYEDGE | ABF_ALLOWFLOAT);
private const int ABF_MIMICTASKBARAUTOHIDE = 0x00000020;
private const int ABF_MIMICTASKBARALWAYSONTOP = 0x00000040;
private const int ABM_NEW = 0x00000000;
private const int ABM_REMOVE = 0x00000001;
private const int ABM_QUERYPOS = 0x00000002;
private const int ABM_SETPOS = 0x00000003;
private const int ABM_GETSTATE = 0x00000004;
private const int ABM_GETTASKBARPOS = 0x00000005;
private const int ABM_ACTIVATE = 0x00000006;
private const int ABM_GETAUTOHIDEBAR = 0x00000007;
private const int ABM_SETAUTOHIDEBAR = 0x00000008;
private const int ABM_WINDOWPOSCHANGED = 0x00000009;
private const int ABN_FULLSCREENAPP = 0x2;
private const int ABN_POSCHANGED = 0x1;
private const int ABN_STATECHANGE = 0x0;
private const int ABN_WINDOWARRANGE = 0x3; // lParam == TRUE means hide
private const int ABS_AUTOHIDE = 0x0000001;
private const int ABS_ALWAYSONTOP = 0x0000002;
private const int AUTOHIDETIMERINTERVAL = 400;
private const int AUTOHIDETIMERID = 1;
private const short GWL_STYLE = (-16);
private const short GWL_EXSTYLE = (-20);
private const int HTBORDER = 18;
private const int HTBOTTOM = 15;
private const int HTBOTTOMLEFT = 16;
private const int HTBOTTOMRIGHT = 17;
private const int HTCAPTION = 2;
private const int HTCLIENT = 1;
private const int HTERROR = (-2);
private const int HTGROWBOX = 4;
private const int HTHSCROLL = 6;
private const int HTLEFT = 10;
private const int HTMAXBUTTON = 9;
private const int HTMENU = 5;
private const int HTMINBUTTON = 8;
private const int HTNOWHERE = 0;
private const int HTREDUCE = HTMINBUTTON;
private const int HTRIGHT = 11;
private const int HTSIZE = HTGROWBOX;
private const int HTSIZEFIRST = HTLEFT;
private const int HTSIZELAST = HTBOTTOMRIGHT;
private const int HTSYSMENU = 3;
private const int HTTOP = 12;
private const int HTTOPLEFT = 13;
private const int HTTOPRIGHT = 14;
private const int HTTRANSPARENT = (-1);
private const int HTVSCROLL = 7;
private const int HTZOOM = HTMAXBUTTON;
private const int HWND_TOP = 0;
private const int HWND_BOTTOM = 1;
private const int HWND_TOPMOST = -1;
private const int HWND_NOTOPMOST = -2;
private const int SM_SWAPBUTTON = 23;
private const int SM_CXDOUBLECLK = 36;
private const int SM_CYDOUBLECLK = 37;
private const int SM_CXVSCROLL = 2;
private const int SM_CXHSCROLL = 21;
private const int SM_CYHSCROLL = 3;
private const int SM_CXSCREEN = 0;
private const int SM_CYSCREEN = 1;
private const int SM_CXBORDER = 5;
private const int SM_CYBORDER = 6;
private const int SPI_GETDRAGFULLWINDOWS = 38;
private const int SPI_GETWORKAREA = 48;
private const int SWP_NOSIZE = 0x0001;
private const int SWP_NOMOVE = 0x0002;
private const int SWP_NOZORDER = 0x0004;
private const int SWP_NOREDRAW = 0x0008;
private const int SWP_NOACTIVATE = 0x0010;
private const int SWP_FRAMECHANGED =0x0020;
private const int SWP_DRAWFRAME = SWP_FRAMECHANGED;
private const int VK_CONTROL = 0x11;
private const int VK_RBUTTON = 0x2;
private const int VK_LBUTTON = 0x1;
private const int WA_INACTIVE = 0;
private const int WMSZ_LEFT = 1;
private const int WMSZ_RIGHT = 2;
private const int WMSZ_TOP = 3;
private const int WMSZ_TOPLEFT = 4;
private const int WMSZ_TOPRIGHT = 5;
private const int WMSZ_BOTTOM = 6;
private const int WMSZ_BOTTOMLEFT = 7;
private const int WMSZ_BOTTOMRIGHT = 8;
private const int WM_NULL = 0x0000;
private const int WM_CREATE = 0x0001;
private const int WM_DESTROY = 0x0002;
private const int WM_MOVE = 0x0003;
private const int WM_SIZE = 0x0005;
private const int WM_ACTIVATE = 0x0006;
private const int WM_SETFOCUS = 0x0007;
private const int WM_KILLFOCUS = 0x0008;
private const int WM_ENABLE = 0x000A;
private const int WM_SETREDRAW = 0x000B;
private const int WM_SETTEXT = 0x000C;
private const int WM_GETTEXT = 0x000D;
private const int WM_GETTEXTLENGTH = 0x000E;
private const int WM_PAINT = 0x000F;
private const int WM_CLOSE = 0x0010;
private const int WM_QUERYENDSESSION = 0x0011;
private const int WM_QUIT = 0x0012;
private const int WM_QUERYOPEN = 0x0013;
private const int WM_ERASEBKGND = 0x0014;
private const int WM_SYSCOLORCHANGE = 0x0015;
private const int WM_ENDSESSION = 0x0016;
private const int WM_SHOWWINDOW = 0x0018;
private const int WM_CTLCOLOR = 0x0019;
private const int WM_WININICHANGE = 0x001A;
private const int WM_SETTINGCHANGE = 0x001A;
private const int WM_DEVMODECHANGE = 0x001B;
private const int WM_ACTIVATEAPP = 0x001C;
private const int WM_FONTCHANGE = 0x001D;
private const int WM_TIMECHANGE = 0x001E;
private const int WM_CANCELMODE = 0x001F;
private const int WM_SETCURSOR = 0x0020;
private const int WM_MOUSEACTIVATE = 0x0021;
private const int WM_CHILDACTIVATE = 0x0022;
private const int WM_QUEUESYNC = 0x0023;
private const int WM_GETMINMAXINFO = 0x0024;
private const int WM_PAINTICON = 0x0026;
private const int WM_ICONERASEBKGND = 0x0027;
private const int WM_NEXTDLGCTL = 0x0028;
private const int WM_SPOOLERSTATUS = 0x002A;
private const int WM_DRAWITEM = 0x002B;
private const int WM_MEASUREITEM = 0x002C;
private const int WM_DELETEITEM = 0x002D;
private const int WM_VKEYTOITEM = 0x002E;
private const int WM_CHARTOITEM = 0x002F;
private const int WM_SETFONT = 0x0030;
private const int WM_GETFONT = 0x0031;
private const int WM_SETHOTKEY = 0x0032;
private const int WM_GETHOTKEY = 0x0033;
private const int WM_QUERYDRAGICON = 0x0037;
private const int WM_COMPAREITEM = 0x0039;
private const int WM_GETOBJECT = 0x003D;
private const int WM_COMPACTING = 0x0041;
private const int WM_COMMNOTIFY = 0x0044 ;
private const int WM_WINDOWPOSCHANGING = 0x0046;
private const int WM_WINDOWPOSCHANGED = 0x0047;
private const int WM_POWER = 0x0048;
private const int WM_COPYDATA = 0x004A;
private const int WM_CANCELJOURNAL = 0x004B;
private const int WM_NOTIFY = 0x004E;
private const int WM_INPUTLANGCHANGEREQUEST = 0x0050;
private const int WM_INPUTLANGCHANGE = 0x0051;
private const int WM_TCARD = 0x0052;
private const int WM_HELP = 0x0053;
private const int WM_USERCHANGED = 0x0054;
private const int WM_NOTIFYFORMAT = 0x0055;
private const int WM_CONTEXTMENU = 0x007B;
private const int WM_STYLECHANGING = 0x007C;
private const int WM_STYLECHANGED = 0x007D;
private const int WM_DISPLAYCHANGE = 0x007E;
private const int WM_GETICON = 0x007F;
private const int WM_SETICON = 0x0080;
private const int WM_NCCREATE = 0x0081;
private const int WM_NCDESTROY = 0x0082;
private const int WM_NCCALCSIZE = 0x0083;
private const int WM_NCHITTEST = 0x0084;
private const int WM_NCPAINT = 0x0085;
private const int WM_NCACTIVATE = 0x0086;
private const int WM_GETDLGCODE = 0x0087;
private const int WM_SYNCPAINT = 0x0088;
private const int WM_NCMOUSEMOVE = 0x00A0;
private const int WM_NCLBUTTONDOWN = 0x00A1;
private const int WM_NCLBUTTONUP = 0x00A2;
private const int WM_NCLBUTTONDBLCLK = 0x00A3;
private const int WM_NCRBUTTONDOWN = 0x00A4;
private const int WM_NCRBUTTONUP = 0x00A5;
private const int WM_NCRBUTTONDBLCLK = 0x00A6;
private const int WM_NCMBUTTONDOWN = 0x00A7;
private const int WM_NCMBUTTONUP = 0x00A8;
private const int WM_NCMBUTTONDBLCLK = 0x00A9;
private const int WM_KEYDOWN = 0x0100;
private const int WM_KEYUP = 0x0101;
private const int WM_CHAR = 0x0102;
private const int WM_DEADCHAR = 0x0103;
private const int WM_SYSKEYDOWN = 0x0104;
private const int WM_SYSKEYUP = 0x0105;
private const int WM_SYSCHAR = 0x0106;
private const int WM_SYSDEADCHAR = 0x0107;
private const int WM_KEYLAST = 0x0108;
private const int WM_IME_STARTCOMPOSITION = 0x010D;
private const int WM_IME_ENDCOMPOSITION = 0x010E;
private const int WM_IME_COMPOSITION = 0x010F;
private const int WM_IME_KEYLAST = 0x010F;
private const int WM_INITDIALOG = 0x0110;
private const int WM_COMMAND = 0x0111;
private const int WM_SYSCOMMAND = 0x0112;
private const int WM_TIMER = 0x0113;
private const int WM_HSCROLL = 0x0114;
private const int WM_VSCROLL = 0x0115;
private const int WM_INITMENU = 0x0116;
private const int WM_INITMENUPOPUP = 0x0117;
private const int WM_MENUSELECT = 0x011F;
private const int WM_MENUCHAR = 0x0120;
private const int WM_ENTERIDLE = 0x0121;
private const int WM_MENURBUTTONUP = 0x0122;
private const int WM_MENUDRAG = 0x0123;
private const int WM_MENUGETOBJECT = 0x0124;
private const int WM_UNINITMENUPOPUP = 0x0125;
private const int WM_MENUCOMMAND = 0x0126;
private const int WM_CTLCOLORMSGBOX = 0x0132;
private const int WM_CTLCOLOREDIT = 0x0133;
private const int WM_CTLCOLORLISTBOX = 0x0134;
private const int WM_CTLCOLORBTN = 0x0135;
private const int WM_CTLCOLORDLG = 0x0136;
private const int WM_CTLCOLORSCROLLBAR = 0x0137;
private const int WM_CTLCOLORSTATIC = 0x0138;
private const int WM_MOUSEMOVE = 0x0200;
private const int WM_LBUTTONDOWN = 0x0201;
private const int WM_LBUTTONUP = 0x0202;
private const int WM_LBUTTONDBLCLK = 0x0203;
private const int WM_RBUTTONDOWN = 0x0204;
private const int WM_RBUTTONUP = 0x0205;
private const int WM_RBUTTONDBLCLK = 0x0206;
private const int WM_MBUTTONDOWN = 0x0207;
private const int WM_MBUTTONUP = 0x0208;
private const int WM_MBUTTONDBLCLK = 0x0209;
private const int WM_MOUSEWHEEL = 0x020A;
private const int WM_PARENTNOTIFY = 0x0210;
private const int WM_ENTERMENULOOP = 0x0211;
private const int WM_EXITMENULOOP = 0x0212;
private const int WM_NEXTMENU = 0x0213;
private const int WM_SIZING = 0x0214;
private const int WM_CAPTURECHANGED = 0x0215;
private const int WM_MOVING = 0x0216;
private const int WM_DEVICECHANGE = 0x0219;
private const int WM_MDICREATE = 0x0220;
private const int WM_MDIDESTROY = 0x0221;
private const int WM_MDIACTIVATE = 0x0222;
private const int WM_MDIRESTORE = 0x0223;
private const int WM_MDINEXT = 0x0224;
private const int WM_MDIMAXIMIZE = 0x0225;
private const int WM_MDITILE = 0x0226;
private const int WM_MDICASCADE = 0x0227;
private const int WM_MDIICONARRANGE = 0x0228;
private const int WM_MDIGETACTIVE = 0x0229;
private const int WM_MDISETMENU = 0x0230;
private const int WM_ENTERSIZEMOVE = 0x0231;
private const int WM_EXITSIZEMOVE = 0x0232;
private const int WM_DROPFILES = 0x0233;
private const int WM_MDIREFRESHMENU = 0x0234;
private const int WM_IME_SETCONTEXT = 0x0281;
private const int WM_IME_NOTIFY = 0x0282;
private const int WM_IME_CONTROL = 0x0283;
private const int WM_IME_COMPOSITIONFULL = 0x0284;
private const int WM_IME_SELECT = 0x0285;
private const int WM_IME_CHAR = 0x0286;
private const int WM_IME_REQUEST = 0x0288;
private const int WM_IME_KEYDOWN = 0x0290;
private const int WM_IME_KEYUP = 0x0291;
private const int WM_MOUSEHOVER = 0x02A1;
private const int WM_MOUSELEAVE = 0x02A3;
private const int WM_CUT = 0x0300;
private const int WM_COPY = 0x0301;
private const int WM_PASTE = 0x0302;
private const int WM_CLEAR = 0x0303;
private const int WM_UNDO = 0x0304;
private const int WM_RENDERFORMAT = 0x0305;
private const int WM_RENDERALLFORMATS = 0x0306;
private const int WM_DESTROYCLIPBOARD = 0x0307;
private const int WM_DRAWCLIPBOARD = 0x0308;
private const int WM_PAINTCLIPBOARD = 0x0309;
private const int WM_VSCROLLCLIPBOARD = 0x030A;
private const int WM_SIZECLIPBOARD = 0x030B;
private const int WM_ASKCBFORMATNAME = 0x030C;
private const int WM_CHANGECBCHAIN = 0x030D;
private const int WM_HSCROLLCLIPBOARD = 0x030E;
private const int WM_QUERYNEWPALETTE = 0x030F;
private const int WM_PALETTEISCHANGING = 0x0310;
private const int WM_PALETTECHANGED = 0x0311;
private const int WM_HOTKEY = 0x0312;
private const int WM_PRINT = 0x0317;
private const int WM_PRINTCLIENT = 0x0318;
private const int WM_HANDHELDFIRST = 0x0358;
private const int WM_HANDHELDLAST = 0x035F;
private const int WM_AFXFIRST = 0x0360;
private const int WM_AFXLAST = 0x037F;
private const int WM_PENWINFIRST = 0x0380;
private const int WM_PENWINLAST = 0x038F;
private const int WM_APP = 0x8000;
private const int WM_USER = 0x0400;
private const int WM_REFLECT = WM_USER + 0x1c00;
private const int WM_APPBARNOTIFY = WM_USER + 100;
private const int WS_CAPTION = 0x00C00000;
private const int WS_SYSMENU = 0x00080000;
#endregion
#region Private API Functions
[DllImport("user32.dll", SetLastError=true)]
private static extern int AdjustWindowRectEx(ref RECT lpRect, int dsStyle, int bMenu, int dwEsStyle);
[DllImport("user32.dll", SetLastError=true)]
private static extern int ClientToScreen(IntPtr hwnd, ref POINT lpPoint);
[DllImport("user32.dll", SetLastError=true)]
private static extern Int16 GetAsyncKeyState(int vKey);
[DllImport("user32.dll", SetLastError=true)]
private static extern int GetClientRect(IntPtr hwnd, ref RECT rect);
[DllImport("user32.dll", SetLastError=true)]
private static extern int GetKeyState(Int16 nVirtKey);
[DllImport("user32.dll", SetLastError=true)]
private static extern int GetMessagePos();
[DllImport("user32.dll", SetLastError=true)]
private static extern int GetSystemMetrics(int nIndex);
[DllImport("kernel32.dll", SetLastError=true)]
private static extern int GetTickCount();
[DllImport("user32.dll", SetLastError=true)]
private static extern int GetWindowLong(IntPtr hwnd, Int16 nIndex);
[DllImport("user32.dll", SetLastError=true)]
private static extern int GetWindowRect(IntPtr hwnd, ref RECT rect);
[DllImport("user32.dll", SetLastError=true)]
private static extern int InflateRect(ref RECT rect, int x, int y);
[DllImport("User32.dll", SetLastError=true)]
private static extern int KillTimer(IntPtr hwnd, int nIDEvent);
[DllImport("User32.dll", CharSet=CharSet.Auto)]
private static extern bool ReleaseCapture();
[DllImport("Kernel32.dll", SetLastError=true)]
private static extern void RtlMoveMemory(ref RECT Destination, IntPtr Source, int Length);
[DllImport("Kernel32.dll", SetLastError=true)]
private static extern void RtlMoveMemory(IntPtr Destination, ref RECT Source, int Length);
[DllImport("user32.dll", CharSet=CharSet.Auto)]
private static extern int SendMessage(IntPtr hWnd, int msg, int wParam, int lParam);
[DllImport("user32.dll", SetLastError=true)]
private static extern int SetTimer(IntPtr hWnd, int nIDEvent, int uElapse, int lpTimerFunc);
[DllImport("User32.dll", CharSet=CharSet.Auto)]
private static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong);
[DllImport("user32.dll", SetLastError=true)]
private static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int x, int y, int Width, int Height, int flags);
[DllImport("shell32.dll", SetLastError=true)]
private static extern int SHAppBarMessage(int message, ref APPBARDATA pData);
[DllImport("user32.dll", SetLastError=true)]
private static extern int SystemParametersInfoA(int uAction, int uParam, ref bool lpvParam, int fuWinIni);
[DllImport("user32.dll", SetLastError=true)]
private static extern int SystemParametersInfoA(int uAction, int uParam, ref RECT lpvParam, int fuWinIni);
[DllImport("user32.dll", SetLastError=true)]
private static extern int UpdateWindow(IntPtr hwnd);
#endregion
#region Public Enumerations
///
/// Enumeration for the edge that the AppBar attaches to
///
public enum AppBarEdge
{
Left = ABE_LEFT,
Top = ABE_TOP,
Right = ABE_RIGHT,
Bottom = ABE_BOTTOM,
Unknown = ABE_UNKNOWN,
Float = ABE_FLOAT
}
///
/// Enumerations for the docking options
///
[FlagsAttribute()]
public enum DockFlags
{
AllowLeft = ABF_ALLOWLEFT,
AllowTop = ABF_ALLOWTOP,
AllowRight = ABF_ALLOWRIGHT,
AllowBottom = ABF_ALLOWBOTTOM,
AllowFloat = ABF_ALLOWFLOAT
}
#endregion
#region Public Properties
///
/// The edges the app bar can attach to
///
[TypeConverterAttribute(typeof(DockFlagConverter)),
Editor(typeof(DockFlagEditor), typeof(UITypeEditor)),
Category("AppBar"),
DefaultValue(DockFlags.AllowFloat),
Description("The edges the app bar can attach to")]
public DockFlags DockOptions
{
get {return (DockFlags) this.appBarState.dockFlags;}
set {this.appBarState.dockFlags = (int) value;}
}
///
/// The increments by which the AppBar can be resized horizontally
///
[Category("AppBar"),
DefaultValue(0),
Description("The increments by which the AppBar can be resized horizontally")]
public int HorzSizeInc
{
get {return this.appBarState.sizeInc.cx;}
set {this.appBarState.sizeInc.cx = value;}
}
///
/// The increments by which the AppBar can be resized vertically
///
[Category("AppBar"),
DefaultValue(0),
Description("The increments by which the AppBar can be resized vertically")]
public int VertSizeInc
{
get {return this.appBarState.sizeInc.cy;}
set {this.appBarState.sizeInc.cy = value;}
}
///
/// The default starting position of the AppBar
///
[Category("AppBar"),
DefaultValue(AppBarEdge.Float),
Description("The default starting position of the AppBar")]
public AppBarEdge Edge
{
get {return (AppBarEdge) this.appBarState.state;}
set {this.appBarState.state = (int) value;}
}
///
/// Whether or not the AppBar hides and then slides out from the edge
///
[Category("AppBar"),
DefaultValue(false),
Description("Whether or not the AppBar hides and then slides out from the edge")]
public bool AutoHide
{
get {return this.appBarState.autoHide;}
set {this.appBarState.autoHide = value;}
}
///
/// Whether the AppBar is always on top
///
[Category("AppBar"),
DefaultValue(false),
Description("Whether the AppBar is always on top")]
public bool AlwaysOnTop
{
get {return this.appBarState.alwaysOnTop;}
set {this.appBarState.alwaysOnTop = value;}
}
///
/// The width of the AppBar when docked on the side
///
[Category("AppBar"),
DefaultValue(0),
Description("The width of the AppBar when docked on the side")]
public int HorzDockSize
{
get {return this.appBarState.dockSizeHorizontal;}
set {this.appBarState.dockSizeHorizontal = value;}
}
///
/// The height of the AppBar when docked on the top or bottom
///
[Category("AppBar"),
DefaultValue(0),
Description("The height of the AppBar when docked on the top or bottom")]
public int VertDockSize
{
get {return this.appBarState.dockSizeVertical;}
set {this.appBarState.dockSizeVertical = value;}
}
#endregion
#region Private Variables
private APPBARSTATE appBarState;
private bool initializing = false;
private bool moving = false;
private System.ComponentModel.IContainer components;
#endregion
#region Constructors
public AppBarForm()
{
this.initializing = true;
components = null;
//
// Required for Windows Form Designer support
//
InitializeComponent();
//
// TODO: Add any constructor code after InitializeComponent call
//
// Force the shell to update its list of AppBars and the workarea.
// This is a precaution and is very useful when debugging. If you create
// an AppBar and then just terminate the application, the shell still
// thinks that the AppBar exists and the user's workarea is smaller than
// it should be. When a new AppBar is created, calling this function
// fixes the user's workarea.
// Set default state of AppBar to docked on bottom with no width & height
this.appBarState = new APPBARSTATE();
this.appBarState.size = Marshal.SizeOf(this.appBarState);
//if (this.DesignMode)
this.appBarState.state = ABE_FLOAT;
this.appBarState.autoHide = false;
this.appBarState.alwaysOnTop = false;
this.appBarState.dockSizeHorizontal = 30;
this.appBarState.dockSizeVertical = 30;
this.appBarState.taskBarState = AppBarMessage(ABM_GETSTATE);
this.appBarState.dockFlags = ABF_ALLOWANYWHERE;
this.appBarState.sizeInc = new SIZE(0, 0); // Don't allow re-sizing
this.appBarState.proposedState = ABE_UNKNOWN;
this.appBarState.fullScreenAppOpen = false;
this.appBarState.autoHideIsVisible = false;
this.appBarState.floatRect = new RECT(this.Left, this.Top, this.Left + this.Width, this.Top + this.Height);
this.initializing = false;
}
///
/// Clean up any resources being used.
///
protected override void Dispose( bool disposing )
{
if( disposing )
{
if (components != null)
{
components.Dispose();
}
}
base.Dispose( disposing );
}
#endregion
#region Windows Form Designer generated code
///
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
///
private void InitializeComponent()
{
//
// AppBarForm
//
this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
this.ClientSize = new System.Drawing.Size(400, 270);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.SizableToolWindow;
this.Location = new System.Drawing.Point(500, 500);
this.Name = "AppBarForm";
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
this.MouseDown += new System.Windows.Forms.MouseEventHandler(this.AppBar_MouseDown);
this.Load += new System.EventHandler(this.AppBarForm_Load);
this.LocationChanged += new System.EventHandler(this.AppBarForm_LocationChanged);
}
#endregion
#region Private Functions
private int GetAutohideEdge()
{
for (int edge = ABE_LEFT; edge <= ABE_BOTTOM; edge++)
{
if (this.Handle.ToInt32() == AppBarMessage(ABM_GETAUTOHIDEBAR, edge))
{
// We are an auto-hide AppBar and we found our edge.
return edge;
}
}
// NOTE: If AppBar is docked but not auto-hidden, we return ABE_UNKNOWN
return ABE_UNKNOWN;
}
private bool ModifyStyle(Int16 styleOffset, int remove, int add, int flags)
{
int style = GetWindowLong(this.Handle, styleOffset);
int newStyle = (style & (~ remove)) | add;
if (style == newStyle)
return false;
SetWindowLong(this.Handle, styleOffset, newStyle);
if (flags != 0)
SetWindowPos(this.Handle, new IntPtr(0), 0, 0, 0, 0, ((SWP_NOSIZE | SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE) | flags));
return true;
}
private void MimicState (int stateChangedMask, int state)
{
bool anyChange = false; // Assume that nothing changes
// If the autohide state changed AND our style allows
// us to mimic the Autohide state
if (((stateChangedMask & ABS_AUTOHIDE) != 0) &&
((this.appBarState.dockFlags & ABF_MIMICTASKBARAUTOHIDE) != 0))
{
bool autoHide = (ABS_AUTOHIDE & state) != 0;
// If our state doesn't match, change our state
if (this.appBarState.autoHide != autoHide)
{
this.appBarState.autoHide = autoHide;
anyChange = true;
}
}
// If the AlwaysOnTop state changed AND our style allows
// us to mimic the AlwaysOnTop state/
if (((stateChangedMask & ABS_ALWAYSONTOP) != 0) &&
((this.appBarState.dockFlags & ABF_MIMICTASKBARALWAYSONTOP) != 0))
{
// If our state doesn't match, change our state
this.appBarState.alwaysOnTop = (ABS_ALWAYSONTOP & state) != 0;
anyChange = true;
}
if (anyChange)
{
SetState();
ShowHiddenAppBar(false);
}
}
private void SetState(int state)
{
if (this.DesignMode)
return;
// If the AppBar is registered as auto-hide, unregister it.
int edge = GetAutohideEdge();
RECT currentRect;
RECT position = new RECT();
if (edge != ABE_UNKNOWN)
{
// Our AppBar is auto-hidden, unregister it.
AppBarMessage(ABM_SETAUTOHIDEBAR, edge, 0);
}
// Save the new requested state
this.appBarState.state = state;
switch (state)
{
case ABE_UNKNOWN:
// We are being completely unregisterred.
// Probably, the AppBar window is being destroyed.
// If the AppBar is registered as NOT auto-hide, unregister it.
AppBarMessage(ABM_REMOVE);
break;
case ABE_FLOAT:
// We are floating and therefore are just a regular window.
// Tell the shell that the docked AppBar should be of 0x0 dimensions
// so that the workspace is not affected by the AppBar.
currentRect = new RECT(0, 0, 0, 0);
AppBarMessage(ABM_SETPOS, state, 0, ref currentRect);
SetWindowPos(this.Handle, IntPtr.Zero, this.appBarState.floatRect.left, this.appBarState.floatRect.top,
this.appBarState.floatRect.Width, this.appBarState.floatRect.Height,
SWP_NOZORDER | SWP_NOACTIVATE);
break;
default:
if (this.appBarState.autoHide & !(Convert.ToBoolean(AppBarMessage(ABM_SETAUTOHIDEBAR, GetState(), 1), CultureInfo.InvariantCulture)))
{
// We couldn't set the AppBar on a new edge, let's dock it instead.
this.appBarState.autoHide = false;
// Call a virtual function to let derived classes know that the AppBar
// changed from auto-hide to docked.
OnAppBarForcedToDocked();
}
GetRect(GetState(), ref position);
if (this.appBarState.autoHide)
{
currentRect = new RECT(0,0,0,0);
AppBarMessage(ABM_SETPOS, ABE_LEFT, 0, ref currentRect);
}
else
{
// Tell the shell where the AppBar is.
AppBarMessage(ABM_SETPOS, state, 0, ref position);
}
AdjustLocationForAutohide(this.appBarState.autoHideIsVisible, ref position);
// Slide window in from or out to the edge
SlideWindow(position);
break;
}
// Set the AppBar's z-order appropriately
IntPtr pwnd = new IntPtr(HWND_NOTOPMOST); // Assume normal Z-Order
if (this.appBarState.alwaysOnTop)
{
// If we are supposed to be always-on-top, put us there.
pwnd = new IntPtr(HWND_TOPMOST);
if (this.appBarState.fullScreenAppOpen)
{
// But, if a full-screen window is opened, put ourself at the bottom
// of the z-order so that we don't cover the full-screen window
pwnd = new IntPtr(HWND_BOTTOM);
}
}
SetWindowPos(this.Handle, pwnd, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
// Make sure that any auto-hide appabrs stay on top of us after we move
// even though our activation state has not changed.
AppBarMessage(ABM_ACTIVATE);
// Tell our derived class that there is a state change
AppBarStateChange(false, state);
}
private void SetState()
{
SetState(GetState());
}
private int AppBarMessage (int message, int edge /*= ABE_FLOAT*/,
int lParam /*= 0*/, ref RECT position /*= null*/)
{
// Initialize an APPBARDATA structure.
APPBARDATA abd = new APPBARDATA();
abd.size = Marshal.SizeOf(abd);
abd.hWnd = this.Handle.ToInt32();
abd.callbackMessage = WM_APPBARNOTIFY;
abd.edge = edge;
abd.position = position;
abd.lParam = lParam;
int result = SHAppBarMessage(message, ref abd);
// If the caller passed a rectangle, return the updated rectangle.
position = abd.position;
return result;
}
private int AppBarMessage (int message, int edge /*= ABE_FLOAT*/,
int lParam /*= 0*/)
{
// Initialize an APPBARDATA structure.
APPBARDATA abd = new APPBARDATA();
abd.size = Marshal.SizeOf(abd);
abd.hWnd = this.Handle.ToInt32();
abd.callbackMessage = WM_APPBARNOTIFY;
abd.edge = edge;
abd.position = new RECT(0, 0, 0, 0);
abd.lParam = lParam;
int result = SHAppBarMessage(message, ref abd);
// If the caller passed a rectangle, return the updated rectangle.
return result;
}
private int AppBarMessage(int message)
{
return AppBarMessage(message, ABE_FLOAT, 0);
}
private int AppBarMessage(int message, int edge)
{
return AppBarMessage(message, edge, 0);
}
private int CalcProposedState (POINT point)
{
// Force the AppBar to float if the user is holding down the Ctrl key
// and the AppBar's style allows floating.
bool fForceFloat = ((GetKeyState(VK_CONTROL) & 0x8000) != 0)
&& ((this.appBarState.dockFlags & ABF_ALLOWFLOAT) != 0);
return (fForceFloat ? ABE_FLOAT : GetEdgeFromPoint(this.appBarState.dockFlags, point));
}
private void GetRect (int stateProposed, ref RECT rectProposed)
{
// This function finds the x, y, cx, cy of the AppBar window
if (ABE_FLOAT == stateProposed)
{
// The AppBar is floating, the proposed rectangle is correct
}
else
{
// The AppBar is docked or auto-hide
// Set dimensions to full screen.
rectProposed = new RECT(0, 0,
GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN));
// Subtract off what we want from the full screen dimensions
if (!this.appBarState.autoHide)
{
// Ask the shell where we can dock.
AppBarMessage(ABM_QUERYPOS, stateProposed, 0, ref rectProposed);
}
switch (stateProposed)
{
case ABE_LEFT:
rectProposed.right =
rectProposed.left + this.appBarState.dockSizeHorizontal;
break;
case ABE_TOP:
rectProposed.bottom =
rectProposed.top + this.appBarState.dockSizeVertical;
break;
case ABE_RIGHT:
rectProposed.left =
rectProposed.right - this.appBarState.dockSizeHorizontal;
break;
case ABE_BOTTOM:
rectProposed.top =
rectProposed.bottom - this.appBarState.dockSizeVertical;
break;
}
}
}
private bool AdjustLocationForAutohide (bool show, ref RECT rect)
{
if ((GetState() == ABE_UNKNOWN) || (GetState() == ABE_FLOAT) ||
!this.appBarState.autoHide)
{
// If we are not docked on an edge OR we are not auto-hidden, there is
// nothing for us to do; just return.
return false;
}
// Showing/hiding doesn't change our size; only our position.
int x = 0;
int y = 0; // Assume a position of (0, 0)
if (show)
{
// If we are on the right or bottom, calculate our visible position
switch (GetState())
{
case ABE_RIGHT:
x = GetSystemMetrics(SM_CXSCREEN) - rect.Width;
break;
case ABE_BOTTOM:
y = GetSystemMetrics(SM_CYSCREEN) - rect.Height;
break;
}
}
else
{
// Keep a part of the AppBar visible at all times
int cxVisibleBorder = 2 * GetSystemMetrics(SM_CXBORDER);
int cyVisibleBorder = 2 * GetSystemMetrics(SM_CYBORDER);
// Calculate our x or y coordinate so that only the border is visible
switch (GetState())
{
case ABE_LEFT:
x = -(rect.Width - cxVisibleBorder);
break;
case ABE_RIGHT:
x = GetSystemMetrics(SM_CXSCREEN) - cxVisibleBorder;
break;
case ABE_TOP:
y = -(rect.Height - cyVisibleBorder);
break;
case ABE_BOTTOM:
y = GetSystemMetrics(SM_CYSCREEN) - cyVisibleBorder;
break;
}
}
rect = new RECT(x, y, x + rect.Width, y + rect.Height);
return true;
}
private void ShowHiddenAppBar (bool show /*= true*/)
{
// Get our window location in screen coordinates.
RECT position = new RECT();
GetWindowRect(this.Handle, ref position);
this.appBarState.autoHideIsVisible = true; // Assume that we are visible
if (AdjustLocationForAutohide(show, ref position))
{
// the rectangle was adjusted, we are an autohide bar
// Rememebr whether we are visible or not.
this.appBarState.autoHideIsVisible = show;
// Slide window in from or out to the edge
SlideWindow(position);
}
if (show)
// Associate a timer with the AppBar. The timer is used to determine
// when a visible, inactive, auto-hide AppBar should be re-hidden.
SetTimer(this.Handle, AUTOHIDETIMERID, AUTOHIDETIMERINTERVAL, 0);
else
{
// Must have just hidden it, so don't bother with the timer for a while
KillTimer(this.Handle, AUTOHIDETIMERID);
}
}
private void SlideWindow(RECT rectEnd)
{
bool fullDragOn = false;
// Only slide the window if the user has FullDrag turned on
SystemParametersInfoA(SPI_GETDRAGFULLWINDOWS, 0, ref fullDragOn, 0);
// Get the current window position
RECT rectStart = new RECT();
GetWindowRect(this.Handle, ref rectStart);
if (fullDragOn && (rectStart != rectEnd))
{
// Get our starting and ending time.
int timeStart = GetTickCount();
int timeEnd = timeStart + AUTOHIDETIMERINTERVAL;
int time;
while ((time = GetTickCount()) < timeEnd)
{
// While we are still sliding, calculate our new position
int x = rectStart.left - (rectStart.left - rectEnd.left)
* (time - timeStart) / AUTOHIDETIMERINTERVAL;
int y = rectStart.top - (rectStart.top - rectEnd.top)
* (time - timeStart) / AUTOHIDETIMERINTERVAL;
int nWidth = rectStart.Width - (rectStart.Width - rectEnd.Width)
* (time - timeStart) / AUTOHIDETIMERINTERVAL;
int nHeight = rectStart.Height - (rectStart.Height - rectEnd.Height)
* (time - timeStart) / AUTOHIDETIMERINTERVAL;
// Show the window at its changed position
SetWindowPos(this.Handle, IntPtr.Zero, x, y, nWidth, nHeight,
SWP_NOZORDER | SWP_NOACTIVATE | SWP_DRAWFRAME);
UpdateWindow(this.Handle);
}
}
// Make sure that the window is at its final position
SetWindowPos(this.Handle, IntPtr.Zero, rectEnd.left, rectEnd.top, rectEnd.Width, rectEnd.Height,
SWP_NOZORDER | SWP_NOACTIVATE | SWP_DRAWFRAME);
UpdateWindow(this.Handle);
}
private static int GetEdgeFromPoint (int flags, POINT point)
{
int state = ABE_FLOAT; // Assume that the AppBar is floating
// Let's get floating out of the way first
if ((flags & ABF_ALLOWFLOAT) != 0)
{
// Get the rectangle that bounds the size of the screen
// minus any docked (but not-autohidden) AppBars.
RECT position = new RECT();
SystemParametersInfoA(SPI_GETWORKAREA, 0, ref position, 0);
// Leave a 1/2 width/height-of-a-scrollbar gutter around the workarea
InflateRect(ref position, -GetSystemMetrics(SM_CXVSCROLL), -GetSystemMetrics(SM_CYHSCROLL));
if (PtInRect(position, point) || ((flags & ABF_ALLOWANYEDGE) == 0))
{
// If the point is in the adjusted workarea
// OR no edges are allowed.
return state; // The AppBar should float
}
}
// If we get here, the AppBar should be docked; determine the proper edge
// Get the dimensions of the screen
int cxScreen = GetSystemMetrics(SM_CXSCREEN);
int cyScreen = GetSystemMetrics(SM_CYSCREEN);
// Find the center of the screen
POINT pointCenter = new POINT(cxScreen / 2, cyScreen / 2);
// Find the distance from the point to the center
POINT pointOffset = new POINT(point.X - pointCenter.X, point.Y - pointCenter.Y);
// Determine if the point is farther from the left/right or top/bottom
bool isLeftOrRight = (Math.Abs(pointOffset.Y) * cxScreen) <=
(Math.Abs(pointOffset.X) * cyScreen);
// If (it should be left/right, AND we allow left/right)
// OR we don't allow top/bottom
if ((isLeftOrRight && ((flags & ABF_ALLOWLEFTRIGHT) != 0)) ||
((flags & ABF_ALLOWTOPBOTTOM) == 0))
{
state = (0 <= pointOffset.X) ? ABE_RIGHT : ABE_LEFT;
}
else
{
state = (0 <= pointOffset.Y) ? ABE_BOTTOM : ABE_TOP;
}
return state; // Return calculated edge
}
private int GetState ()
{
return ((this.appBarState.proposedState != ABE_UNKNOWN)
? this.appBarState.proposedState : this.appBarState.state);
}
private bool IsEdgeLeftOrRight (int edge)
{
return ((edge == ABE_LEFT) || (edge == ABE_RIGHT));
}
private void ClientToScreen(IntPtr hwnd, ref RECT rect)
{
POINT point = new POINT();
point.X = rect.left;
point.Y = rect.top;
ClientToScreen(hwnd, ref point);
rect.left = point.X;
rect.top = point.Y;
point.X = rect.right;
point.Y = rect.bottom;
ClientToScreen(hwnd, ref point);
rect.right = point.X;
rect.bottom = point.Y;
}
private int GetStyle()
{
return GetWindowLong(this.Handle, GWL_STYLE);
}
private int GetExStyle()
{
return GetWindowLong(this.Handle, GWL_EXSTYLE);
}
private static bool PtInRect (RECT rect, POINT point)
{
if (point.X < rect.left | point.X > rect.right)
return false;
if (point.Y < rect.top | point.Y > rect.bottom)
return false;
return true;
}
private void AppBarStateChange (bool proposed, int stateProposed)
{
bool fullDragOn = false;
// Find out if (the user has FullDrag turned on
SystemParametersInfoA(SPI_GETDRAGFULLWINDOWS, 0, ref fullDragOn, 0);
// If FullDrag is turned on OR the appbar has changed position
if ((fullDragOn | ! proposed) & !(stateProposed == ABE_UNKNOWN))
{
if (stateProposed == ABE_FLOAT)
// Show the window adornments
ModifyStyle((short) GWL_STYLE, 0, (WS_CAPTION | WS_SYSMENU), (SWP_DRAWFRAME));
else
// Hide the window adornments
ModifyStyle((short) GWL_STYLE, (WS_CAPTION | WS_SYSMENU), 0, (SWP_DRAWFRAME));
}
OnAppBarStateChange(proposed, stateProposed);
}
private void ABNFullScreenApp (bool open)
{
// This function is called when a FullScreen window is openning or
// closing. A FullScreen window is a top-level window that has its caption
// above the top of the screen allowing the entire screen to be occupied
// by the window's client area.
// If the AppBar is a topmost window when a FullScreen windiw is activated,
// we need to change our window to a non-topmost window so that the AppBar
// doesn't cover the FullScreen window's client area.
// If the FullScreen window is closing, we need to set the AppBar's
// Z-Order back to when the user wants it to be.
if (this.appBarState.fullScreenAppOpen != open)
{
this.appBarState.fullScreenAppOpen = open;
SetState();
}
OnAbnFullScreenApp(open);
}
private void ABNPosChanged ()
{
// The TaskBar or another AppBar has changed its size or position.
if ((GetState() != ABE_FLOAT) && !this.appBarState.autoHide)
{
// If we're not floating and we're not auto-hidden, we have to
// reposition our window.
SetState();
}
OnAbnPosChanged();
}
private void ABNWindowArrange(bool beginning)
{
OnAbnWindowArrange(beginning);
}
private void ABNStateChange (int stateChangedMask, int state)
{
// Make our state mimic the taskbar's state.
MimicState(stateChangedMask, state);
OnAbnStateChange(stateChangedMask, state);
}
#endregion
#region Protected Functions
protected void OnMouseDown()
{
ReleaseCapture();
SendMessage(this.Handle, WM_NCLBUTTONDOWN, HTCAPTION, 0);
}
protected virtual void OnAppBarStateChange(bool proposed, int proposedState)
{
// This function intentionally left blank.
}
protected virtual void OnAppBarForcedToDocked()
{
// Display the ppBar's caption text as the message box caption text.
string sz = "AppBar";
//GetWindowText(this.Handle, sz);
MessageBox.Show(null,
"There is already an auto hidden window on this edge.\r\nOnly one auto hidden window is allowed on each edge.",
sz,MessageBoxButtons.OK, MessageBoxIcon.Stop);
}
protected virtual void OnAbnWindowArrange (bool beginning)
{
// This function intentionally left blank.
}
protected virtual void OnAbnStateChange (int stateChangedMask, int state)
{
// This function intentionally left blank.
}
protected virtual void OnAbnFullScreenApp (bool open)
{
// This function intentionally left blank.
}
private void OnAbnPosChanged ()
{
// This function intentionally left blank.
}
#endregion
#region Message Processors
[SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)]
protected override void WndProc(ref System.Windows.Forms.Message m)
{
switch (m.Msg)
{
case WM_APPBARNOTIFY:
OnAppBarCallbackMsg(ref m);
break;
case WM_CREATE:
OnCreate(ref m);
return;
case WM_DESTROY:
OnDestroy(ref m);
return;
case WM_TIMER:
OnTimer(ref m);
return;
case WM_NCHITTEST:
OnNcHitTest(ref m);
return;
case WM_ENTERSIZEMOVE:
this.moving = true;
OnEnterSizeMove(ref m);
Debug.WriteLine("OnEnterSizeMove");
break;
case WM_EXITSIZEMOVE:
OnExitSizeMove(ref m);
this.moving = false;
Debug.WriteLine("OnExitSizeMove");
break;
case WM_NCMOUSEMOVE:
OnNcMouseMove(ref m);
return;
case WM_WINDOWPOSCHANGED:
OnWindowPosChanged(ref m);
Debug.WriteLine("OnWindowPosChanged");
return;
case WM_MOVING:
OnMoving(ref m);
Debug.WriteLine("OnMoving");
break;
case WM_ACTIVATE:
OnActivate(ref m);
return;
case WM_SIZING:
OnSizing(ref m);
Debug.WriteLine("OnSizing");
break;
}
base.WndProc(ref m);
}
// Called when the AppBar recieves a WM_APPBARNOTIFY window message
private void OnAppBarCallbackMsg (ref Message message)
{
switch (message.WParam.ToInt32())
{
case ABN_FULLSCREENAPP:
ABNFullScreenApp(Convert.ToBoolean(message.WParam.ToInt32(), CultureInfo.InvariantCulture));
break;
case ABN_POSCHANGED:
ABNPosChanged();
break;
case ABN_WINDOWARRANGE:
ABNWindowArrange(Convert.ToBoolean(message.WParam.ToInt32(), CultureInfo.InvariantCulture));
break;
case ABN_STATECHANGE:
// The shell sends ABN_STATECHANGE notifications at inappropriate
// times. So, we remember the TaskBar's current state and set
// a mask indicating which states have actually changed. This mask
// and the state information is passed to the derived class.
// Get the state of the Taskbar
int taskBarState = AppBarMessage(ABM_GETSTATE);
// Prepare a mask indicating which states have changed. The code in
// the derived class should only act on the states that have changed.
int stateChangedMask = 0;
if ((taskBarState & ABS_ALWAYSONTOP) !=
(this.appBarState.taskBarState & ABS_ALWAYSONTOP))
{
stateChangedMask |= ABS_ALWAYSONTOP;
}
if ((taskBarState & ABS_AUTOHIDE) !=
(this.appBarState.taskBarState & ABS_AUTOHIDE))
{
stateChangedMask |= ABS_AUTOHIDE;
}
// Call the derived class
ABNStateChange(stateChangedMask, taskBarState);
// Save the TaskBar's state so that we can see exactly which states
// change the next time be get an ABN_STATECHANGE notification.
this.appBarState.taskBarState = taskBarState;
break;
}
message.Result = new IntPtr(0);
}
private void OnCreate(ref Message message)
{
//if (CDialog::OnCreate(lpCreateStruct) == -1)
// return(-1);
base.WndProc(ref message);
// Register our AppBar window with the Shell
AppBarMessage(ABM_NEW);
// Force the AppBar to mimic the state of the TaskBar
// Assume that all states have changed
MimicState(ABS_ALWAYSONTOP | ABS_AUTOHIDE, this.appBarState.taskBarState);
message.Result = new IntPtr(0);
}
private void OnDestroy(ref Message message)
{
//// CDialog::OnDestroy();
base.WndProc(ref message);
// Kill the Autohide timer
KillTimer(this.Handle, AUTOHIDETIMERID);
// Unregister our AppBar window with the Shell
SetState(ABE_UNKNOWN);
}
private void OnWindowPosChanged(ref Message message)
{
// CDialog::OnWindowPosChanged(lpwndpos);
base.WndProc(ref message);
// When our window changes position, tell the Shell so that any
// auto-hidden AppBar on our edge stays on top of our window making it
// always accessible to the user.
AppBarMessage(ABM_WINDOWPOSCHANGED);
}
private void OnActivate(ref Message message)
{
//// CDialog::OnActivate(nState, pWndOther, bMinimized);
base.WndProc(ref message);
if ((message.WParam.ToInt32() & 0xFFFF) == WA_INACTIVE)
{
// Hide the AppBar if we are docked and auto-hidden
ShowHiddenAppBar(false);
}
// When our window changes position, tell the Shell so that any
// auto-hidden AppBar on our edge stays on top of our window making it
// always accessible to the user.
AppBarMessage(ABM_ACTIVATE);
}
private void OnTimer(ref Message message)
{
// Get the position of the mouse and the AppBar's position
// Everything must be in screen coordinates.
POINT point = new POINT(GetMessagePos());
RECT position = new RECT();
GetWindowRect(this.Handle, ref position);
// Add a little margin around the AppBar
InflateRect(ref position, 2 * GetSystemMetrics(SM_CXDOUBLECLK),
2 * GetSystemMetrics(SM_CYDOUBLECLK));
if (!PtInRect(position, point))
{
// If the mouse is NOT over the AppBar, hide the AppBar
ShowHiddenAppBar(false);
}
}
private void OnNcMouseMove(ref Message message)
{
// If we are a docked, auto-hidden AppBar, shown us
// when the user moves over our non-client area
ShowHiddenAppBar(true);
//// CDialog::OnNcMouseMove(nHitTest, point);
base.WndProc(ref message);
}
private void OnNcHitTest(ref Message message)
{
// Find out what the system thinks is the hit test code
//// int hitResult = CDialog::OnNcHitTest(point);
base.WndProc(ref message);
int hitResult = message.Result.ToInt32();
// NOTE: If the user presses the secondary mouse button, pretend that the
// user clicked on the client area so that we get WM_CONTEXTMENU messages
bool primaryMouseBtnDown = Convert.ToBoolean((GetAsyncKeyState(GetSystemMetrics(SM_SWAPBUTTON) != 0
? VK_RBUTTON : VK_LBUTTON) & 0x8000) != 0, CultureInfo.InvariantCulture);
if ((hitResult == HTCLIENT) && primaryMouseBtnDown)
{
// User clicked in client area, allow AppBar to move. We get this
// behavior by pretending that the user clicked on the caption area.
hitResult = HTCAPTION;
}
// When the AppBar is docked, the user can resize only one edge.
// This next section determines which edge the user can resize.
// To allow resizing, the AppBar window must have the WS_THICKFRAME style.
// If the AppBar is docked and the hittest code is a resize code...
if ((GetState() != ABE_FLOAT) && (GetState() != ABE_UNKNOWN) &&
(HTSIZEFIRST <= hitResult) && (hitResult <= HTSIZELAST))
{
if (0 == (IsEdgeLeftOrRight(GetState()) ?
this.appBarState.sizeInc.cx : this.appBarState.sizeInc.cy))
{
// If the width/height size increment is zero, then resizing is NOT
// allowed for the edge that the AppBar is docked on.
hitResult = HTBORDER; // Pretend that the mouse is not on a resize border
}
else
{
// Resizing IS allowed for the edge that the AppBar is docked on.
// Get the location of the appbar's client area in screen coordinates.
RECT rect = new RECT();
GetClientRect(this.Handle, ref rect);
ClientToScreen(this.Handle, ref rect);
hitResult = HTBORDER; // Assume that we can't resize
POINT point = new POINT(message.LParam.ToInt32());
switch (GetState())
{
case ABE_LEFT:
if (point.X > rect.right) hitResult = HTRIGHT;
break;
case ABE_TOP:
if (point.Y > rect.bottom) hitResult = HTBOTTOM;
break;
case ABE_RIGHT:
if (point.X < rect.left) hitResult = HTLEFT;
break;
case ABE_BOTTOM:
if (point.Y < rect.top) hitResult = HTTOP;
break;
}
}
}
message.Result = new IntPtr(hitResult); // Return the hittest code
}
private void OnEnterSizeMove(ref Message message)
{
// The user started moving/resizing the AppBar, save its current state.
this.appBarState.proposedState = GetState();
message.Result = new IntPtr(0);
}
private void OnExitSizeMove(ref Message message)
{
// The user stopped moving/resizing the AppBar, set the new state.
// Save the new proposed state of the AppBar.
int proposedState = this.appBarState.proposedState;
// Set the proposed state back to unknown. This causes GetState
// to return the current state rather than the proposed state.
this.appBarState.proposedState = ABE_UNKNOWN;
// Get the location of the window in screen coordinates
RECT position = new RECT();
GetWindowRect(this.Handle, ref position);
// If the AppBar's state has changed...
if (GetState() == proposedState)
{
switch (GetState())
{
case ABE_UNKNOWN:
break;
case ABE_LEFT:
case ABE_RIGHT:
// Save the new width of the docked AppBar
this.appBarState.dockSizeHorizontal = position.Width;
break;
case ABE_TOP:
case ABE_BOTTOM:
this.appBarState.dockSizeVertical = position.Height;
break;
}
}
// Always save the new position of the floating AppBar
if (proposedState == ABE_FLOAT)
{
position.right = position.left + this.appBarState.floatRect.Width;
position.bottom = position.top + this.appBarState.floatRect.Height;
this.appBarState.floatRect = position;
}
// After setting the dimensions, set the AppBar to the proposed state
SetState(proposedState);
if (this.moving)
this.Visible = true;
message.Result = new IntPtr(0);
}
private void OnMoving(ref Message message)
{
// We control the moving of the AppBar. For example, if the mouse moves
// close to an edge, we want to dock the AppBar.
// The lParam contains the window's position proposed by the system
RECT rect = new RECT();
RtlMoveMemory(ref rect, message.LParam, Marshal.SizeOf(rect));
// Get the location of the mouse cursor
POINT point = new POINT(GetMessagePos());
// Where should the AppBar be based on the mouse position?
int stateProposed = CalcProposedState(point);
if ((this.appBarState.proposedState != ABE_FLOAT) && (stateProposed == ABE_FLOAT))
{
// While moving, the user took us from a docked/autohidden state to
// the float state. We have to calculate a rectangle location so that
// the mouse cursor stays inside the window.
rect = this.appBarState.floatRect;
rect = new RECT(point.X - rect.Width / 2, point.Y,
(point.X - rect.Width / 2) + rect.Width, point.Y + rect.Height);
}
// Remember the most-recently proposed state
this.appBarState.proposedState = stateProposed;
// Tell the system where to move the window based on the proposed state
GetRect(stateProposed, ref rect);
// Tell our derived class that there is a proposed state change
AppBarStateChange(true, stateProposed);
RtlMoveMemory(message.LParam, ref rect, Marshal.SizeOf(rect));
message.Result = new IntPtr(0);
}
private void OnSizing(ref Message message)
{
// We control the sizing of the AppBar. For example, if the user re-sizes
// an edge, we want to change the size in descrete increments.
// The lParam contains the window's position proposed by the system
RECT rect = new RECT();
RtlMoveMemory(ref rect, message.LParam, Marshal.SizeOf(rect));
// Get the minimum size of the window assuming it has no client area.
// This is the width/height of the window that must always be present
RECT rectBorder = new RECT(0, 0, 0, 0);
AdjustWindowRectEx(ref rectBorder, GetStyle(), 0, GetExStyle());
// We force the window to resize in discrete units set by the this.appBarState.sizeInc
// member. From the new, proposed window dimensions passed to us, round
// the width/height to the nearest discrete unit.
int newWidth;
int newHeight;
if (this.appBarState.sizeInc.cx != 0)
newWidth = ((rect.Width - rectBorder.Width) + this.appBarState.sizeInc.cx / 2) /
this.appBarState.sizeInc.cx * this.appBarState.sizeInc.cx + rectBorder.Width;
else
newWidth = rect.right - rect.left;
if (this.appBarState.sizeInc.cy != 0)
newHeight = ((rect.Height - rectBorder.Height) + this.appBarState.sizeInc.cy / 2) /
this.appBarState.sizeInc.cy * this.appBarState.sizeInc.cy + rectBorder.Height;
else
newHeight = rect.bottom - rect.top;
// Adjust the rectangle's dimensions
switch (message.WParam.ToInt32())
{
case WMSZ_LEFT:
rect.left = rect.right - newWidth;
break;
case WMSZ_TOP:
rect.top = rect.bottom - newHeight;
break;
case WMSZ_RIGHT:
rect.right = rect.left + newWidth;
break;
case WMSZ_BOTTOM:
rect.bottom = rect.top + newHeight;
break;
case WMSZ_BOTTOMLEFT:
rect.bottom = rect.top + newHeight;
rect.left = rect.right - newWidth;
break;
case WMSZ_BOTTOMRIGHT:
rect.bottom = rect.top + newHeight;
rect.right = rect.left + newWidth;
break;
case WMSZ_TOPLEFT:
rect.left = rect.right - newWidth;
rect.top = rect.bottom - newHeight;
break;
case WMSZ_TOPRIGHT:
rect.top = rect.bottom - newHeight;
rect.right = rect.left + newWidth;
break;
}
RtlMoveMemory(message.LParam, ref rect, Marshal.SizeOf(rect));
message.Result = new IntPtr(0);
}
#endregion
#region Event Handlers
private void AppBar_MouseDown(object sender, System.Windows.Forms.MouseEventArgs e)
{
// Trap the mousedown to allow moving the form by clicking on the client area.
OnMouseDown();
}
private void AppBarForm_LocationChanged(object sender, System.EventArgs e)
{
// Trap when the forms location is set from the inheriting forms properties.
// Basically, this event fires once for the AppBar class, but initializing will be true
// It fires again when the inheriting form sets it's location. At this time, we record
// the location and size of the form, and then disable this event from firing again
if (!this.initializing)
{
this.appBarState.floatRect.left = this.Left;
this.appBarState.floatRect.top = this.Top;
this.appBarState.floatRect.right = this.Right;
this.appBarState.floatRect.bottom = this.Bottom;
this.LocationChanged -= new System.EventHandler(this.AppBarForm_LocationChanged);
}
}
private void AppBarForm_Load(object sender, System.EventArgs e)
{
if (!this.DesignMode)
SetState(this.appBarState.state);
}
#endregion
#region Custom Editor
///
/// This code is courtesy of Jose Castanedo of Janus Systems
/// www.janusys.com
/// Provides a custom editor for the Docking Flags which displays as a drop down list with check
/// boxes allowing the user to select one or more options
///
[Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced)]
public class DockFlagConverter : TypeConverter
{
///
/// Constructor takes no arguments
///
public DockFlagConverter()
{
}
public override bool CanConvertTo(System.ComponentModel.ITypeDescriptorContext context,Type type)
{
if (type == typeof(string))
{
return true;
}
else
{
return base.CanConvertTo(type);
}
}
public override object ConvertTo(System.ComponentModel.ITypeDescriptorContext context,System.Globalization.CultureInfo culture,object value,Type destinationType)
{
if (value == null)
{
return string.Empty;
}
else
{
Type type = value.GetType();
string strValue = string.Empty;
Array values = Enum.GetValues(type);
if (type == typeof(DockFlags))
{
for(int i = 0; i
0) strValue += ", ";
strValue += values.GetValue(i).ToString();
}
}
}
}
}
return strValue;
}
}
}
[Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced)]
public class DockFlagEditor : System.Drawing.Design.UITypeEditor
{
private IWindowsFormsEditorService edSvc = null;
public DockFlagEditor()
{
}
protected virtual void SetEditorProps(object value,System.Windows.Forms.CheckedListBox editor)
{
Type type = value.GetType();
Array values = Enum.GetValues(type);
for (int i = 0; i < values.Length; i++)
{
editor.Items.Add(values.GetValue(i));
if ((int) values.GetValue(i) != 0)
{
if (((int) value & (int) values.GetValue(i)) == (int) values.GetValue(i))
{
editor.SetItemChecked(i, true);
}
}
else
{
if ((int) value == 0)
{
editor.SetItemChecked(i, true);
}
}
}
editor.BorderStyle = System.Windows.Forms.BorderStyle.None;
}
public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value)
{
if (context != null && context.Instance != null && provider != null)
{
edSvc = (IWindowsFormsEditorService) provider.GetService(typeof(IWindowsFormsEditorService));
if (edSvc != null)
{
System.Windows.Forms.CheckedListBox listBox = new System.Windows.Forms.CheckedListBox();
listBox.CheckOnClick = true;
this.SetEditorProps(value, listBox);
edSvc.DropDownControl(listBox);
if (value.GetType() == typeof(DockFlags))
{
DockFlags proposedValue = DockFlags.AllowFloat;
for(int i=0; i