Custom Window Style and Accounting for the Taskbar

来源:互联网 发布:农业大学网络教育 编辑:程序博客网 时间:2024/06/01 07:41

November 10, 2010 by bbauer42

A little while back I was tasked with creating a custom window in WPF that had its own look and feel. In order to do this you must first set the WindowStyle to None. This is great because it opens the door for you to customize your new window to look however you want. Unfortunately, when you set your WindowStyle to None you lose all of the base functionality of the window. You now have to implement native window actions such as sizing, dragging, the minimize, maximize/restore, and close buttons, etc. After looking around the web for a while I found a really good starting point for creating my custom window. Here is a link to a sample project that has an Office Style custom window already created.

http://blog.opennetcf.com/ayakhnin/content/binary/OfficeStyleWindow.zip

In this project you will find a nicely styled window whose look should be easily customizable. Now we just have to add some code to make our new window accommodate for the taskbar. One thing we must be especially aware of is accommodating for the taskbar when it is set to auto-hide. When the taskbar is auto-hidden, you have to leave 2 pixels available for the bar on the docked edge so the user can mouse over that area to restore the hidden taskbar.

After much testing and digging I ended up writing a public static WindowSizing class that should handle the bar correctly. Using the sample linked above, we must first insert these two lines inside of the Window1 constructor after InitializeComponent(); in the Window1.xaml.cs file.

this.Loaded += new RoutedEventHandler(Window1_Loaded);this.SourceInitialized += new EventHandler(Window1_SourceInitialized);

Here is the code for the event handlers.

void Window1_Loaded(object sender, RoutedEventArgs e){     this.WindowState = WindowState.Maximized;}void Window1_SourceInitialized(object sender, EventArgs e){     WindowSizing.WindowInitialized(this);}

And here is the public static WindowSizing class code which should be self explanatory.

using System;using System.Runtime.InteropServices;using System.Windows;namespace OfficeStyleWindowProject{    public static class WindowSizing    {        const int MONITOR_DEFAULTTONEAREST = 0x00000002;        #region DLLImports        [DllImport("shell32", CallingConvention = CallingConvention.StdCall)]        public static extern int SHAppBarMessage(int dwMessage, ref APPBARDATA pData);        [DllImport("user32", SetLastError = true)]        static extern IntPtr FindWindow(string lpClassName, string lpWindowName);        [DllImport("user32")]        internal static extern bool GetMonitorInfo(IntPtr hMonitor, MONITORINFO lpmi);        [DllImport("user32")]        internal static extern IntPtr MonitorFromWindow(IntPtr handle, int flags);        #endregion        private static MINMAXINFO AdjustWorkingAreaForAutoHide(IntPtr monitorContainingApplication, MINMAXINFO mmi)        {            IntPtr hwnd = FindWindow("Shell_TrayWnd", null);            if (hwnd == null) return mmi;            IntPtr monitorWithTaskbarOnIt = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);            if (!monitorContainingApplication.Equals(monitorWithTaskbarOnIt)) return mmi;            APPBARDATA abd = new APPBARDATA();            abd.cbSize = Marshal.SizeOf(abd);            abd.hWnd = hwnd;            SHAppBarMessage((int)ABMsg.ABM_GETTASKBARPOS, ref abd);            int uEdge = GetEdge(abd.rc);            bool autoHide = System.Convert.ToBoolean(SHAppBarMessage((int)ABMsg.ABM_GETSTATE, ref abd));            if (!autoHide) return mmi;            switch (uEdge)            {                case (int)ABEdge.ABE_LEFT:                    mmi.ptMaxPosition.x += 2;                    mmi.ptMaxTrackSize.x -= 2;                    mmi.ptMaxSize.x -= 2;                    break;                case (int)ABEdge.ABE_RIGHT:                    mmi.ptMaxSize.x -= 2;                    mmi.ptMaxTrackSize.x -= 2;                    break;                case (int)ABEdge.ABE_TOP:                    mmi.ptMaxPosition.y += 2;                    mmi.ptMaxTrackSize.y -= 2;                    mmi.ptMaxSize.y -= 2;                    break;               case (int)ABEdge.ABE_BOTTOM:                    mmi.ptMaxSize.y -= 2;                    mmi.ptMaxTrackSize.y -= 2;                    break;                default:                    return mmi;            }            return mmi;        }        private static int GetEdge(RECT rc)        {            int uEdge = -1;            if (rc.top == rc.left && rc.bottom > rc.right)                uEdge = (int)ABEdge.ABE_LEFT;            else if (rc.top == rc.left && rc.bottom < rc.right)                uEdge = (int)ABEdge.ABE_TOP;            else if (rc.top > rc.left)                uEdge = (int)ABEdge.ABE_BOTTOM;            else                uEdge = (int)ABEdge.ABE_RIGHT;            return uEdge;        }        public static void WindowInitialized(Window window)        {            IntPtr handle = (new System.Windows.Interop.WindowInteropHelper(window)).Handle;            System.Windows.Interop.HwndSource.FromHwnd(handle).AddHook(new System.Windows.Interop.HwndSourceHook(WindowProc));        }        private static IntPtr WindowProc(System.IntPtr hwnd, int msg, System.IntPtr wParam, System.IntPtr lParam, ref bool handled)        {            switch (msg)            {               case 0x0024:                    WmGetMinMaxInfo(hwnd, lParam);                    handled = true;                    break;            }            return (IntPtr)0;        }        private static void WmGetMinMaxInfo(IntPtr hwnd, IntPtr lParam)        {            MINMAXINFO mmi = (MINMAXINFO)Marshal.PtrToStructure(lParam, typeof(MINMAXINFO));            IntPtr monitorContainingApplication = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);            if (monitorContainingApplication != System.IntPtr.Zero)            {                MONITORINFO monitorInfo = new MONITORINFO();                GetMonitorInfo(monitorContainingApplication, monitorInfo);                RECT rcWorkArea = monitorInfo.rcWork;                RECT rcMonitorArea = monitorInfo.rcMonitor;                mmi.ptMaxPosition.x = Math.Abs(rcWorkArea.left - rcMonitorArea.left);                mmi.ptMaxPosition.y = Math.Abs(rcWorkArea.top - rcMonitorArea.top);                mmi.ptMaxSize.x = Math.Abs(rcWorkArea.right - rcWorkArea.left);                mmi.ptMaxSize.y = Math.Abs(rcWorkArea.bottom - rcWorkArea.top);                mmi.ptMaxTrackSize.x = mmi.ptMaxSize.x;                                                 //maximum drag X size for the window                mmi.ptMaxTrackSize.y = mmi.ptMaxSize.y;                                                 //maximum drag Y size for the window                mmi.ptMinTrackSize.x = 800;                                                             //minimum drag X size for the window                mmi.ptMinTrackSize.y = 600;                                                             //minimum drag Y size for the window                mmi = AdjustWorkingAreaForAutoHide(monitorContainingApplication, mmi);                  //need to adjust sizing if taskbar is set to autohide            }            Marshal.StructureToPtr(mmi, lParam, true);        }        public enum ABEdge        {            ABE_LEFT = 0,            ABE_TOP = 1,            ABE_RIGHT = 2,            ABE_BOTTOM = 3        }        public enum ABMsg        {            ABM_NEW = 0,            ABM_REMOVE = 1,            ABM_QUERYPOS = 2,            ABM_SETPOS = 3,            ABM_GETSTATE = 4,            ABM_GETTASKBARPOS = 5,            ABM_ACTIVATE = 6,            ABM_GETAUTOHIDEBAR = 7,            ABM_SETAUTOHIDEBAR = 8,            ABM_WINDOWPOSCHANGED = 9,            ABM_SETSTATE = 10        }        [StructLayout(LayoutKind.Sequential)]        public struct APPBARDATA        {            public int cbSize;            public IntPtr hWnd;            public int uCallbackMessage;            public int uEdge;            public RECT rc;            public bool lParam;        }        [StructLayout(LayoutKind.Sequential)]        public struct MINMAXINFO        {            public POINT ptReserved;            public POINT ptMaxSize;            public POINT ptMaxPosition;            public POINT ptMinTrackSize;            public POINT ptMaxTrackSize;        };        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]        public class MONITORINFO        {            public int cbSize = Marshal.SizeOf(typeof(MONITORINFO));            public RECT rcMonitor = new RECT();            public RECT rcWork = new RECT();            public int dwFlags = 0;        }        [StructLayout(LayoutKind.Sequential)]        public struct POINT        {            public int x;            public int y;            public POINT(int x, int y)            {                this.x = x;                this.y = y;            }        }        [StructLayout(LayoutKind.Sequential, Pack = 0)]        public struct RECT        {            public int left;            public int top;            public int right;            public int bottom;        }    }}
0 0