WEB开发网
开发学院软件开发Delphi 组件制作之四(定制外观) 阅读

组件制作之四(定制外观)

 2006-02-04 13:36:35 来源:WEB开发网   
核心提示:时常想,如果一个组件能够按自己想要的外观显示,那该是件多么COOL的事啊,这一篇就要来做一个精美外观的组件,但是,做什么好呢.Button? 高手突破>有关于自己定义外观的Button,以及CheckBox等的做法,Button从CustomPanel继承,重载Paint方法来画外观.如果你有兴趣,可以去找来看,
时常想,如果一个组件能够按自己想要的外观显示,那该是件多么COOL的事啊,这一篇就要来做一个精美外观的组件,但是,做什么好呢.Button? 高手突破>有关于自己定义外观的Button,以及CheckBox等的做法,Button从CustomPanel继承,重载Paint方法来画外观.如果你有兴趣,可以去找来看,这里就不做Button了,做一个Memo如何呢.?是个不错的主意。   我们先起个名字叫做TCoolMemo。以上篇已经讲了很多组件的技术,这里就只说出几个重点。其余不多说了。   首先,该Memo从CustomMemo继承,它有这样外观:属于平面的,边框是可以设置颜色的线,对应的颜色变量为FEdgeColor,另外,离边框以内的两个象素处,还有另一个框,当鼠标进入Memo时,这个框会显示,当鼠标离开时,为个框消失,同样也可以设置颜色,对应变量为FEnterColor。 那么鼠标进入和离开怎么判断呢,这里Memo将截获两个Delphi的内部消息: //下面两个获得Delphi的内部消息,鼠标进入和离开时发生    PRocedure CMMouseEnter (var Message: TMessage); message CM_MOUSEENTER;    procedure CMMouseLeave (var Message: TMessage); message CM_MOUSELEAVE; 其实父类已经截获了这两个消息,并作了相应处理,所以TCoolMemo中的消息处理函数要 Inherited;再作自己的处理。这里又用到了一个变量 MouseIn:Boolean;//标识鼠标是否进入组件   接下来TCoolMemo还要截获两个消息: procedure WMPaint (var Message: TMessage); message WM_PAINT; procedure WMNCCalcSize (var Message: TWMNCCalcSize); message WM_NCCALCSIZE; 第一个很熟悉,当需要重画时,触发该消息, 第二个是当窗体需要计算位置和尺寸时触发,消息中包含了窗口客户区的大小,我们用这个的目的主要是将客户区缩小三个象素,以便画组件时不会画到客户区。 procedure TCoolMemo.WMNCCalcSize (var Message: TWMNCCalcSize); begin   inherited;   InflateRect(Message.CalcSize_Params^.rgrc[0], -3, -3); end;   而上面几个消息处理函数,CM_MOUSEENTER和CM_MOUSELEAVE;将引起TCoolMemo的外观变化,WM_PAINT保存其外观不被擦去。所以要用到一个画组件的函数,即: drawBorder; 里面用到了几个API的GDI函数。我在代码中有详细的说明,加上自己看帮助,应该是可以看懂的。   另外,相比于Memo,它的扩展了这样的功能:设置边距和获得光标的位置。这两个对应的性属为Margin,Position。他们都是Public的,不可以在对象察看器中看到。 我们一个个来说 边距设置 property Margin:byte read FMargin write setMargin default 0; 其中setMargin函数中发送了两个消息: //该消息取得输入区的尺寸 SendMessage(Handle, EM_GETRECT, 0, Longint(@Rect)); //该消息设定输入区的大小 SendMessage(Handle, EM_SETRECT, 0, Longint(@Rect));   光标的位置: property Position:TPosition read getPosition; TPostion是一个结构,其中有行和列两个值: TPosition=record  //指定光标的行和列    row:longint;    col:longint;   end; getPosition;中还要处理中文的问题,代码有详细说明,如果文本中有中文,一样也可以得到正确的行和列。   最后增加了两个事件 property OnEnter; property OnExit; 都是从父类中显化出来的,其实就是CM_MOUSEENTER和CM_MOUSELEAVE;消息引起的。,当你想作一个三态按钮,这两个事件很有作用。   好了,重点就是上面那几个了,以下是源代码,其中也有详细的说明:   unit CoolMemo;   interface   uses   Windows, Messages, Classes, Forms,Controls, Graphics, StdCtrls;   type    //用设定边缘的空白   TPosition=record  //指定光标的行和列    row:longint;    col:longint;   end;   TCoolMemo=class(TCustomMemo)   private    FMargin:byte;  //边距的大小    FEdgeColor:TColor;//边框的颜色    FEnterColor:TColor;//鼠标进入时边框内侧的框颜色    MouseIn: Boolean; //标识鼠标是否进入    function getPosition:TPosition;//光标的行和列    procedure setMargin(value:byte);    procedure setEdgeColor(Value:TColor);    procedure setEnterColor(Value:TColor);    //下面两个获得Delphi的内部消息,鼠标进入和离开时发生    procedure CMMouseEnter (var Message: TMessage); message CM_MOUSEENTER;    procedure CMMouseLeave (var Message: TMessage); message CM_MOUSELEAVE;    //当一个窗口的外观必须被画时,应用程序发送这个消息给该窗口    procedure WMPaint (var Message: TMessage); message WM_PAINT;    //窗体需要计算位置和尺寸时触发    //我们用这个的目的主要是将客户区缩小三个象素,以便画组件时不会画到客户区。   procedure WMNCCalcSize (var Message: TWMNCCalcSize); message WM_NCCALCSIZE;   protected   //画窗体的边框,使其看起来更美观.    procedure drawBorder;   public    constructor Create (AOwner: TComponent); override;    property Position:TPosition read getPosition;    property Margin:byte read FMargin write setMargin default 0;   published    property EdgeColor:TColor read FEdgeColor write SetEdgeColor default $ff0000;    property EnterColor:TColor read FEnterColor write SetEnterColor default $0000ff;    //显式化父类的属性    property Align;    property Alignment;    property DragCursor;    property DragMode;    property Enabled;    property Color;    property Font;    property Lines;    property MaxLength;    property OEMConvert;    property ParentFont;    property ParentShowHint;    property PopupMenu;    property ReadOnly;    property ShowHint;    property ScrollBars;    property TabOrder;    property TabStop;    property Visible;    property WantReturns;    property WantTabs;    property WordWrap;      property OnChange;    property OnClick;    property OnDblClick;    property OnDragDrop;    property OnDragOver;    property OnEndDrag;    //增加这两个事件,处理鼠标进入和离开    property OnEnter;    property OnExit;    property OnKeyDown;    property OnKeyPress;    property OnKeyUp;    property OnMouseDown;    property OnMouseMove;    property OnMouseUp;    property OnStartDrag;   end;   procedure Register;   implementation   procedure Register; begin   RegisterComponents('Samples', [TCoolMemo]); end;   constructor TCoolMemo.Create(AOwner:TComponent); begin   inherited Create(Aowner);   ControlStyle := ControlStyle - [csFramed];   ParentFont := True;   FEdgeColor := $ff0000;   FEnterColor := $0000ff;   //设定外观,平面无边形   Ctl3D := False;   FMargin:=0;   BorderStyle:=bsNone;   height:=150;   width:=200; end;   procedure TCoolMemo.setMargin(Value:byte); var   Rect: TRect; begin //该消息取得客户区的尺寸   SendMessage(Handle, EM_GETRECT, 0, Longint(@Rect));   //以下是重新确定尺寸   Rect.Top := Value;   Rect.Left := Value;   Rect.Right := Width -Value;   Rect.Bottom := Height -Value; //该消息设定客户区的大小   SendMessage(Handle, EM_SETRECT, 0, Longint(@Rect));   Fmargin:=value; end;   function TCoolMemo.getPosition:TPosition; var   row,Col:longint;   CBLines:longint;   str:WideString; begin //该消息取得光标所在的行,   row:= SendMessage(Handle,EM_LINEFROMCHAR,SelStart,0);   //该消息取得光标所在行开始的位置,位置从第一行的0开始计数,   //每过一个字符增加1,   CBLines:=SendMessage(Handle,EM_LINEINDEX,row,0);   //得到光标的所在行的所在列   Col:=SelStart-CBLines;   //为了解决中文的问题,需要用宽字符型来取得光标所在行   //,行中光标所在列之前的字符串,这样可以解决中文列数的确定问题.   str:=Copy(Lines[row],1,col);   col:=Length(Str)+1;   result.row:=row+1;   result.col:=col; end;   procedure TCoolMemo.setEdgeColor(Value:TCOlor); begin  if FEdgeColor<>value then  begin   FEdgeColor:=value;   drawBorder;  end; end;   procedure TCoolMemo.setEnterColor(Value:TColor); begin  if FEnterColor<>value then  begin   FEnterColor:=value;   drawBorder;  end; end;   procedure TCoolMemo.CMMouseEnter(var Message: TMessage); begin   inherited;    MouseIn:= True;    drawBorder; end;   procedure TCoolMemo.CMMouseLeave(var Message:TMessage); begin   inherited;   MouseIn:=False;   drawBorder; end;   procedure TCoolMemo.WMPaint (var Message: TMessage); begin   inherited;   drawBorder; end;   procedure TCoolMemo.WMNCCalcSize (var Message: TWMNCCalcSize); begin   inherited;   InflateRect(Message.CalcSize_Params^.rgrc[0], -3, -3); end;   procedure TCoolMemo.drawBorder; var   DC: HDC;  //设备描述表   R: TRect; //客户区   EnterBrush,OuterBrush,BorderBrush:HBRUSH;  //画笔句柄,API begin   DC:= GetWindowDC(Handle);  //取得该组件的设备描述表   try    GetWindowRect(Handle, R);  //取得该组件的客户区尺寸    OffsetRect(R, -R.Left, -R.Top); //左上偏移    //创建画笔,两个,分别代码边框,边框内,白色画笔    BorderBrush := CreateSolidBrush(ColorToRGB(FEdgeColor));    EnterBrush:= CreateSolidBrush(ColorToRGB(FEnterColor));    OuterBrush:=CreateSolidBrush(ColorToRGB(clWhite)); //not(csDesigning in ComponentState保证在设计期不变    if (not(csDesigning in ComponentState)) and    (MouseIn=true) then  //如果鼠标进入    begin     //画一个矩形框,用BorderBrush画笔     FrameRect(DC, R, BorderBrush);     //把R缩小一个象素     InflateRect(R, -1, -1);     //画一个矩形框,用outerBrush画笔     FrameRect(DC, R, outerBrush);     InflateRect(R, -1, -1);     FrameRect(DC, R, EnterBrush);    end    else  //如果鼠标没有进入    begin     FrameRect(DC, R, BorderBrush);     InflateRect(R, -1, -1);     FrameRect(DC, R, outerBrush);     InflateRect(R, -1, -1);     FrameRect(DC, R, outerBrush);    end;   finally    ReleaseDC(Handle, DC);  //释放设备描述表   end;   DeleteObject(BorderBrush);  //释放画笔   DeleteObject(EnterBrush);   DeleteObject(OuterBrush); end;   end.   安装上去试试吧,比Memo1好看多了,功能也强多了。是吗。  至此已经做了三个组件了,其实不算很复杂,只要理清思绪。到这里似乎可以结束这次的组件制作之旅了,但是还没有。我们似乎还没有做过非可视化组件。所以我想最后一个,就是做一个非可视化组件。想知道是什么,往下看吧。

Tags:组件 制作 定制

编辑录入:爽爽 [复制链接] [打 印]
赞助商链接