好久没有来了,今天终于有时间了,可以来写写关于控件开发的文章了。 今天我以我4年前写的一个小控件为例写一下如何开发控件的方法。 这个控件实现后的效果可见附件1。 首先我们继承自TCustomControl一个类TKindSpinEdit。因为类TCustomControl中有Canvas属性,利用这个属性我们可以来绘制我们需要的图像。
TKindSpinEdit = class(TCustomControl) private ..... protected { Protected declarations } procedure Change; dynamic; public { Public declarations } procedure Paint;override; constructor Create (AOwner: TComponent); override; destructor Destroy; override; published property MaxValue:Integer read FMaxValue write SetMaxValue; property MinValue:Integer read FMinValue write SetMinValue; property Value:Integer read FValue write SetValue; property BorderColor:TCOlor read FBorderColor write SetBorderColor; property DrawColor:TColor read FDrawColor write SetDrawColor; property Color; property Font:TFont read FFont write SetFont; property ClickColor:TColor read FClickColor write SetClickColor; property Step:Integer read FStep write SetStep; property WayStyle:TWayStyle read FWayStyle write SetWayStyle; property Width; property Height; property OnChange: TNotifyEvent read FOnChange write FOnChange; property OnClick; property OnContextPopup; property OnDblClick; property OnDragDrop; property OnDockDrop; property OnDockOver; property OnDragOver; property OnEndDock; property OnEndDrag; property OnEnter; property OnExit; property OnGetSiteInfo; property OnMouseDown; property OnMouseMove; property OnMouseUp; property OnStartDock; property OnStartDrag; property OnUnDock; end;
不要被published中的属性给吓住,其实里面很多都是继承自TCustomControl类的。只有少量的属性会对我们编写这个控件产生影响,所以才自己定义,例如: property MaxValue; property MinValue; 这两个属性就是用来设置最大值和最小值的。 property Value;属性是用来记录当前的值的。 property BorderColor;属性是用来确定绘制边框的时候使用的颜色的。 property DrawColor;属性是用来确定填充“-”和“+”内容是使用的颜色的。 property ClickColor:属性是用来确定您点击的时候绘制“-”和“+”的内容颜色。
下来我们要截获两个消息,一个是鼠标点击消息一个是鼠标离开消息,及CM_MOUSEENTER消息和CM_MOUSELEAVE消息。如果有这两个消息的时候我们则对我们的控件 进行重新绘制。因为TCustomControl类已经实现了绘制函数,所以我们需要重载一下procedure Paint;override;在重载的Paint函数中我们需要绘制这个控件 的边框,左区域,右区域和中心区域。一下是Paint函数的内容: procedure TKindSpinEdit.Paint; var R:TRect; begin R:=ClientRect; Frame3D(Canvas,R,FBorderColor,FBorderColor,1); //绘制边框 DrawBorder; //绘制左框 DrawLeft; //绘制右框 DrawRight; //绘制中间 DrawCenter; inherited; end;
函数DrawBorder;是用来绘制边框的,具体的实现是: procedure TKindSpinEdit.DrawCenter; var TextW,TextH:Integer; Cap:String; R:TRect; begin with Canvas do begin //绘制横向中间边框 Brush.Style:=bsClear; R:=ClientRect; //设置绘制的区域 Brush.Color:=Color; //设置绘制的颜色 FillRect(EditRect); //向绘制区域中绘制颜色 Cap:=IntToStr(FValue); //计算当前值应该书写的位置 TextW:=((EditRect.Right-EditRect.Left-Canvas.TextWidth(Cap)) div 2)+EditRect.Left; TextH:=((EditRect.Bottom-EditRect.Top-Canvas.TextHeight(Cap)) div 2)+EditRect.Top; //将当前值写入相应的位置 TextOut(TextW,TextH,Cap); end;
end;
函数DrawLeft是用来绘制左区域,具体的实现是: procedure TKindSpinEdit.DrawLeft; var OldWidth:Integer; X,Y,X1,Y1:Integer; begin {首先根据不同的显示效果得到左区域,右区域和中心书写当前值的区域} case FWayStyle of twsHorizontal: begin LeftR:=ClientRect; LeftR.Right:=LeftR.Left+17; RightR:=ClientRect; RightR.Left:=RightR.Right-17; EditRect.Top:=ClientRect.Top+1; EditRect.Bottom:=ClientRect.Bottom-1; EditRect.Left:=LeftR.Right+1; EditRect.Right:=RightR.Left-1; end; twsVertical: begin RightR:=ClientRect; RightR.Bottom:=RightR.Top+17; LeftR:=ClientRect; LeftR.Top:=LeftR.Bottom-17; EditRect.Top:=RightR.Bottom+1; EditRect.Bottom:=LeftR.Top-1; EditRect.Left:=LeftR.Left+1; EditRect.Right:=LeftR.Right-1; end; end; with Canvas do begin {根据当前状态(是鼠标点击下去还是鼠标放开)来绘制} if FFlag then begin //绘制点击时的状态 Brush.Color:=FDrawColor; //设置画刷颜色 FillRect(LeftR); //填充左区域 Pen.Color:=FClickColor; //设置画笔颜色 Pen.Width:=1; //设置画笔宽度 Rectangle(LeftR); //用画笔绘制边框 Pen.Color:=FClickColor; //设置点击后的画笔颜色 Pen.Width:=1; X:= LeftR.Left+4; X1:=LeftR.Right-4; Y:=((LeftR.Bottom-LeftR.Top) div 2)+1+LeftR.Top; Y1:=((LeftR.Bottom-LeftR.Top) div 2)+1+LeftR.Top; {绘制减号} Moveto(((LeftR.Right-LeftR.Left-8) div 2)+LeftR.Left ,((LeftR.Bottom-LeftR.Top) div 2)+1+LeftR.Top); LineTo(((LeftR.Right-LeftR.Left-8) div 2)+LeftR.Left+8,((LeftR.Bottom-LeftR.Top) div 2)+1+LeftR.Top); end else begin //绘制非点击时的状态 Brush.Color:=FDrawColor; FillRect(LeftR); Pen.Color:=FBorderColor; Pen.Width:=1; Rectangle(LeftR); Pen.Color:=FBorderColor; Pen.Width:=1; Moveto(((LeftR.Right-LeftR.Left-8) div 2)+LeftR.Left,((LeftR.Bottom-LeftR.Top) div 2)+LeftR.Top); LineTo(((LeftR.Right-LeftR.Left-8) div 2)+LeftR.Left+8,((LeftR.Bottom-LeftR.Top) div 2)+LeftR.Top); end;
end;
end;
剩余的绘制右区域和绘制中间区域的仿佛类似。 在我的代码中我定义了一个变量FFlag,这个变量是用来标志鼠标是否按下的。当鼠标按下和鼠标放开的时候绘制的仿佛是不同的。
变量FWayStyle是用来定义这个控件的显示方式。twsHorizontal是横向显示,twsVertical是纵向显示。
现在这个控件已经绘制成功了,可是当点击“-”或者“+”的时候怎么样让值变化呢? 这需要对鼠标点击时间和鼠标放开事件进行处理 procedure TKindSpinEdit.MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); begin if Button=mbLeft then //判断是否是左键点击 begin I:=0; FFlag:=true; FTimer.Enabled:=true; if(x>=LeftR.Left) and (x<=LeftR.Right) and (y>=LeftR.Top) and (y<=LeftR.Bottom) then //判断是否点击的是左区域 begin FClick:=1; MathValue; DrawCenter; DrawLeft; FTimer.OnTimer:=OnTimer; end; if(x>=RightR.Left) and (x<=RightR.Right) and (y>=RightR.Top) and (y<=RightR.Bottom) then //判断是否点击的是右区域 begin FClick:=2; MathValue; DrawCenter; DrawRight; FTimer.OnTimer:=OnTimer; end; end; inherited; end;
同时我在这个控件中定义了一个始终FTimer。用来处理用户一直点击鼠标的情况。
下面是我写的完整代码。如果还有不清楚的地方,可以给我留言!