WinForms形成皮肤
介绍 SkinForm类允许您创建自定义皮肤。net WinForms表单对象,对象多达你想补充道,并处理许多事件(如鼠标、键盘和油漆)对你的皮肤像你使用标准的控制。 背景 很多WinForms应用程序有自己的皮肤有更好的外观和感觉与标准相比WinForms的皮肤。也少的WinForms应用程序有用我们的应用程序中,这是地方放置表单的文本和系统按钮(在我的环境中,我称之为酒吧形式)。此外,改善我们的外观和感觉,更好地利用屏幕的面积可以给我们一个特殊的点。所以,我们为什么不尝试创建更好的自己吗? 预览 自定义皮肤(AiSkin): 自定义皮肤(AiSkin)额外的自定义按钮放置在系统按钮: 自定义皮肤(AiSkin)额外的自定义按钮放置在系统按钮,和选项卡: 主要的对象 有几类皮肤(在Ai。控制命名空间): 主要课程: SkinForm类。 这是主要的类来管理皮肤和皮肤之间的联系形式。 FormHook类。 从NativeWindow继承类。定意把消息处理通过指向剥皮的形式。这个类包含在SkinForm类。 SkinBase类。 基类皮肤管理。这个类包含皮肤的变量信息,被FormHook类和函数来处理消息。 BarFormButton类。 类代表一个按钮将被放置在酒吧的形式。 MinimizeButton类。 继承自BarFormButton,代表了最小化按钮的形式。 MaximizeButton类。 继承自BarFormButton,表示表单的最大化按钮。 CloseButton类。 继承BarFormButton,代表了关闭按钮的形式。 其他类: CustomButton类。 继承自BarFormButton,代表了一个自定义按钮的形式。 TabFormButton类。 类代表一个选项卡按钮,将放在吧台形式。BarFormButtonCollection类。 代表BarFormButton对象的集合。这个集合不能包含MinimizeButton之一,MaximizeButton或CloseButton。 TabFormButtonCollection类。 代表TabFormButton对象的集合。 Win32API类。 封装结构、外部函数和常量用于win32 API调用。 示例类: AiSkin类。 SkinBase的实现类,支持标签和自定义按钮。 创建和使用自己的自定义皮肤,创建一个类,继承SkinBase类,创建一个SkinForm类的实例并定制皮肤类在WinForms表单对象,并设置形式和皮肤SkinForm实例的属性的实例WinForms形式和定制皮肤的实例,如下: 隐藏,复制Code
public class YourSkin : SkinBase { // your implementation here } public class YourForm : System.Windows.Forms.Form { public YourForm() { // Create an instance of SkinForm class. SkinForm sf = new SkinForm(); // Create an instance of YourSkin class. YourSkin skin = new YourSkin(); // Sets both Form and Skin property of sf. sf.Skin = skin; sf.Form = this; } }
简要描述 以下是简短的解释重要的事情来开发定制的皮肤。 FormHook 这个类的目的是把剥了皮的窗口消息处理形式通过其指向功能。这个类将提供皮肤所需的任何事件的过程。对于键盘消息处理,这类使用windows原始输入功能。剥了皮的形式将注册接收原始表单的输入消息HandleCreated事件时触发。 隐藏,复制Code
private class FormHook : NativeWindow { // ... /// <summary> /// Called when the handle of the form is created. /// </summary> private void form_HandleCreated(object sender, EventArgs e) { AssignHandle(((Form)sender).Handle); // Registering form for raw input Win32API.RAWINPUTDEVICE[] rid = new Win32API.RAWINPUTDEVICE[1]; rid[0].usUsagePage = 0x01; rid[0].usUsage = 0x06; rid[0].dwFlags = Win32API.RIDEV_INPUTSINK; rid[0].hwndTarget = ((Form)sender).Handle; _RIDRegistered = Win32API.RegisterRawInputDevices (rid, (uint)rid.Length, (uint)Marshal.SizeOf(rid[0])); if (isProcessNCArea()) { updateStyle(); updateCaption(); } } }
窗口消息连接过程发生在指向这个类的函数。 隐藏,收缩,复制Code
private class FormHook : NativeWindow { // ... /// <summary> /// Invokes the default window procedure associated with this window. /// </summary> [PermissionSet(SecurityAction.Demand, Name = "FullTrust")] protected override void WndProc(ref Message m) { bool suppressOriginalMessage = false; switch (m.Msg) { case Win32API.WM_STYLECHANGED: updateStyle(); if (_owner._skin != null) _owner._skin.setRegion(_owner._form.Size); break; #region Form Activation case Win32API.WM_ACTIVATEAPP: if (_owner._skin != null) _owner._skin.FormIsActive = (int)m.WParam != 0; onNCPaint(true); break; case Win32API.WM_ACTIVATE: if (_owner._skin != null) _owner._skin.FormIsActive = ((int)Win32API.WA_ACTIVE == (int)m.WParam || (int)Win32API.WA_CLICKACTIVE == (int)m.WParam); onNCPaint(true); break; case Win32API.WM_MDIACTIVATE: if (m.WParam == _owner._form.Handle) { if (_owner._skin != null) _owner._skin.FormIsActive = false; } else if (m.LParam == _owner._form.Handle) { if (_owner._skin != null) _owner._skin.FormIsActive = true; } onNCPaint(true); break; #endregion #region Mouse Events case Win32API.WM_NCLBUTTONDOWN: case Win32API.WM_NCRBUTTONDOWN: case Win32API.WM_NCMBUTTONDOWN: suppressOriginalMessage = onNCMouseDown(ref m); break; case Win32API.WM_NCLBUTTONUP: case Win32API.WM_NCMBUTTONUP: case Win32API.WM_NCRBUTTONUP: suppressOriginalMessage = onNCMouseUp(ref m); break; case Win32API.WM_NCMOUSEMOVE: suppressOriginalMessage = onNCMouseMove(ref m); break; case Win32API.WM_NCMOUSELEAVE: case Win32API.WM_MOUSELEAVE: case Win32API.WM_MOUSEHOVER: _owner._skin.onMouseLeave(); break; case Win32API.WM_NCLBUTTONDBLCLK: suppressOriginalMessage = onNCDoubleClick(ref m); break; #endregion #region Non-client Hit Test case Win32API.WM_NCHITTEST: suppressOriginalMessage = onNCHitTest(ref m); break; #endregion #region Painting and sizing operation case Win32API.WM_NCPAINT: if (onNCPaint(true)) { m.Result = (IntPtr)1; suppressOriginalMessage = true; } break; case Win32API.WM_NCCALCSIZE: if (m.WParam == (IntPtr)1) { if (!isProcessNCArea()) break; Win32API.NCCALCSIZE_PARAMS p = (Win32API.NCCALCSIZE_PARAMS)m.GetLParam (typeof(Win32API.NCCALCSIZE_PARAMS)); if (_owner._skin != null) p = _owner._skin.calculateNonClient(p); Marshal.StructureToPtr(p, m.LParam, true); suppressOriginalMessage = true; } break; case Win32API.WM_SHOWWINDOW: if (_owner._skin != null) _owner._skin.setRegion(_owner._form.Size); break; case Win32API.WM_SIZE: onResize(m); break; case Win32API.WM_GETMINMAXINFO: suppressOriginalMessage = calculateMaximumSize(ref m); break; case Win32API.WM_WINDOWPOSCHANGING: Win32API.WINDOWPOS wndPos = (Win32API.WINDOWPOS)m.GetLParam (typeof(Win32API.WINDOWPOS)); if ((wndPos.flags & Win32API.SWP_NOSIZE) == 0) { if (_owner._skin != null) _owner._skin.setRegion (new Size(wndPos.cx, wndPos.cy)); } break; case Win32API.WM_WINDOWPOSCHANGED: if (_owner._form.WindowState == FormWindowState.Maximized) _owner._form.Region = null; Win32API.WINDOWPOS wndPos2 = (Win32API.WINDOWPOS)m.GetLParam (typeof(Win32API.WINDOWPOS)); if ((wndPos2.flags & (int)Win32API.SWP_NOSIZE) == 0) { updateCaption(); onNCPaint(true); } break; #endregion #region Raw Input case Win32API.WM_INPUT: if (_owner._skin != null) { if (_owner._skin.FormIsActive) { uint dwSize = 0, receivedBytes; uint szRIHeader = (uint)Marshal.SizeOf(typeof(Win32API.RAWINPUTHEADER)); int res = Win32API.GetRawInputData(m.LParam, Win32API.RID_INPUT, IntPtr.Zero, ref dwSize, szRIHeader); if (res == 0) { IntPtr buffer = Marshal.AllocHGlobal((int)dwSize); if (buffer != IntPtr.Zero) { receivedBytes = (uint)Win32API.GetRawInputData (m.LParam, Win32API.RID_INPUT, buffer, ref dwSize, szRIHeader); Win32API.RAWINPUT raw = (Win32API.RAWINPUT) Marshal.PtrToStructure(buffer, typeof(Win32API.RAWINPUT)); if (raw.header.dwType == Win32API.RIM_TYPEKEYBOARD) { // Process keyboard event. if (raw.keyboard.Message == Win32API.WM_KEYDOWN || raw.keyboard.Message == Win32API.WM_SYSKEYDOWN) { ushort key = raw.keyboard.VKey; Keys kd = (Keys)Enum.Parse(typeof(Keys), Enum.GetName(typeof(Keys), key)); if (kd != System.Windows.Forms.Control.ModifierKeys) kd = kd | System.Windows.Forms.Control.ModifierKeys; // Call skin's onKeyDown function. KeyEventArgs ke = new KeyEventArgs(kd); suppressOriginalMessage = _owner._skin.onKeyDown(ke); } } } } } } break; #endregion } if(!suppressOriginalMessage) base.WndProc(ref m); } }
SkinBase 这个类封装了基本组件和功能需要构建自己的皮肤。 3系统的基本组件包括按钮(最小化、最大化、关闭),矩形对持有非客户区信息。 隐藏,复制Code
public abstract class SkinBase : IDisposable { // ... #region Protected Fields protected MinimizeButton _minimizeButton = new MinimizeButton(); protected MaximizeButton _maximizeButton = new MaximizeButton(); protected CloseButton _closeButton = new CloseButton(); protected bool _formIsActive = true; #region Standard Rectangle for Non-client area protected Rectangle _rectClient; protected Rectangle _rectIcon; protected internal Rectangle _rectBar; protected Rectangle _rectBorderTop; protected internal Rectangle _rectBorderLeft; protected internal Rectangle _rectBorderBottom; protected internal Rectangle _rectBorderRight; protected Rectangle _rectBorderTopLeft; protected Rectangle _rectBorderTopRight; protected Rectangle _rectBorderBottomLeft; protected Rectangle _rectBorderBottomRight; #endregion #endregion }
基本功能对皮肤形成消息处理,封面激活/失活,碰撞测试,形成大小/状态改变/文本变化,非客户区鼠标事件(鼠标,鼠标,下双击),和按键按下。鼠标事件和碰撞测试,鼠标指针的位置是相对于表单的左上角,而不是屏幕。 隐藏,收缩,复制Code
public abstract class SkinBase : IDisposable { // ... /// <summary> /// Called when the text property of the form has been changed. /// </summary> protected internal abstract void onFormTextChanged(); /// <summary> /// Called when the left button of the mouse is double-clicked on the /// non-client area of the form. /// </summary> protected internal abstract bool onDoubleClick(); /// <summary> /// Called when the mouse pointer is moved over the non-client area of the form. /// </summary> protected internal abstract bool onMouseMove(MouseEventArgs e); /// <summary> /// Called when the mouse pointer is over the non-client area of the form /// and a mouse button is pressed. /// </summary> protected internal abstract bool onMouseDown(MouseEventArgs e); /// <summary> /// Called when the mouse pointer is over the non-client area of the form /// and a mouse button is released. /// </summary> protected internal abstract bool onMouseUp(MouseEventArgs e); /// <summary> /// Called when the mouse pointer is leaving the non-client area of the form. /// </summary> protected internal abstract bool onMouseLeave(); /// <summary> /// Called when the non-client area of the form is redrawn /// </summary> protected internal abstract bool onPaint(PaintEventArgs e); /// <summary> /// Called when one of the registered keys of the skin is pressed. /// </summary> protected internal abstract bool onKeyDown(KeyEventArgs e); /// <summary> /// Called when the form need to set its region. /// </summary> protected internal abstract bool setRegion(Size size); /// <summary> /// Called when the non-client are of the form need to be calculated. /// </summary> protected internal abstract Win32API.NCCALCSIZE_PARAMS calculateNonClient(Win32API.NCCALCSIZE_PARAMS p); /// <summary> /// Called when the bar of the form is updated. /// </summary> protected internal abstract void updateBar(Rectangle rect); /// <summary> /// Called when the hit-test is performed on the non-client area of the form. /// </summary> protected internal abstract int nonClientHitTest(Point p); }
在创建定制皮肤通过继承SkinBase类时,重要的事情我们需要注意calculateNonClient和nonClientHitTest功能。 calculateNonClient函数是函数,你必须决定的大小非客户区域的形式通过修改p。rect0值: 减少p.rect0的价值。你领域的高度酒吧形式。减少p.rect0的价值。离开字段的表单的左边框的宽度。减少p.rect0的价值。正确的字段的表单的右边框的宽度。减少p.rect0的价值。底部高度字段的表单的底部边框。 Hide,收缩,复制Code
protected internal override Win32API.NCCALCSIZE_PARAMS calculateNonClient (Win32API.NCCALCSIZE_PARAMS p) { // Check if we don't need to calculate the client area. if (Form == null || Form.WindowState == FormWindowState.Minimized || (Form.WindowState == FormWindowState.Minimized && Form.MdiParent != null)) return p; // Calculate the valid client area of the form here, that is stored // in rect0 of the p parameter. p.rect0.Top += _rectBar.Height; _rectClient.Y = _rectBar.Height + 1; if (Form.WindowState == FormWindowState.Maximized) { // The form is maximized, thus the borders will not be calculated // and the status bar only will be calculated. //p.rect0.Bottom -= _rectStatus.Height; _rectClient.X = 0; _rectClient.Width = p.rect0.Right - (p.rect0.Left + 1); _rectClient.Height = p.rect0.Bottom - (p.rect0.Top + 1); } else { // Deflate the left, right, and bottom of the rect0 by the left border width, // right border width, and sum of the status and bottom border height. p.rect0.Left += _rectBorderLeft.Width; p.rect0.Right -= _rectBorderRight.Width; p.rect0.Bottom -= _rectBorderBottom.Height; _rectClient.X = _rectBorderLeft.Width + 1; _rectClient.Width = p.rect0.Right - (p.rect0.Left + 2); _rectClient.Height = p.rect0.Bottom - (p.rect0.Top + 2); } return p; }
nonClientHitTest函数是告诉系统鼠标指针指向了非客户区的哪一部分。传递给此函数的Point p参数相对于已蒙皮表单的左上角。此函数的结果必须是hit-test结果常量之一,这些常量前缀为HT。在我的实现中,当鼠标指针指向最小化、最大化或关闭按钮时,我返回HTOBJECT,而不是返回HTMINBUTTON、HTMAXBUTTON或HTCLOSE,因为我宁愿使用自己的工具提示,而不是系统工具提示:D。 隐藏,收缩,复制Code
protected internal override int nonClientHitTest(Point p) { if (_rectClient.Contains(p)) return Win32API.HTCLIENT; if (_rectIcon.Contains(p)) return Win32API.HTMENU; // Always return HTOBJECT instead of the corresponding hittest value, // to prevent the default tooltip to be shown. if (_minimizeButton.Enabled && _minimizeButton.Visible) { if (_minHost.Bounds.Contains(p)) return Win32API.HTOBJECT; } if (_maximizeButton.Enabled && _maximizeButton.Visible) { if (_maxHost.Bounds.Contains(p)) return Win32API.HTOBJECT; } if (_closeButton.Enabled && _closeButton.Visible) { if (_closeHost.Bounds.Contains(p)) return Win32API.HTOBJECT; } // Test for custom bar button, if any of them, then return the HTOBJECT if (Form.FormBorderStyle == FormBorderStyle.Sizable || Form.FormBorderStyle == FormBorderStyle.SizableToolWindow && Form.WindowState != FormWindowState.Maximized) { // Test for borders. // Corners if (_rectBorderTopLeft.Contains(p)) return Win32API.HTTOPLEFT; if (_rectBorderTopRight.Contains(p)) return Win32API.HTTOPRIGHT; if (_rectBorderBottomLeft.Contains(p)) return Win32API.HTBOTTOMLEFT; if (_rectBorderBottomRight.Contains(p)) return Win32API.HTBOTTOMRIGHT; // vertical and horizontal if (_rectBorderTop.Contains(p)) return Win32API.HTTOP; if (_rectBorderLeft.Contains(p)) return Win32API.HTLEFT; if (_rectBorderRight.Contains(p)) return Win32API.HTRIGHT; if (_rectBorderBottom.Contains(p)) return Win32API.HTBOTTOM; } if (Form.WindowState != FormWindowState.Maximized) { // Test for bar form. if (_rectBar.Contains(p)) return Win32API.HTCAPTION; } // Default return value. return Win32API.HTNOWHERE; }
历史 2012年7月4日:初版 本文转载于:http://www.diyabc.com/frontweb/news12034.html