版权所有,转载请注明出处:http://guangboo.org/2013/02/04/setwindowrgn-application
SetWindowRgn函数用于设置窗体的绘制区域,该区域决定了窗口的形状,并且超出该区域的一切绘制工作都无效的,不会显示在屏幕上。
函数签名
int SetWindowRgn(HWND hWnd, HRGN hRgn, BOOL bRedraw);
其中参数hWnd是要设置区域的窗口句柄;hRgn是区域句柄,该值可以是NULL;bRedraw表示设置完窗口区域后是否重新绘制窗口,True,表示重新绘制,否则不绘,如果窗口可见,bRedraw一般设为True。返回值为非零时表示设置成功,否则失败。
需要注意的是,这里的区域坐标是相对于窗口的左上角而言的,而不是窗口的工作区。一旦方法调用成功,那么系统就获取了这片区域的句柄,并可以在这片区域进行绘制。
C#函数声明
该函数定义在User32.dll中(参考http://msdn.microsoft.com/zh-cn/library/dd145102(v=VS.85).aspx),因此C#可以通过平台调用来引用该win32api,如下声明:
[DllImport("user32.dll")] private static extern int SetWindowRgn(IntPtr hWnd, IntPtr hRgn, bool bRedraw);
其中hWnd可以在Form中使用this.Handle获取,而hRgn则没有类似的定义,也没有对应的结构体。但是C#中有Region类(System.Drawing命名空间),并且该类提供了GetHrgn方法,用来获取Region类的句柄。并且如果你要绘制圆角矩形的窗体,还可以调用CreateRoundRectRgn方法来获取圆角矩形的区域句柄,该方法的在gdi32.dll中定义(参考http://msdn.microsoft.com/zh-cn/library/dd183516.aspx)。声明如下:
[DllImport("gdi32.dll")] private static extern IntPtr CreateRoundRectRgn(int x1, int y1, int x2, int y2, int cx, int cy);
参数x1,y1表示圆角矩形左上角的坐标,x2,y2表示矩形的宽和高,cx, cy表示圆角所占椭圆的宽和高。如果调用成功,返回值为圆角矩形的区域句柄,否则为NULL。
截图应用
有了SetWindowRgn函数,我们就可以实现对窗口“挖洞”了。类似于QQ的截图功能,它的效果是,整个屏幕都变黑底半透明,然则在屏幕上选择一款矩形区域,并且选择的矩形区域是没有黑底半透明修饰的。那么这样的效果就像对一个全屏的,背景是黑色半透明的窗体,选择一块矩形区域,并将选择的矩形区域“挖空”一样。现在有了SetWindowRgn函数,我们就可以实现“挖空”效果了。一段测试代码,用来绘制“中空”的窗体,只有重写OnPaint方法或在WnProc方法中拦截WM_PAINT消息:
protected override void OnPaint(PaintEventArgs e) { System.Drawing.Region region = new System.Drawing.Region(); region.Union(this.Bounds); region.Exclude(new Rectangle(40, 40, 100, 100)); SetWindowRgn(this.Handle, region.GetHrgn(e.Graphics), true); base.OnPaint(e); }
或者:
protected override void WndProc(ref Message m) { switch (m.Msg) { case WM_PAINT: System.Drawing.Region region = new System.Drawing.Region(); region.Union(this.Bounds); region.Exclude(new Rectangle(40, 40, 100, 100)); IntPtr dc = GetWindowDC(m.HWnd); Graphics g = Graphics.FromHdc(dc); SetWindowRgn(m.HWnd, region.GetHrgn(g), true); break; } base.WndProc(ref m); }
两个代码的效果是一样的,都在相对窗体的(40,40)的位置开始“挖”去一个长宽是100X100的矩形,运行效果如图:
“挖空”的效果已经实现,接着就是将窗体全屏,设置黑色背景,半透明化;然后将“挖空”的矩形改成屏幕选择的区域。这些代码可以参加《C#实现屏幕截图》,这里不再贴出代码。
不过有一点需要说明,在选择区域时,应该调用Refresh方法,来刷新界面,否则是不会发出WM_PAINT消息的,也就没有了OnPaint事件了。
绘制多边形窗体
最常用的窗体样式就是圆角窗体了,如果使用SetWindowRgn方法就变的异常简单,只需要简单几行代码即可,如下:
const int WM_PAINT = 0x000F; const int WM_SIZE = 0x0005; protected override void WndProc(ref Message m) { switch (m.Msg) { case WM_PAINT: case WM_SIZE: SetWindowRgn(m.HWnd, CreateRoundRectRgn(0, 0, this.Width, this.Height, 20, 20), true); break; } base.WndProc(ref m); }
这里用到了CreateRoundRectRgn函数,用于创建圆角区域,当然你也可以使用Region类来自己手动创建圆角区域。运行效果如下图:
这是在Window Classic主题下的效果。当然,如果希望Window XP主题下(本身就是圆角的窗体主题)使窗体没有圆角效果,只需要将CreateRoundRectRgn(0, 0, this.Width, this.Height, 20, 20)代码修改成CreateRoundRectRgn(0, 0, this.Width, this.Height, 0, 0)即可,运行效果如图:
但这里的示例仅仅用来演示修改窗体形状原理的,一般在实现一个窗体效果时,还需要很多的修饰,具体可以参考《C#重绘Windows窗体标题栏和边框》。上面主要演示了最常用的圆角或实角矩形窗体效果,当然有了Region类和 SetWindowRgn函数,我们可以实现更加丰富的窗体效果,只是这些效果不是很常用。