文档库

最新最全的文档下载
当前位置:文档库 > C#中窗体Form的美化窗体圆角的处理

C#中窗体Form的美化窗体圆角的处理

C#中窗体Form的美化VS足够强大,强大到只需动动鼠标就可以写出个基本的界面出来,但是其自带的控件都是千篇一律的样式,对于追求完美的我而言,实在是忍不下去了,只好自己亲自动手对其进行改造----继承已有的控件,再对其相关的消息或事件进行处理。窗体Form作为界面的主体部分,必先对其进行美化,在窗体自绘的过程中,需要使用到GDI+,如若对GDI+不是很了解的同学可移步我的CSDN博客或者搜索下相关的介绍。

这篇文章将要介绍到的内容:

?窗体圆角的处理

?无边框窗体大小的改变与移动

?窗体边框的绘制与边框阴影的实现

?系统按钮的绘制与事件处理

?窗体标题栏的绘制

?解决窗体闪烁的问题

实现效果演示:

C#中窗体Form的美化窗体圆角的处理

C#中窗体Form的美化窗体圆角的处理

代码下载

一:窗体圆角的处理

对于无边框窗体圆角矩形的处理,我这里采用的是使用API函数CreateRoundRectRgn,相比于自己用GDI+写的处理圆角的函数,效果要稍微好点,至少线条在圆角处过渡的比较平滑,为了便于复用,我把其封装到窗体自绘辅助类RenderHlper的SetFormRoundRectRgn函数中:

View Code ///

///设置窗体的圆角矩形

///

///需要设置的窗体

///圆角矩形的半径

public static void SetFormRoundRectRgn(Form form, int rgnRadius)

{

int hRgn = 0;

hRgn = Win32.CreateRoundRectRgn(0, 0, form.Width + 1, form.Height + 1, rgnRadius, rgnRadius);

Win32.SetWindowRgn(form.Handle, hRgn, true);

Win32.DeleteObject(hRgn);

}

此处需要把所需要的API函数引用到类Win32中,引用的时候注意添加System.Runtime.InteropServices 命名空间:

View Code [DllImport("gdi32.dll")]

public static extern int CreateRoundRectRgn(int x1, int y1, int x2, int y2, int x3, int y3);

[DllImport("user32.dll")]

public static extern int SetWindowRgn(IntPtr hwnd, int hRgn, Boolean bRedraw);

[DllImport("gdi32.dll", EntryPoint = "DeleteObject", CharSet = CharSet.Ansi)]

public static extern int DeleteObject(int hObject);

重写窗体的OnSizeChanged事件,并在此事件中调用SetFormRoundRectRgn,此处的Radius参数为定义的窗体圆角半径属性:

View Code protected override void OnSizeChanged(EventArgs e)

{

base.OnSizeChanged(e);

RenderHelper.SetFormRoundRectRgn(this, Radius);

}

二:无边框窗体大小的改变与移动

当把窗体的FormBorderStyle属性调整为FormBorderStyle.None时,此时,窗体的大小改变不了,同时窗体不能移动。要想实现无边框窗体大小的改变与移动,可采用如下方法:

(1)重写窗体的过程WndProc:

主要是对WM_NCHITTEST消息进行处理,根据事件的发生位置来进行不同方向箭头的调整,窗体大小改变与移动的函数:

View Code //调整窗体大小

private void WmNcHitTest(ref Message m)

{

int wparam = m.LParam.ToInt32();

Point mouseLocation = new

Point(RenderHelper.LOWORD(wparam),RenderHelper.HIWORD(wparam));

mouseLocation = PointToClient(mouseLocation);

if (WindowState != FormWindowState.Maximized )

{

if (CanResize == true)

{

if (mouseLocation.X < 5 && mouseLocation.Y < 5)

{

m.Result = new IntPtr(Win32.HTTOPLEFT);

return;

}

if (mouseLocation.X > Width - 5 && mouseLocation.Y < 5)

{

m.Result = new IntPtr(Win32.HTTOPRIGHT);

return;

}

if (mouseLocation.X < 5 && mouseLocation.Y > Height - 5)

{

m.Result = new IntPtr(Win32.HTBOTTOMLEFT);

return;

}

if (mouseLocation.X > Width - 5 && mouseLocation.Y > Height - 5)

{

m.Result = new IntPtr(Win32.HTBOTTOMRIGHT);

return;

}

if (mouseLocation.Y < 3)

{

m.Result = new IntPtr(Win32.HTTOP);

return;

}

if (mouseLocation.Y > Height - 3)

{

m.Result = new IntPtr(Win32.HTBOTTOM);

return;

}

if (mouseLocation.X < 3)

{

m.Result = new IntPtr(Win32.HTLEFT);

return;

}

if (mouseLocation.X > Width - 3)

{

m.Result = new IntPtr(Win32.HTRIGHT);

return;

}

}

}

m.Result = new IntPtr(Win32.HTCAPTION);

}

重写窗体过程:

View Code protected override void WndProc(ref Message m)

{

switch (m.Msg)

{

case Win32.WM_NCHITTEST:

WmNcHitTest(ref m);

break;

default:

base.WndProc(ref m);

break;

}

}

(2)对于仅仅只想实现窗体的移动而不改变窗体的大小,可以重写OnMouseDown事件中发送HTCAPTION

消息来实现无边框窗体的移动,具体的实现代码如下:

View Code ///

///移动窗体

///

public static void MoveWindow(Form form)

{

Win32.ReleaseCapture();

Win32.SendMessage(form.Handle, Win32.WM_NCLBUTTONDOWN, Win32.HTCAPTION, 0);

}

调用窗体移动函数:

View Code protected override void OnMouseDown(MouseEventArgs e)

{

base.OnMouseDown(e);

if (e.Button == MouseButtons.Left)

{

Render.MoveWindow(this);

}

}

三:窗体边框的绘制与边框阴影的实现

边框的绘制:边框的绘制使用用PS制作好的图片来进行贴图操作,在贴图的过程中使用九宫图贴图方法,保证此边框图片能满足任何大小的窗体。

窗体边框的实现:此部分主要涉及到对CS_DropSHADOW的了解,只要在窗口的ClassStyle添加此样式即可,关键代码如下:

View Code protected override CreateParams CreateParams

{

get {

CreateParams cp = base.CreateParams;

if (!DesignMode)

{

cp.ClassStyle |= (int) ClassStyle.CS_DropSHADOW;

}

return cp;

}

}

四:系统按钮的绘制与事件处理

此部分是所有部分中最难的部分,在此部分中既要实现系统按钮不同状态下(鼠标操作改变按钮状态)的绘制,还有对其相应的事件进行处理,所以我创建了2个类:SystemButton类和SystemButtonManager类。SystemButton类表示系统按钮类,而SystemButtonManager的功能是对系统按钮各个状态与事件的管理。类SystemButtonManager的类图如下所示:

C#中窗体Form的美化窗体圆角的处理

属性、方法、事件的功能介绍如下表:

C#中窗体Form的美化窗体圆角的处理

对于类SystemButtonManager,主要是管理三个系统按钮的状态与事件,其他特别要介绍的是定义的系统按钮状态索引器,根据提供的索引来获取或者设置按钮的当前状态。

五:窗体标题栏的绘制

标题栏的绘制主要涉及到窗体Icon图标的绘制与窗体标题的绘制,绘制的过程中定义了2个属性:IconRect,TextRect,分别对应着图标的坐标矩形与窗体标题的坐标矩形,图标与标题的绘制在这个矩形中绘制,需要提醒的时,图标的绘制需要注意到是否窗体的ShowIcon属性。

六:解决窗体闪烁的问题

在窗体的自绘过程中,当调整窗体的大小等操作而触发窗体重绘,此时窗体的闪烁现象更为明显,相信大部分同学在自定义控件的过程中或多或多的出现这种问题,对于此问题,每个人又不同的解决方法,这里我提供四种解决方案类解决窗体的闪烁:

方法一:第一个容易想到的是采用双缓冲机制来进行图形的绘制,对双缓冲不了解的同学可以参考下我的另外一篇文章《浅谈C#中是双缓冲》。

方法二:当将CS_DropSHADOW样式添加到窗体的ClassStyle样式中可以明显的解决窗体闪烁的现象。代码见本文的第三部分--窗体边框的绘制与边框阴影的实现。

方法三:当窗体进行重绘时,对WM_ERASEBKGND消息进行忽略,应用代码如下:View Code protected override void WndProc(ref Message m)

{

switch (m.Msg)

{

case Win32.WM_ERASEBKGND:

m.Result = IntPtr.Zero;

break;

default:

base.WndProc(ref m);

break;

}

}

方法四:将WS_CLIPCHILDREN样式添加到窗体的ExStyle样式中,此方法对解决窗体挂有很多子控件时窗体闪烁的现象特别明显,应用代码如下:

View Code protected override CreateParams CreateParams

{

get {

CreateParams cp = base.CreateParams;

if (!DesignMode)

{

cp.ExStyle |= (int)WindowStyle.WS_CLIPCHILDREN;

}

return cp;

}

}