需求:
QQ我的资料中基本资料窗体中的文本框:
正常状态下,文本框只有一条看起来只有一个像素的边框,边框的颜色从上到下由深到浅的渐变,当鼠标定位到该文本框时,其边框会变粗,而且边框的颜色加亮显示
如下图所示:
实现思路: 一、准备两个边框素材图片,
一个是正常状态下的边框素材,
比如:
另一个是鼠标进入到文本框内的边框素材
比如:
二、需要的参数
首先是边框素材的绘制边距,分为左边距,右边距,上边距,下边距
边框素材根据边距的设置,使用九宫格缩放绘制到界面上
其次是边框的边距,也为左边距,右边距,上边距,下边框
代表的是文本框客户区(即输入区)的大小
默认的,边框素材的绘制边距和边框的边距是一样的
三、消息处理
边框属于文本框的非客户区域,在文本框的WM_NCPAINT中绘制
文本框的边框风格有两种:bsNone和bsSingle
bsNone即为无边框样式,不需要绘制边框,而且文本框的大小就是客户区的大小
bsSingle为单边框样式,默认边框为两个像素的宽
如果需要自定义文本框的边框宽度,那么需要处理WM_NCCALCSIZE消息
通过WM_NCCALCSIZE消息,使边框扩展为自己所设置的宽度
例(边框扩展一个像表):
- procedure TSkinNormalEdit.WMNCCalcSize(varMessage: TWMNCCalcSize);
- var
- NCCalcSizeParams: PNCCalcSizeParams;
- begin
- Inherited;
- if(BorderStyle=bsNone) then
- begin
- end
- else
- begin
- NCCalcSizeParams:=Message.CalcSize_Params;
- Inc(NCCalcSizeParams.rgrc0.Top,1);
- Inc(NCCalcSizeParams.rgrc0.Left,1);
- Dec(NCCalcSizeParams.rgrc0.Right,1);
- Dec(NCCalcSizeParams.rgrc0.Bottom,1);
- end;
- end;
四、边框绘制时机
边框有两种状态,鼠标进入到文本框中和鼠标离开文本框
所以,要判断这两种状态
像一般的从TCustomControl或TGraphicControl继承过来的控件,
我们可以通过Delphi内部的管理消息CM_MOUSEENTER和CM_MOUSELEAVE消息处理
但是文本框包含非客户区(边框)和客户区
CM_MOUSEENTER和CM_MOUSELEAVE消息只是鼠标进入或是离开客户区才会响应
所以接下来要处理如何判断鼠标在非客户区中
非客户区的鼠标移动消息主要有三个
WM_NCMOUSEMOVE:非客户区鼠标移动
WM_NCHITTEST:非客户区鼠标移动在控件的哪个部位(标题栏?边框?边角?客户区等等)
WM_NCMOUSELEAVE:鼠标离开非客户区(但是文本框不触发这个消息)
综上,没有一个消息可以简单的标识鼠标是否在控件中
CM_MOUSEENTER可以判断鼠标进入控件的客户区
WM_NCHITTEST可以判断鼠标在控件的非客户区
CM_MOUSELEAVE不能判断鼠标离开控件的客户区
所以,用一个定时器加一个判断鼠标在客户区的过程组合
WM_NCHITTEST消息中,判断当鼠标进入第一次非客户区时,响应鼠标进入消息,重绘边框,设置定时器,判断鼠标是否会在100毫秒内离开文本框
CM_MOUSEENTER消息中,鼠标已经进入客户区了,重绘边框
CM_MOUSELEAVE 消息中,只是表明了鼠标离开客户区,所以要启动定时器,每100毫秒判断鼠标是否在文本框中,如果检测到鼠标离开文本框,那么需要响应鼠标离开,重绘边框
五、边框绘制
边框为非客户区,在文本框的WM_NCPAINT消息中绘制
比如:
procedure TSkinNormalEdit.WMNCPaint(varMessage: TWMNCPaint); var tmpWindowDC:HDC; tmpBorderImage:IGPBitmap; tmpWindowCanvas:TCanvas; tmpBitmapGraphics:IGPGraphics; begin ifSelf.BorderStyle=bsNone then begin Inherited; end else begin tmpWindowDC:=GetWindowDC(Handle); Try if tmpWindowDC<>0 then begin tmpWindowCanvas:=TCanvas.Create; Try tmpWindowCanvas.Handle:=tmpWindowDC; FParentBackGroundBitmap.SetSize(Width,Height); if FIsBroderTransparent then begin //绘制文本框背景 DrawParentImageDefault(Self,FParentBackGroundBitmap.Canvas.Handle); end; //边框为png素材,使用GDI+绘制,需要获取绘制接口,需要引用gdiplus和gdiplushelpers单元 tmpBitmapGraphics:=FParentBackGroundBitmap.Canvas.ToGPGraphics; //根据鼠标状态判断边框图片 if Self.MouseInClient or CursorInControl then begin tmpBorderImage:=Self.FHoverBorderBitmap; end else begin tmpBorderImage:=Self.FNormalBorderBitmap; end; //再绘制背景 if tmpBorderImage<>nil then begin //九宫格绘制边框素材 TSkinHelper.StretchDrawImageBorderInRectByMargins(tmpBitmapGraphics, tmpBorderImage,TGPRect.Create(0,0,Width,Height), Self.FBorderDrawMargins.Left, Self.FBorderDrawMargins.Top, Self.FBorderDrawMargins.Right, Self.FBorderDrawMargins.Bottom); end; //绘制最外的边框 //左边框 BitBlt( tmpWindowDC,0,0,Self.FBorderMargins.Left,Height, FParentBackGroundBitmap.Canvas.Handle,0,0,SRCCOPY ); //右边框 BitBlt( tmpWindowDC,Width-Self.FBorderMargins.Right,0,Self.FBorderMargins.Right,Height, FParentBackGroundBitmap.Canvas.Handle,Width-Self.FBorderMargins.Right,0,SRCCOPY ); //上边框 BitBlt( tmpWindowDC,0,0,Width,Self.FBorderMargins.Top, FParentBackGroundBitmap.Canvas.Handle,0,0,SRCCOPY ); //下边框 BitBlt( tmpWindowDC,0,Height-Self.FBorderMargins.Bottom,Width,Self.FBorderMargins.Bottom, FParentBackGroundBitmap.Canvas.Handle,0,Height-Self.FBorderMargins.Bottom,SRCCOPY );
Finally FreeAndNil(tmpWindowCanvas); End; end; Finally ReleaseDC(Handle,tmpWindowDC); End; end; end;
|
请发表评论