公布TstringGrid增强控件TcbStrGrid源码,带CheckBox的TStringGrid控件

unit CbStrGrid;
    {************************扩展的TStringGrid控件TcbStrGrid********************
    [功能简介] 增强的字符串表格控件,主要功能有
        1.在strGrid上显示带CheckBox的列;
        2.设置列标题及列数据对齐方式,列数据的显示方式,如按货币的方式,数字的方式;
          若是按货币/数字方式显示的话,能进行输入控制,即只能输入数字。
        3.自动生成行号,设置要显示合计的行,自动求合计;
        4.加入清除表格clear方法等
    [实现思想]
        1.重载DrawCell方法。按照属性的设置情况,自定义画出显示的内容。
        而实际的值保持不变。
        2.重载SelectCell方法实现设置只读列等。
        3.重载SizeChanged方法实现自动添加行号
        4.根据上面的方法其实你可以做得更多,包括
          在表格中画图片,进度条等
          绑定数据集,相信会对做三层很有帮助。
    [关键属性/方法]
       集合字符串,特指以数字和,构成的字符串,如 '1,2,3'
       1.procedure clear;             //清空表格中的数据

       2.procedure DoSumAll;          //对所有的数字列/货币求和
         property OnSumValueChanged: TSumValueChanged
         合计值发生变化时触发
         property DisplaySumRow: Boolean
       是否要显示合计,要显示合计,则用户在strGrid上编辑时,自动更新合计值,若要手动更新合计,
       请调用doSumAll方法

       3.property CheckColumnIndex:integer       //设置带checkBox的列
         property OnCheckChanged: TCheckChanged
       当鼠标/空格键操作导致checkBox列的值发生变化时触发该事件
       注意: 只是响应了鼠标/键盘在strGrid上操作,当在程序中赋值而导致的checkbox变化时,该事件并不触发
        function  NonChecked: boolean;   //若没有check选择任何行返回True;

       4.property TitleAlign: TTitleAlign     //标题对齐方式

       5.property ColsCurrency: String        //以货币方式显示的列的集合字符串
         property ColsNumber: String          //以数字方式显示的列的集合字符串
         property ColsAlignLeft: String       //向左靠齐显示的列的集合字符串
         property ColsAlignCenter: String     //居中显示的列的集合字符串
         property ColsAlignRight: String      //向右靠齐显示的列的集合字符串
         注意:设置时请不要重复设置列,包括checkColumnIndex,为什么呢? 请看源代码

       6.property ColsReadOnly: string        //设置只读的列的集合字符串,其他的列可以直接编辑
    [注意事项]
       按方向键有点画FocusRect时有点小问题。
    [修改日志]
       作者: majorsoft(杨美忠)      创建日期: 2004-6-6     修改日期 2004-6-8     Ver0.92
       Email: majorcompu@163.com    QQ:122646527   (dfw)  欢迎指教!
    [版权声明]  Ver0.92
      该程序版权为majorsoft(杨美忠)所有,你可以免费地使用、修改、转载,不过请附带上本段注释,
      请尊重别人的劳动成果,谢谢。
    ****************************************************************************}
interface

uses
  Windows, SysUtils, Classes, Controls, Grids, Graphics;

const
  STRSUM='合计';

type
  TTitleAlign=(taLeft, taCenter, taRight);  //标题对齐方式
  TInteger=set of 0..254;
  TCheckChanged = procedure (Sender: TObject; ARow: Longint) of object;
  TSumValueChanged = procedure (Sender: TObject) of object;

  TCbStrGrid = class(TStringGrid)
  private
    fCheckColumnIndex: integer;
    FDownColor: TColor;
    fIsDown: Boolean;                                 //鼠标(或键盘)是否按下 用来显示动画效果
    fTitleAlign: TTitleAlign;                         //标题对齐方式

    FAlignLeftCols: String;
    FAlignLeftSet: TInteger;
    FAlignRightCols: String;
    FAlignRightSet: TInteger;
    FAlignCenterCols: String;
    FAlignCenterSet: TInteger;
    fCurrCols: string;                                //需要以货币方式显示的列的字符串,以','分隔
    fCurrColsSet: TInteger;                           //需要以货币方式显示的列的序号的集合
    fNumCols: string;                                 //需要以数字方式显示的列的字符串,以','分隔
    fNumColsSet: TInteger;                            //需要以数字方式显示的列的序号的集合
    FColsReadOnly: string;                            //只读列的列序号字符串
    FReadOnlySet: TInteger;                           //只读列的序号的集合
    FCheckChanged: TCheckChanged;                     //最近check变化事件
    FDisplaySumRow: Boolean;
    FOnSumValueChanged: TSumValueChanged;                          
    procedure AlterCheckColValue;                     //交替更换带checkbox的列的值
    procedure SetAlignLeftCols(const Value: String);
    procedure SetAlignCenterCols(const Value: String);
    procedure SetAlignRightCols(const Value: String);
    procedure setCheckColumnIndex(const value:integer);
    procedure SetColorDown(const value: TColor);
    procedure setTitleAlign(const value: TTitleAlign);
    procedure setCurrCols(const value: string);
    procedure setNumCols(const value: string);
    procedure SetColsReadOnly(const Value: string);
    procedure SetDisplaySumRow(const Value: Boolean);
    procedure SetOnSumValueChanged(const Value: TSumValueChanged);
  protected
    procedure DrawCell(ACol, ARow: Longint; ARect: TRect;
      AState: TGridDrawState); override;   //画
    procedure KeyDown(var Key: Word; Shift: TShiftState); override;
    procedure KeyPress(var Key: Char); override;
    procedure KeyUp(var Key: Word; Shift: TShiftState); override;
    procedure MouseDown(Button: TMouseButton; Shift: TShiftState;
      X, Y: Integer); override;
    procedure MouseUp(Button: TMouseButton; Shift: TShiftState;
      X, Y: Integer); override;
    function SelectCell(ACol, ARow: Longint): Boolean; override;
    procedure SizeChanged(OldColCount, OldRowCount: Longint); override;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    procedure clear;                 //清空表格中的数据
    procedure DoSumAll;              //对所有的数字列/货币求和
    function  NonChecked: boolean;   //若没有check选择任何行返回True;
  published
    property CheckColumnIndex:integer read FCheckColumnIndex write SetCheckColumnIndex default 1;   //设置带checkBox的列
    property ColorDown: TColor read FDownColor write SetColorDown default $00C5D6D9;
    property TitleAlign: TTitleAlign read fTitleAlign write setTitleAlign default taLeft;  //标题对齐方式
    property ColsCurrency: String read fCurrCols write setCurrCols;                        //以货币方式显示的列的集合字符串
    property ColsNumber: String read fNumCols write SetNumCols;                            //以数字方式显示的列的集合字符串
    property ColsAlignLeft: String read FAlignLeftCols write SetAlignLeftCols;             //向左靠齐显示的列的集合字符串
    property ColsAlignCenter: String read FAlignCenterCols write SetAlignCenterCols;       //居中显示的列的集合字符串
    property ColsAlignRight: String read FAlignRightCols write SetAlignRightCols;          //向右靠齐显示的列的集合字符串
    property ColsReadOnly: string read FColsReadOnly write SetColsReadOnly;                //设置只读的列的集合字符串,其他的列可以直接编辑
    {property DisplaySumRow:
     是否要显示合计,要显示合计,则用户在strGrid上编辑时,自动更新合计值,若要手动更新合计,
     请调用doSumAll方法}
    property DisplaySumRow: Boolean read FDisplaySumRow write SetDisplaySumRow;
    {property OnCheckChanged:
    当鼠标/空格键操作导致checkBox列的值发生变化时触发该事件
    注意: 只是响应了鼠标/键盘在strGrid上操作,当在程序中赋值而导致的checkbox变化时,该事件并不触发}
    property OnCheckChanged: TCheckChanged  read FCheckChanged write FCheckChanged;
    property OnSumValueChanged: TSumValueChanged read FOnSumValueChanged write SetOnSumValueChanged;

  end;

procedure Register;
function MyStrToint(Value:string):integer;
function MyStrToFloat(str:string):extended;
function PointInRect(const pt:Tpoint; const Rect: TRect):boolean;
function ExtractNumToSet(const str: string; var aSet: TInteger):Boolean; //从 str中提取数字放到aSet集合中,若成功则返回true

implementation

function MyStrToint(value:string):integer;
begin
  tryStrToInt(trim(value),result);
end;

function MyStrToFloat(str:string):extended;
begin
  if trim(str)='' then
    result:=0.0
  else TryStrTofloat(trim(str),result);
end;

function PointInRect(const pt:Tpoint; const Rect: TRect):boolean;
begin
  if (Pt.X>=Rect.Left) and (Pt.X<=Rect.Right) and
     (Pt.Y>= Rect.Top) and (Pt.Y<=Rect.Bottom) then
    result:=True
  else result:=false;
end;

function ExtractNumToSet(const str: string; var aSet: TInteger):Boolean;
var
  tmpStr:string;
  iComma, i:Integer;  //逗号位置
begin
  aSet:=[]; //初始化集合

  if Length(str)=0 then
  begin
    result:=true;
    exit;
  end;

  if not (str[1] in ['0'..'9']) then  //检查合法性1
  begin
    result:=false;
    exit;
  end;

  for i:=1 to Length(str) do      //检查合法性2
    if not (str[i] in ['0'..'9', ',']) then
    begin
      result:=false;
      exit;
    end;

  tmpStr:=Trim(Str);
  while length(tmpStr)>0 do
  begin
    iComma:=pos(',', tmpStr);
    if (tmpstr[1] in ['0'..'9']) then
      if (iComma>0) then
      begin
        include(aSet, StrToInt(Copy(tmpStr, 1, iComma-1)));
        tmpStr:=copy(tmpStr, iComma+1, length(tmpStr)-iComma);
      end
      else begin
        include(aSet, StrToInt(tmpStr));
        tmpStr:='';
      end
    else tmpStr:=copy(tmpStr, iComma+1, length(tmpStr)-iComma);
  end;

  result:=true;
end;

procedure Register;
begin
  RegisterComponents('MA', [TCbStrGrid]);
end;

{ TCbStrGrid }

procedure TCbStrGrid.AlterCheckColValue;
begin
  if (Row>0) and (col=fCheckColumnIndex) then
  begin
    if MyStrToint(Cells[col,Row])=0 then
      Cells[col, Row]:='1'
    else Cells[col, Row]:='0';

  end;
end;

constructor TCbStrGrid.Create(AOwner: TComponent);
begin
  inherited;
  Options:=Options + [goColSizing];
  fCheckColumnIndex:=1;
  FDownColor:=$00C5D6D9;
  Height:=150;
  Width:=350;
  col:=ColCount-1;
end;

destructor TCbStrGrid.Destroy;
begin

  inherited;
end;

procedure TCbStrGrid.DrawCell(ACol, ARow: Integer; ARect: TRect;
  AState: TGridDrawState);
var
  area, CheckboxRect: TRect;
  CurPt: TPoint;
  value, OffSetX, OffSetY:integer;
  strCell: String;
begin
  Area:= ARect;
  InflateRect(Area, -2, -2);  //缩小区域  主要作为text out区域

  if (ARow>0) then
  begin
    if aCol in fNumColsSet then    //数字方式
    begin
      strCell:=FormatFloat('#,##0.##', MyStrToFloat(Cells[ACol, ARow]));
      DrawText(canvas.Handle, PChar(strCell), Length(strCell), Area, DT_RIGHT) //设为靠右
    end
    else if aCol in fCurrColsSet then  //货币方式
    begin
      strCell:='¥'+FormatFloat('#,###.00', MyStrToFloat(Cells[ACol, ARow]));
      DrawText(canvas.Handle, PChar(strCell), Length(strCell), Area, DT_RIGHT) //设为靠右
    end
    else if aCol in FAlignLeftSet then
       DrawText(Canvas.Handle, PChar(Cells[ACol, ARow]),Length(Cells[ACol, ARow]), Area, DT_Left)
    else if aCol in FAlignCenterSet then
       DrawText(Canvas.Handle, PChar(Cells[ACol, ARow]),Length(Cells[ACol, ARow]), Area, DT_Center)
    else if aCol in FAlignRightSet then
       DrawText(Canvas.Handle, PChar(Cells[ACol, ARow]),Length(Cells[ACol, ARow]), Area, DT_Right)
    else if (aCol=fCheckColumnIndex) then    //checkBox方式
    begin
      if (Cells[0, ARow]=STRSUM) then exit;  //合计行的checkBox不画

      value:=MyStrToint(Cells[fCheckColumnIndex,aRow]);

      Canvas.FillRect(ARect);
      with ARect do
      begin
        OffSetX:=(Right- Left- 10) div 2;
        OffSetY:=(Bottom- Top- 10) div 2;
      end;

      CheckboxRect:=Rect(ARect.Left+OffSetX, ARect.Top + OffSetY,     //取得checkBox要画的区域
                         ARect.Left+OffSetX+11, ARect.Top + OffSetY +11);

      canvas.pen.style := psSolid;
      canvas.pen.width := 1;
      getCursorPos(CurPt);
      CurPt:=self.ScreenToClient(CurPt);

      {画背景}
      if (fisDown) and PointInRect(CurPt, ARect) then
      begin
        canvas.brush.color := fDownColor;
        canvas.pen.color := clBlack;
      end
      else begin
        canvas.brush.color := color;
        canvas.pen.color := clBlack;
      end;
      canvas.FillRect(CheckboxRect);
 
      { 画勾}
      if (value<>0) then       //不为0表示checked=true;
      begin
        canvas.penpos := Point(CheckboxRect.left+2, CheckboxRect.top+4);//设置起点
        canvas.lineto(CheckboxRect.left+6, CheckboxRect.top+8);         //画到...
        canvas.penpos := Point(CheckboxRect.left+2, CheckboxRect.top+5);
        canvas.lineto(CheckboxRect.left+5, CheckboxRect.top+8);
        canvas.penpos := Point(CheckboxRect.left+2, CheckboxRect.top+6);
        canvas.lineto(CheckboxRect.left+5, CheckboxRect.top+9);
        canvas.penpos := Point(CheckboxRect.left+8, CheckboxRect.top+2);
        canvas.lineto(CheckboxRect.left+4, CheckboxRect.top+6);
        canvas.penpos := Point(CheckboxRect.left+8, CheckboxRect.top+3);
        canvas.lineto(CheckboxRect.left+4, CheckboxRect.top+7);
        canvas.penpos := Point(CheckboxRect.left+8, CheckboxRect.top+4);
        canvas.lineto(CheckboxRect.left+5, CheckboxRect.top+7);
      end;
      {画边界}
      Area:=CellRect(Col, Row);
      DrawFocusRect(canvas.Handle, Area);   //
      canvas.brush.color :=clBlack;
      canvas.FrameRect(CheckboxRect);
    end
    else inherited DrawCell(ACol, ARow, ARect, AState);
  end
  else if (ARow=0) then
  begin
    Canvas.FillRect(ARect);
    case fTitleAlign of
      taLeft: DrawText(Canvas.Handle, PChar(Cells[ACol, ARow]),Length(Cells[ACol, ARow]), Area, DT_Left);
      taCenter: DrawText(Canvas.Handle, PChar(Cells[ACol, ARow]),Length(Cells[ACol, ARow]), Area, DT_Center);
      taRight: DrawText(Canvas.Handle, PChar(Cells[ACol, ARow]),Length(Cells[ACol, ARow]), Area, DT_Right);
    end;
  end
  else inherited DrawCell(ACol, ARow, ARect, AState);
end;

procedure TCbStrGrid.KeyDown(var Key: Word; Shift: TShiftState);
begin
  if (key=vk_space) and (Row>0) and (col=fCheckColumnIndex)then
    fIsDown:=True;
  inherited;
end;

procedure TCbStrGrid.KeyUp(var Key: Word; Shift: TShiftState);
var
  Area:TRect;
begin
  if (key=vk_space) and (Row>0) and (col=fCheckColumnIndex)then
  begin
    AlterCheckColValue;
    fIsDown:=false;
    if Assigned(FCheckChanged) then FCheckChanged(self, Row);
  end;

  inherited;
  if key=vk_Up then     //vk_up TMD变态
  begin
    Area:=self.CellRect(Col, Row);
    DrawFocusRect(canvas.Handle, Area);
  end;

  if FDisplaySumRow then DoSumAll;
end;

procedure TCbStrGrid.MouseDown(Button: TMouseButton; Shift: TShiftState; X,
  Y: Integer);
begin
  if (Row>0) and (col=fCheckColumnIndex)then
    fIsDown:=True;

  inherited;
end;

procedure TCbStrGrid.MouseUp(Button: TMouseButton; Shift: TShiftState; X,
  Y: Integer);
var
  curPt: TPoint;
  Area:TRect;
begin
  getCursorPos(CurPt);
  CurPt:=self.ScreenToClient(CurPt);
  Area:=self.CellRect(Col, Row);
  if (Row>0) and (col=fCheckColumnIndex) and PointInRect(CurPt, Area) then
  begin
    AlterCheckColValue;
    fIsDown:=false;
    if Assigned(FCheckChanged) then FCheckChanged(self, Row);
  end;
 
  inherited;
 
  if FDisplaySumRow then DoSumAll;
end;

procedure TCbStrGrid.SetAlignLeftCols(const Value: String);
begin
  if ExtractNumToSet(Value, fAlignLeftSet) then
    FAlignLeftCols := Value
  else Raise Exception.Create('属性值设置错误, 请用数字和,分隔的方式设置属性');
  InvalidateGrid;
end;

procedure TCbStrGrid.setCheckColumnIndex(const value: integer);
begin
  if (value>colCount) then raise exception.Create('CheckColumnIndex越界');
  fCheckColumnIndex:=Value;
  repaint;
end;

procedure TCbStrGrid.SetColorDown(const value: TColor);
begin
  fDownColor:=value;
  InvalidateCell(fCheckColumnIndex, row);
end;

procedure TCbStrGrid.SetAlignCenterCols(const Value: String);
begin
  if ExtractNumToSet(Value, FAlignCenterSet) then
     FAlignCenterCols := Value
  else Raise Exception.Create('属性值设置错误, 请用数字和,分隔的方式设置属性');
  InvalidateGrid;
end;

procedure TCbStrGrid.SetAlignRightCols(const Value: String);
begin
  if ExtractNumToSet(Value, FAlignRightSet) then
     FAlignRightCols := Value
  else Raise Exception.Create('属性值设置错误, 请用数字和,分隔的方式设置属性');
  InvalidateGrid;
end;

procedure TCbStrGrid.setCurrCols(const value: string);
begin
  if ExtractNumToSet(Value, fCurrColsSet) then
    fCurrCols:=value
  else Raise Exception.Create('属性值设置错误, 请用数字和,分隔的方式设置属性');
  InvalidateGrid;
end;

procedure TCbStrGrid.setNumCols(const value: string);
begin
  if ExtractNumToSet(Value, fNumColsSet) then
    fNumCols:=value
  else Raise Exception.Create('属性值设置错误, 请用数字和,分隔的方式设置属性');
  InvalidateGrid;
end;

procedure TCbStrGrid.setTitleAlign(const value: TTitleAlign);
begin
  if not(value in [taLeft, taCenter, taRight]) then  Raise Exception.Create('属性值设置错误,请在[taLeft, taCenter, taRight]选择');
  fTitleAlign:=value;
  InvalidateGrid;
end;

function TCbStrGrid.SelectCell(ACol, ARow: Integer): Boolean;
begin
  if (ACol=fCheckColumnIndex) or (ACol in FReadOnlySet) then
    Options:=Options - [goEditing]
  else Options:=Options + [goEditing];

  Inherited SelectCell(ACol, ARow);
end;

procedure TCbStrGrid.SetColsReadOnly(const Value: string);
begin
  if ExtractNumToSet(Value,FReadOnlySet) then
    FColsReadOnly := Value
  else Raise Exception.Create('属性值设置错误, 请用数字和,分隔的方式设置属性');
  InvalidateGrid;
end;

procedure TCbStrGrid.clear;
var
  i,j:integer;
begin
  for i:=1 to RowCount-1 do
    for j:=1 to ColCount-1 do
     Cells[j,i]:='';         //注意j,i的顺序

  InvalidateGrid;
end;

procedure TCbStrGrid.SizeChanged(OldColCount, OldRowCount: Integer);
var
  i:integer;
begin
  inherited;
  for i:=1 to RowCount-1 do
     Cells[0,i]:=inttostr(i);

  if FDisplaySumRow then cells[0, RowCount-1]:=STRSUM;
  InvalidateGrid;
end;

procedure TCbStrGrid.SetDisplaySumRow(const Value: Boolean);
begin
  FDisplaySumRow := Value;
  RowCount:=RowCount+1;      //仅做刷新用  会调用SizeChanged
  RowCount:=RowCount-1;      //非常规做法。没想到好办法。
  if FDisplaySumRow then DoSumAll;
  InvalidateGrid;
end;

procedure TCbStrGrid.DoSumAll;
var
  i, j:integer;
begin
  if not fDisplaySumRow then exit;

  for j:=1 to ColCount-1 do  //先初始化
    if (j in fCurrColsSet) or (j in fNumColsSet) then
    Cells[j, RowCount-1]:='0';

  for i:=1 to RowCount-2 do
    for j:=1 to ColCount-1 do
      if (j in fCurrColsSet) or (j in fNumColsSet) then
      Cells[j, RowCount-1]:=FloatToStr((MyStrToFloat(Cells[j, RowCount-1]) + MyStrToFloat(Cells[j, i])));

  if Assigned(FOnSumValueChanged) then FOnSumValueChanged(self);
end;

procedure TCbStrGrid.KeyPress(var Key: Char);
begin
  if (Col in fCurrColsSet+ fNumColsSet) then
    if not(key in ['0'..'9', '.', '-', char(VK_back), char(VK_Delete)]) then
    key:=#0;
  inherited KeyPress(Key);
end;

function TCbStrGrid.NonChecked: boolean;
var
  i, iMax:integer;
begin
  result:=True;

  if FDisplaySumRow then IMax:= RowCount-2 else IMax:= RowCount-1;
  for i:=1 to iMax do
  begin
    if Cells[CheckColumnIndex, i]='1' then
    begin
      result:=false;
      exit;
    end
  end;
end;

procedure TCbStrGrid.SetOnSumValueChanged(const Value: TSumValueChanged);
begin
  FOnSumValueChanged := Value;
end;

end.  
 
 
阅读更多
想对作者说点什么?

博主推荐

换一批

没有更多推荐了,返回首页