Главная Информер Журнал Форум Wiki DRKB Discuz!ML Помощь проекту




Видоизменяем чекбоксы в Delphi.

Автор: Maarten de Haan

В WIN3.1 чекбоксы заполняются символом "X". В WIN95 и WINNT - символом "V". В тандартной палитре Delphi чекбоксы заполняются символом "X". Спрашивается - почему фирма Borland/Inprise не исправила значёк чекбокса для W95/W98 ?. Данный пример позволяет заполнять чекбокс такими значками как: "X", "V", "o", "закрашенным прямоугольником", или бриллиантиком.

Пример тестировался под WIN95 и WINNT.

Совместимость: Delphi 5.x (или выше)

  Собственно сам исходничек:

{
====================================================================
                          Обозначения
====================================================================
X = крестик
V = галочка
o = кружок

+-+
|W| = заполненный прямоугольник
+-+

/\
= бриллиантик
\/

====================================================================
                  Преимущества этого чекбокса
====================================================================
Вы можете найти множество чекбоксов в интернете. Но у них есть недостаток, они не обрабатывают сообщение WM_KILLFOCUS. Приведённый ниже пример делает это.
====================================================================
}
Unit CheckBoxX;

Interface

Uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

Const
   { другие константы }
   fRBoxWidth  : Integer = 13; // ширина квадрата checkbox
   fRBoxHeight : Integer = 13; // высота квадрата checkbox

Type
  TState = (cbUnchecked,cbChecked,cbGrayed); // такой же как в Delphi
  TType = (cbCross,cbMark,cbBullet,cbDiamond,cbRect); // добавленный
  TMouseState = (msMouseUp,msMouseDown);
  TAlignment = (taRightJustify,taLeftJustify); // The same

  TCheckBoxX = class(TCustomControl)

  Private
    { Private declarations }
    fChecked        : Boolean;
    fCaption        : String;
    fColor          : TColor;
    fState          : TState;
    fFont            : TFont;
    fAllowGrayed    : Boolean;
    fFocus          : Boolean;
    fType            : TType;
    fMouseState     : TMouseState;
    fAlignment      : TAlignment;
    fTextTop        : Integer;  // отступ текта с верху
    fTextLeft       : Integer;  // отступ текта с лева
    fBoxTop         : Integer;  // координата чекбокса сверху
    fBoxLeft        : Integer;  // координата чекбокса слева

    Procedure fSetChecked(Bo : Boolean);
    Procedure fSetCaption(S : String);
    Procedure fSetColor(C : TColor);
    Procedure fSetState(cbState : TState);
    Procedure fSetFont(cbFont : TFont);
    Procedure fSetAllowGrayed(Bo : Boolean);
    Procedure fSetType(T : TType);
    Procedure fSetAlignment(A : TAlignment);

  Protected
    { Protected declarations }
    Procedure Paint; override;
    Procedure MouseDown(Button: TMouseButton; Shift: TShiftState;
      X, Y: Integer); override;
    Procedure MouseUp(Button: TMouseButton; Shift: TShiftState;
      X, Y: Integer); override;
    Procedure WMKillFocus(var Message : TWMKillFocus);
      Message WM_KILLFOCUS; // это убирает контур фокуса!
    Procedure WMSetFocus(var Message : TWMSetFocus);
      Message WM_SETFOCUS; // Если вы используете клавишу TAB или Shift-Tab
    Procedure KeyDown(var Key : Word; Shift : TShiftState); override;
      // перехват KeyDown
    Procedure KeyUp(var Key : Word; Shift : TShiftState); override;
      // перехват KeyUp

  Public
    { Public declarations }
    // Если поместить Create и Destroy в раздел protected,
    // то Delphi начинает ругаться.
    Constructor Create(AOwner : TComponent); override;
    Destructor Destroy; override;

  Published
    { Published declarations }
    { --- Свойства --- }
    Property Action;
    Property Alignment : TAlignment
       read fAlignment write fSetAlignment;
    Property AllowGrayed : Boolean
       read fAllowGrayed write fSetAllowGrayed;
    Property Anchors;
    Property BiDiMode;
    Property Caption : String
       read fCaption write fSetCaption;
    Property CheckBoxType : TType
       read fType write fSetType;
    Property Checked : Boolean
       read fChecked write fSetChecked;
    Property Color : TColor
       read fColor write fSetColor;
    Property Constraints;
    //Property Ctrl3D;
    Property Cursor;
    Property DragCursor;
    Property DragKind;
    Property DragMode;
    Property Enabled;
    Property Font : TFont
       read fFont write fSetFont;
    //Property Height;
    Property HelpContext;
    Property Hint;
    Property Left;
    Property Name;
    //Property PartenBiDiMode;
    Property ParentColor;
    //Property ParentCtrl3D;
    Property ParentFont;
    Property ParentShowHint;
    //Property PopMenu;
    Property ShowHint;
    Property State : TState
       read fState write fSetState;
    Property TabOrder;
    Property TabStop;
    Property Tag;
    Property Top;
    Property Visible;
    //Property Width;
    { --- Events --- }
    Property OnClick;
    Property OnContextPopup;
    Property OnDragDrop;
    Property OnDragOver;
    Property OnEndDock;
    Property OnEndDrag;
    Property OnEnter;
    Property OnExit;
    Property OnKeyDown;
    Property OnKeyPress;
    Property OnKeyUp;
    Property OnMouseDown;
    Property OnMouseMove;
    Property OnMouseUp;
    Property OnStartDock;
    Property OnStartDrag;
  End;

Procedure Register; //Hello!

Implementation

{-------------------------------------------------------------------}
Procedure TCheckBoxX.KeyDown(var Key : Word; Shift : TShiftState);

Begin
If fFocus then
   If Shift = [] then
      If Key = 0032 then
         Begin
         fMouseState := msMouseDown;
         If fState <> cbGrayed then
            Begin
            SetFocus; // Устанавливаем фокус на этот компонент
                      // всем другим компонентам Windows посылает сообщение WM_KILLFOCUS.
            fFocus := True;
            Invalidate;
            End;
         End;
Inherited KeyDown(Key,Shift);
End;
{-------------------------------------------------------------------}
Procedure TCheckBoxX.KeyUp(var Key : Word; Shift : TShiftState);

Begin
If fFocus then
   If Shift = [] then
      If Key = 0032 then
         Begin
         If fState <> cbGrayed then
            fSetChecked(not fChecked); // Изменяем состояние
         fMouseState := msMouseUp;
         End;
Inherited KeyUp(Key,Shift);
End;
{-------------------------------------------------------------------}
Procedure TCheckBoxX.WMSetFocus(var Message : TWMSetFocus);

Begin
fFocus := True;
Invalidate;
End;
{-------------------------------------------------------------------}
Procedure TCheckBoxX.WMKillFocus(var Message : TWMKillFocus);

Begin
fFocus := False; // Удаляем фокус у всех компонент, которые не имеют фокуса.
Invalidate;
End;
{-------------------------------------------------------------------}
Procedure TCheckBoxX.fSetAlignment(A : TAlignment);

Begin
If A <> fAlignment then
   Begin
   fAlignment := A;
   Invalidate;
   End;
End;
{-------------------------------------------------------------------}
Procedure TCheckBoxX.fSetType(T : TType);

Begin
If fType <> T then
   Begin
   fType := T;
   Invalidate;
   End;
End;
{-------------------------------------------------------------------}
Procedure TCheckBoxX.fSetFont(cbFont : TFont);

Var
   FontChanged : Boolean;

Begin
FontChanged := False;

If fFont.Style <> cbFont.Style then
   Begin
   fFont.Style := cbFont.Style;
   FontChanged := True;
   End;

If fFont.CharSet <> cbFont.Charset then
   Begin
   fFont.Charset := cbFont.Charset;
   FontChanged := True;
   End;

If fFont.Size <> cbFont.Size then
   Begin
   fFont.Size := cbFont.Size;
   FontChanged := True;
   End;

If fFont.Name <> cbFont.Name then
   Begin
   fFont.Name := cbFont.Name;
   FontChanged := True;
   End;

If fFont.Color <> cbFont.Color then
   Begin
   fFont.Color := cbFont.Color;
   FontChanged := True;
   End;

If FontChanged then
   Invalidate;
End;
{-------------------------------------------------------------------}
procedure TCheckBoxX.MouseDown(Button: TMouseButton; Shift: TShiftState;
  X, Y: Integer);

Begin
// Процедура MouseDown вызывается, когда кнопка мышки нажимается в пределах
// кнопки, соответственно мы не можем получить значения координат X и Y.
inherited MouseDown(Button, Shift, X, Y);
fMouseState := msMouseDown;
If fState <> cbGrayed then
   Begin
   SetFocus; // Устанавливаем фокус на этот компонент
             // всем другим компонентам Windows посылает сообщение WM_KILLFOCUS.
   fFocus := True;
   Invalidate;
   End;
End;
{-------------------------------------------------------------------}
procedure TCheckBoxX.MouseUp(Button: TMouseButton; Shift: TShiftState;
  X, Y: Integer);

Begin
// Процедура MouseUp вызывается, когда кнопка мышки отпускается в пределах
// кнопки, соответственно мы не можем получить значения координат X и Y.
inherited MouseUp(Button, Shift, X, Y);
If fState <> cbGrayed then
   fSetChecked(not fChecked); // Изменяем состояние
fMouseState := msMouseUp;
End;
{-------------------------------------------------------------------}
Procedure TCheckBoxX.fSetAllowGrayed(Bo : Boolean);

Begin
If fAllowGrayed <> Bo then
   Begin
   fAllowGrayed := Bo;
   If not fAllowGrayed then
      If fState = cbGrayed then
         Begin
         If fChecked then
            fState := cbChecked
         else
            fState := cbUnChecked;
         End;
   Invalidate;
   End;
End;
{-------------------------------------------------------------------}
Procedure TCheckBoxX.fSetState(cbState : TState);

Begin
If fState <> cbState then
   Begin
   fState := cbState;
   If (fState = cbChecked) then
      fChecked := True;

   If (fState = cbGrayed) then
      fAllowGrayed := True;

   If fState = cbUnChecked then
      fChecked := False;

   Invalidate;
   End;
End;
{-------------------------------------------------------------------}
Procedure TCheckBoxX.fSetColor(C : TColor);

Begin
If fColor <> C then
   Begin
   fColor := C;
   Invalidate;
   End;
End;
{-------------------------------------------------------------------}
Procedure TCheckBoxX.fSetCaption(S : String);

Begin
If fCaption <> S then
   Begin
   fCaption := S;
   Invalidate;
   End;
End;
{-------------------------------------------------------------------}
Procedure TCheckBoxX.fSetChecked(Bo : Boolean);

Begin
If fChecked <> Bo then
   Begin
   fChecked := Bo;
   If fState <> cbGrayed then
      Begin
      If fChecked then
         fState := cbChecked
      else
         fState := cbUnChecked;
      End;
   Invalidate;
   End;
End;
{-------------------------------------------------------------------}
Procedure TCheckBoxX.Paint;

Var
   Buffer                        : Array[0..127] of Char;
   I                              : Integer;
   fTextWidth,fTextHeight        : Integer;

Begin
{Get Delphi's componentname and initially write it in the caption}
GetTextBuf(Buffer,SizeOf(Buffer));
If Buffer <> '' then
   fCaption := Buffer;

Canvas.Font.Size := Font.Size;
Canvas.Font.Style := Font.Style;
Canvas.Font.Color := Font.Color;
Canvas.Font.Charset := Font.CharSet;

fTextWidth := Canvas.TextWidth(fCaption);
fTextHeight := Canvas.TextHeight('Q');

If fAlignment = taRightJustify then
   Begin
   fBoxTop := (Height - fRBoxHeight) div 2;
   fBoxLeft := 0;
   fTextTop := (Height - fTextHeight) div 2;
   fTextLeft := fBoxLeft + fRBoxWidth + 4;
   End
else
   Begin
   fBoxTop := (Height - fRBoxHeight) div 2;
   fBoxLeft := Width - fRBoxWidth;
   fTextTop := (Height - fTextHeight) div 2;
   fTextLeft := 1;
   //If fTextWidth > (Width - fBoxWidth - 4) then
   //   fTextLeft := (Width - fBoxWidth - 4) -  fTextWidth;
   End;

// выводим текст в caption
Canvas.Pen.Color := fFont.Color;
Canvas.Brush.Color := fColor;
Canvas.TextOut(fTextLeft,fTextTop,fCaption);

// Рисуем контур фокуса
If fFocus = True then
   Canvas.DrawFocusRect(Rect(fTextLeft - 1,
                             fTextTop - 2,
                             fTextLeft + fTextWidth + 1,
                             fTextTop + fTextHeight + 2));

If (fState = cbChecked) then
   Canvas.Brush.Color := clWindow;

If (fState = cbUnChecked) then
   Canvas.Brush.Color := clWindow;

If (fState = cbGrayed) then
   Begin
   fAllowGrayed := True;
   Canvas.Brush.Color := clBtnFace;
   End;

// Создаём бокс clBtnFace когда кнопка мыши нажимается
// наподобие "стандартного" CheckBox
If fMouseState = msMouseDown then
   Canvas.Brush.Color := clBtnFace;

Canvas.FillRect(Rect(fBoxLeft + 2,
                     fBoxTop + 2,
                     fBoxLeft + fRBoxWidth - 2,
                     fBoxTop + fRBoxHeight - 2));

// Рисуем прямоугольный чекбокс
Canvas.Brush.Color := clBtnFace;
Canvas.Pen.Color := clGray;
Canvas.MoveTo(fBoxLeft + fRBoxWidth - 1,fBoxTop);
   Canvas.LineTo(fBoxLeft,fBoxTop);
   Canvas.LineTo(fBoxLeft,fBoxTop + fRBoxHeight);

Canvas.Pen.Color := clWhite;
Canvas.MoveTo(fBoxLeft + fRBoxWidth - 1,fBoxTop);
   Canvas.LineTo(fBoxLeft + fRBoxWidth - 1,
      fBoxTop + fRBoxHeight - 1);
   Canvas.LineTo(fBoxLeft - 1,fBoxTop + fRBoxHeight - 1);

Canvas.Pen.Color := clBlack;
Canvas.MoveTo(fBoxLeft + fRBoxWidth - 3,fBoxTop + 1);
   Canvas.LineTo(fBoxLeft + 1,fBoxTop + 1);
   Canvas.LineTo(fBoxLeft + 1,fBoxTop + fRBoxHeight - 2);

Canvas.Pen.Color := clBtnFace;
Canvas.MoveTo(fBoxLeft + fRBoxWidth - 2,fBoxTop + 1);
   Canvas.LineTo(fBoxLeft + fRBoxWidth - 2,
      fBoxTop + fRBoxHeight - 2);
   Canvas.LineTo(fBoxLeft,fBoxTop + fRBoxHeight - 2);

// Теперь он должен быть таким же как чекбокс в Delphi

If fChecked then
   Begin
   Canvas.Pen.Color := clBlack;
   Canvas.Brush.Color := clBlack;

   // Рисуем прямоугольник
   If fType = cbRect then
      Begin
      Canvas.FillRect(Rect(fBoxLeft + 4,fBoxTop + 4,
         fBoxLeft + fRBoxWidth - 4,fBoxTop + fRBoxHeight - 4));
      End;

   // Рисуем значёк "о"
   If fType = cbBullet then
      Begin
      Canvas.Ellipse(fBoxLeft + 4,fBoxTop + 4,
         fBoxLeft + fRBoxWidth - 4,fBoxTop + fRBoxHeight - 4);
      End;

   // Рисуем крестик
   If fType = cbCross then
      Begin
      {Right-top to left-bottom}
      Canvas.MoveTo(fBoxLeft + fRBoxWidth - 5,fBoxTop + 3);
         Canvas.LineTo(fBoxLeft + 2,fBoxTop + fRBoxHeight - 4);
      Canvas.MoveTo(fBoxLeft + fRBoxWidth - 4,fBoxTop + 3);
         Canvas.LineTo(fBoxLeft + 2,fBoxTop + fRBoxHeight - 3);
      Canvas.MoveTo(fBoxLeft + fRBoxWidth - 4,fBoxTop + 4);
         Canvas.LineTo(fBoxLeft + 3,fBoxTop + fRBoxHeight - 3);
      {Left-top to right-bottom}
      Canvas.MoveTo(fBoxLeft + 3,fBoxTop + 4);
         Canvas.LineTo(fBoxLeft + fRBoxWidth - 4,
            fBoxTop + fRBoxHeight - 3);
      Canvas.MoveTo(fBoxLeft + 3,fBoxTop + 3);
         Canvas.LineTo(fBoxLeft + fRBoxWidth - 3,
            fBoxTop + fRBoxHeight - 3);  //mid
      Canvas.MoveTo(fBoxLeft + 4,fBoxTop + 3);
         Canvas.LineTo(fBoxLeft + fRBoxWidth - 3,
            fBoxTop + fRBoxHeight - 4);
      End;

   // Рисуем галочку
   If fType = cbMark then
      For I := 0 to 2 do
         Begin
         {Left-mid to left-bottom}
         Canvas.MoveTo(fBoxLeft + 3,fBoxTop + 5 + I);
         Canvas.LineTo(fBoxLeft + 6,fBoxTop + 8 + I);
         {Left-bottom to right-top}
         Canvas.MoveTo(fBoxLeft + 6,fBoxTop + 6 + I);
         Canvas.LineTo(fBoxLeft + 10,fBoxTop + 2 + I);
         End;

   // Рисуем бриллиантик
   If fType = cbDiamond then
      Begin
      Canvas.Pixels[fBoxLeft + 06,fBoxTop + 03] := clBlack;
      Canvas.Pixels[fBoxLeft + 06,fBoxTop + 09] := clBlack;

      Canvas.MoveTo(fBoxLeft + 05,fBoxTop + 04);
      Canvas.LineTo(fBoxLeft + 08,fBoxTop + 04);

      Canvas.MoveTo(fBoxLeft + 05,fBoxTop + 08);
      Canvas.LineTo(fBoxLeft + 08,fBoxTop + 08);

      Canvas.MoveTo(fBoxLeft + 04,fBoxTop + 05);
      Canvas.LineTo(fBoxLeft + 09,fBoxTop + 05);

      Canvas.MoveTo(fBoxLeft + 04,fBoxTop + 07);
      Canvas.LineTo(fBoxLeft + 09,fBoxTop + 07);

      Canvas.MoveTo(fBoxLeft + 03,fBoxTop + 06);
      Canvas.LineTo(fBoxLeft + 10,fBoxTop + 06); // middle line
      End;
   End;
End;
{-------------------------------------------------------------------}
Procedure Register;

Begin
RegisterComponents('Samples', [TCheckBoxX]);
End;
{-------------------------------------------------------------------}
Destructor TCheckBoxX.Destroy;

Begin
inherited Destroy;
End;
{-------------------------------------------------------------------}
Constructor TCheckBoxX.Create(AOwner : TComponent);

Begin
inherited Create(AOwner);
Height := 17;
Width := 97;
fChecked := False;
fColor := clBtnFace;
fState := cbUnChecked;
fFont := inherited Font;
fAllowGrayed := False;
fFocus := False;
fMouseState := msMouseUp;
fAlignment := taRightJustify;
TabStop := True; // Sorry
End;
{-------------------------------------------------------------------}
End.
{===================================================================}