北京那家医院可以治疗白癜风 http://pf.39.net/bdfyy/bdfzj/前言
最近“解决自媒体一键多平台发布”项目立项以来,桌面端选择了WinForm进行开发(虽然丢了很多年,但总算多少了解点)。
为了重绘标题栏、最大最小化按钮和关闭按钮,需要对WinForm的边框进行重绘。在网上搜了很多资料,都是将FormBorderStyle设置为none。虽然这也能实现无边框化,但存在一个很大的问题,就是窗体靠边后,无法实现系统自带的分屏功能。为了解决这一个问题,在搜索查阅了大量资料后,终于解决了,现将其分享给各位小伙伴。
一、开发环境
vs
Windows10
二、具体代码
要实现窗口的重绘,我们需要重写系统消息的处理方法。这个时候我们就需要重写Form基类的WndProc()方法,代码如下:
protectedoverridevoidWndProc(refMessagem){//重写消息处理过程}
重写WndProc()方法,首先要判断当前是否为设计模式,防止运行过程的执行结果影响设计窗体,代码如下:
protectedoverridevoidWndProc(refMessagem){if(DesignMode){base.WndProc(refm);return;}}
Windows消息有很多类型,要实现无边框我们需要重写WM_NCCALCSIZE、WM_WINDOWPOSCHANGED、WM_NCACTIVATE三个消息类型处理过程。
首先判断消息类型的具体代码如下:
switch(m.Msg){case(int)0x://WM_NCCALCSIZE:break;case(int)0x://WM_WINDOWPOSCHANGED:break;case(int)0x://WM_NCACTIVATE:break;default:base.WndProc(refm);break;}
其中WM_NCCALCSIZE可以在应用程序可以在窗口的大小或位置发生更改时控制窗口工作区的内容,通过此消息需要将无边框化的窗体大小去掉设计窗口里面窗体的标题和边框宽高度,以及当启动位置设置为屏幕中心时将大小变化带来的位置坐标影响进行修改,具体代码如下:
privatevoidModifyFormSizeAndLcation(){vardiffHeight=this.Height-this.ClientRectangle.Height;vardiffWidth=this.Width-this.ClientRectangle.Width;if(diffHeight!=0
diffWidth!=0){varnewWidth=this.Width-diffWidth;varnewHeight=this.Height-diffHeight;if(this.StartPosition==FormStartPosition.CenterScreen){varscreenWorkingArea=Screen.GetWorkingArea(this);varcenterX=screenWorkingArea.Width/2-newWidth/2;varcenterY=screenWorkingArea.Height/2-newHeight/2;this.Location=newPoint(centerX,centerY);}this.Size=newSize(newWidth,newHeight);}}
其中WM_WINDOWPOSCHANGED消息是用以处理由于窗体边框的修改带来的窗体大小、位置等的更改,现在我们需要拦截这个消息重绘控件布局,具体代码如下:
//向默认窗口过程发送指定消息。DefWndProc(refm);//用当前大小和位置更新控件的边界。UpdateBounds();//获取窗口的位置varpos=(WINDOWPOS)Marshal.PtrToStructure(m.LParam,typeof(WINDOWPOS));//设置窗口的区域staticvoidSetWindowRegion(Formform,IntPtrhwnd,intleft,inttop,intright,intbottom){varrgn=User32.CreateRectRgn(0,0,0,0);varhrg=newHandleRef((object)form,rgn);User32.GetWindowRgn(hwnd,hrg.Handle);RECTbox;User32.GetRgnBox(hrg.Handle,outbox);if(box.left!=left
box.top!=top
box.right!=right
box.bottom!=bottom){varhr=newHandleRef((object)form,User32.CreateRectRgn(left,top,right,bottom));User32.SetWindowRgn(hwnd,hr.Handle,User32.IsWindowVisible(hwnd));}User32.DeleteObject(rgn);}m.Result=newIntPtr(1);
其中WM_NCACTIVATE是在需要更改其非client区域以指示活动或非活动状态时,发送到窗口。拦截进行重写,防止如文件选择框等无法获取焦点,具体代码如下:
privatevoidWmNCActivate(refMessagemsg){if((User32.GetWindowLong(this.Handle,-16)(int)0x)0){DefWndProc(refmsg);}else{msg.Result=newIntPtr(1);}}
三、完整代码
1、FormFrameless.cs
publicclassFormBorderless:Form{protectedoverridevoidWndProc(refMessagem){if(DesignMode){base.WndProc(refm);return;}switch(m.Msg){case(int)0x://WM_NCCALCSIZE:ModifyFormSizeAndLcation();break;case(int)0x://WM_WINDOWPOSCHANGED:{DefWndProc(refm);UpdateBounds();varpos=(WINDOWPOS)Marshal.PtrToStructure(m.LParam,typeof(WINDOWPOS));SetWindowRegion(this,m.HWnd,0,0,pos.cx,pos.cy);m.Result=newIntPtr(1);break;}case(int)0x://WM_NCACTIVATE:{WmNCActivate(refm);break;}default:{base.WndProc(refm);break;}}}///summary///将窗体的大小去掉原始标题和边框尺寸///同时当启动位置设置为屏幕中心时,对坐标进行校准////summaryprivatevoidModifyFormSizeAndLcation(){vardiffHeight=this.Height-this.ClientRectangle.Height;vardiffWidth=this.Width-this.ClientRectangle.Width;if(diffHeight!=0
diffWidth!=0){varnewWidth=this.Width-diffWidth;varnewHeight=this.Height-diffHeight;if(this.StartPosition==FormStartPosition.CenterScreen){varscreenWorkingArea=Screen.GetWorkingArea(this);varcenterX=screenWorkingArea.Width/2-newWidth/2;varcenterY=screenWorkingArea.Height/2-newHeight/2;this.Location=newPoint(centerX,centerY);}this.Size=newSize(newWidth,newHeight);}}privatevoidWmNCActivate(refMessagemsg){if((User32.GetWindowLong(this.Handle,-16)(int)0x)0){DefWndProc(refmsg);}else{msg.Result=newIntPtr(1);}}staticvoidSetWindowRegion(Formform,IntPtrhwnd,intleft,inttop,intright,intbottom){varrgn=User32.CreateRectRgn(0,0,0,0);varhrg=newHandleRef((object)form,rgn);User32.GetWindowRgn(hwnd,hrg.Handle);RECTbox;User32.GetRgnBox(hrg.Handle,outbox);if(box.left!=left
box.top!=top
box.right!=right
box.bottom!=bottom){varhr=newHandleRef((object)form,User32.CreateRectRgn(left,top,right,bottom));User32.SetWindowRgn(hwnd,hr.Handle,User32.IsWindowVisible(hwnd));}User32.DeleteObject(rgn);}}
2、调用非托管代码的存放类User32.cs
classUser32{[DllImport("user32.dll")]publicstaticexternInt32GetWindowLong(IntPtrhWnd,Int32Offset);[DllImport("gdi32.dll")]publicstaticexternIntPtrCreateRectRgn(intnLeftRect,intnTopRectintnRightRect,intnBottomRect);[DllImport("user32.dll")]publicstaticexternintGetWindowRgn(IntPtrhWnd,IntPtrhRgn);[DllImport("gdi32.dll")]publicstaticexternintGetRgnBox(IntPtrhrgn,outRECTlprc);[DllImport("user32.dll")]publicstaticexternintSetWindowRgn(IntPtrhWnd,IntPtrhRgn,boolbRedraw);[DllImport("user32.dll")][return:MarshalAs(UnmanagedType.Bool)]publicstaticexternboolIsWindowVisible(IntPtrhWnd);[DllImport("gdi32.dll")]publicstaticexternboolDeleteObject(IntPtrhObj);}
3、WINDOWPOS.cs
[StructLayout(LayoutKind.Sequential)]internalstructWINDOWPOS{internalIntPtrhwnd;internalIntPtrhWndInsertAfter;internalintx;internalinty;internalintcx;internalintcy;internaluintflags;}
4、RECT.cs
[StructLayout(LayoutKind.Sequential)]publicstructRECT{publicintleft;publicinttop;publicintright;publicintbottom;}
四、最后
各位小伙伴如果有更好的分屏方案,可以告诉我哦~