• 首页
  • 小学语文
  • 中学语文
  • 中学英语
  • 免费论文
  • 教学随笔
  • 学生作文
  • 综合考试
  • 试题教案
  • 育儿话题
  • 教学资源
  • 编程技术
  • 博客
  • 组件制作之四(定制外观)

    日期:2003-03-28  地址:  作者:

    时常想,如果一个组件能够按自己想要的外观显示,那该是件多么COOL的事啊,这一篇就要来做一个精美外观的组件,但是,做什么好呢.Button? 高手突破>有关于自己定义外观的Button,以及CheckBox等的做法,ButtonCustomPanel继承,重载Paint方法来画外观.如果你有兴趣,可以去找来看,这里就不做Button,做一个Memo如何呢.?是个不错的主意。

     

    我们先起个名字叫做TCoolMemo。以上篇已经讲了很多组件的技术,这里就只说出几个重点。其余不多说了。

     

    首先,该MemoCustomMemo继承,它有这样外观:属于平面的,边框是可以设置颜色的线,对应的颜色变量为FEdgeColor,另外,离边框以内的两个象素处,还有另一个框,当鼠标进入Memo时,这个框会显示,当鼠标离开时,为个框消失,同样也可以设置颜色,对应变量为FEnterColor

    那么鼠标进入和离开怎么判断呢,这里Memo将截获两个Delphi的内部消息:

    //下面两个获得Delphi的内部消息,鼠标进入和离开时发生

         procedure CMMouseEnter (var Message: TMessage); message CM_MOUSEENTER;

         procedure CMMouseLeave (var Message: TMessage); message CM_MOUSELEAVE;

    其实父类已经截获了这两个消息,并作了相应处理,所以TCoolMemo中的消息处理函数要

    Inherited;再作自己的处理。这里又用到了一个变量

    MouseInBoolean;//标识鼠标是否进入组件

     

    接下来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_MOUSEENTERCM_MOUSELEAVE;将引起TCoolMemo的外观变化,WM_PAINT保存其外观不被擦去。所以要用到一个画组件的函数,即:

    drawBorder;

    里面用到了几个APIGDI函数。我在代码中有详细的说明,加上自己看帮助,应该是可以看懂的。

     

    另外,相比于Memo,它的扩展了这样的功能:设置边距和获得光标的位置。这两个对应的性属为MarginPosition。他们都是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_MOUSEENTERCM_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);

    对 组件制作之四(定制外观) 文章的评论    [查看网友评论]

    验证码:
    匿名发表: