I'm trying to understand how the SpeedButton Glyph property work, I find that the field declared as:
FGlyph: TObject;
While the property as:
property Glyph: TBitmap read GetGlyph write SetGlyph stored HasCustomGlyph;
That put me in a way where I can't understand that code even if I read it line by line, when I was trying to create my own SpeedButton that accepts .PNG images too instead of .bmp images only.
For the first time I was thinking to declare the property as TPicture instead of TBitmap.
Is there any way to create MySpeedButton with Glyph : TPicture?
What I try is below:
TMyButton = class(TSpeedButton)
private
//
FGlyph: TPicture;
procedure SetGlyph(const Value: TPicture);
protected
//
public
//
published
//
Property Glyph : TPicture read FGlyph write SetGlyph;
end;
And the procedure:
procedure TMyButton.SetGlyph(const Value: TPicture);
begin
FGlyph := Value;
end;
Your SetGlyph() needs to call FGlyph.Assign(Value) instead of FGlyph := Value. Be sure to create FGlyph in the constructor and destroy it in the destructor. Then you can call draw the graphic in an overriden Paint() when Graphic is not empty.
type
TMyButton = class(TGraphicControl)
private
FGlyph: TPicture;
procedure GlyphChanged(Sender: TObject);
procedure SetGlyph(const Value: TPicture);
protected
procedure Paint; override;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
published
property Glyph : TPicture read FGlyph write SetGlyph;
end;
constructor TMyButton.Create(AOwner: TComponent);
begin
inherited;
FGlyph := TPicture.Create;
FGlyph.OnChange := GlyphChanged;
end;
destructor TMyButton.Destroy;
begin
FGlyph.Free;
inherited;
end;
procedure TMyButton.GlyphChanged(Sender: TObject);
begin
Invalidate;
end;
procedure TMyButton.SetGlyph(const Value: TPicture);
begin
FGlyph.Assign(Value):
end;
procedure TMyButton.Paint;
begin
...
if (FGlyph.Graphic <> nil) and (not FGlyph.Graphic.Empty) then
Canvas.Draw(..., FGlyph.Graphic);
...
end;
I have created a similar component that is a SpeedButton which accepts a TPicture as its Glyph.
this is the unit. I hope you benefit well from it.
unit ncrSpeedButtonunit;
interface
uses
Winapi.Windows, Vcl.Controls, Winapi.Messages, Vcl.Graphics, System.Classes;
type
TButtonState = (bs_Down, bs_Normal, bs_Active);
TGlyphCoordinates = class(TPersistent)
private
FX: integer;
FY: integer;
FOnChange: TNotifyEvent;
procedure SetX(aX: integer);
procedure SetY(aY: integer);
function GetX: integer;
function GetY: integer;
public
procedure Assign(aValue: TPersistent); override;
published
property X: integer read GetX write SetX;
property Y: integer read GetY write SetY;
property OnChange: TNotifyEvent read FOnChange write FOnChange;
end;
TNCRSpeedButton = class(TGraphicControl)
private
FGlyph: TPicture;
FGlyphCoordinates: TGlyphCoordinates;
FColor: TColor;
FActiveColor: TColor;
FDownColor: TColor;
FBorderColor: TColor;
Fstate: TButtonState;
FFlat: boolean;
FTransparent: boolean;
procedure CMMouseEnter(var Message: TMessage); message CM_MOUSEENTER;
procedure CMMouseLeave(var Message: TMessage); message CM_MOUSELEAVE;
procedure CMMouseDown(var Message: TMessage); message WM_LBUTTONDOWN;
procedure CMMouseUp(var Message: TMessage); message WM_LBUTTONUP;
procedure SetGlyph(aGlyph: TPicture);
procedure SetGlyphCoordinates(aCoordinates: TGlyphCoordinates);
procedure SetColor(aColor: TColor);
procedure SetActiveColor(aActiveColor: TColor);
procedure SetDownColor(aDownColor: TColor);
procedure SetBorderColor(aBorderColor: TColor);
procedure SetFlat(aValue: boolean);
procedure GlyphChanged(Sender: TObject);
procedure CoordinatesChanged(Sender: TObject);
procedure SetTransparency(aValue: boolean);
protected
procedure Paint; override;
procedure Resize; override;
public
Constructor Create(Owner: TComponent); override;
Destructor Destroy; override;
published
property Glyph: Tpicture read FGlyph write SetGlyph;
property GlyphCoordinates: TGlyphCoordinates read FGlyphCoordinates write SetGlyphCoordinates;
property Color: TColor read FColor write SetColor;
property ActiveColor: TColor read FActiveColor write SetActiveColor;
property DownColor: TColor read FDownColor write SetDownColor;
property BorderColor: TColor read FBorderColor write SetBorderColor;
property Flat: boolean read FFlat write SetFlat;
property IsTransparent: boolean read FTransparent write SetTransparency;
property ParentShowHint;
property ParentBiDiMode;
property PopupMenu;
property ShowHint;
property Visible;
property OnClick;
property OnDblClick;
property OnMouseActivate;
property OnMouseDown;
property OnMouseEnter;
property OnMouseLeave;
property OnMouseMove;
property OnMouseUp;
end;
implementation
{ TNCRSpeedButton }
Constructor TNCRSpeedButton.Create(Owner: TComponent);
begin
inherited Create(Owner);
FGlyph := TPicture.Create;
FGlyph.OnChange := GlyphChanged;
FGlyphCoordinates := TGlyphCoordinates.Create;
FGlyphCoordinates.OnChange := CoordinatesChanged;
FState := bs_Normal;
FColor := clBtnFace;
FActiveColor := clGradientActiveCaption;
FDownColor := clHighlight;
FBorderColor := clBlue;
FFlat := False;
FTransparent := False;
SetBounds(0, 0, 200, 50);
end;
Destructor TNCRSpeedButton.Destroy;
begin
FGlyph.Free;
FGlyphCoordinates.Free;
inherited;
end;
procedure CreateMask(aCanvas: TCanvas; Area: TRect; aColor: Tcolor);
var
EBitmap, OBitmap: TBitmap;
begin
EBitmap := TBitmap.Create;
OBitmap := TBitmap.Create;
try
EBitmap.Width := Area.Width ;
EBitmap.Height := Area.Height;
EBitmap.Canvas.CopyRect(Area, aCanvas, Area);
OBitmap.Width := Area.Width;
OBitmap.Height := Area.Height;
OBitmap.Canvas.CopyRect(Area, aCanvas, Area);
OBitmap.Canvas.Brush.Color := aColor;
OBitmap.Canvas.Pen.Style := psClear;
OBitmap.Canvas.Rectangle(Area);
aCanvas.Draw(0, 0, EBitmap);
aCanvas.Draw(0, 0, OBitmap, 127);
finally
EBitmap.free;
OBitmap.free;
end;
end;
procedure DrawParentImage(Control: TControl; Dest: TCanvas);
var
SaveIndex: Integer;
DC: HDC;
Position: TPoint;
begin
with Control do
begin
if Parent = nil then
Exit;
DC := Dest.Handle;
SaveIndex := SaveDC(DC);
GetViewportOrgEx(DC, Position);
SetViewportOrgEx(DC, Position.x - Left, Position.y - Top, nil);
IntersectClipRect(DC, 0, 0, Parent.ClientWidth, Parent.ClientHeight);
Parent.Perform(WM_ERASEBKGND, DC, 0);
Parent.Perform(WM_PAINT, DC, 0);
RestoreDC(DC, SaveIndex);
end;
end;
procedure TNCRSpeedButton.Paint;
var
BackgroundColor: TColor;
begin
case FState of
bs_Down: BackgroundColor := FDownColor;
bs_Normal: BackgroundColor := FColor;
bs_Active: BackgroundColor := FActiveColor;
else
BackgroundColor := FColor;
end;
// Drawing Background
if not FTransparent then
begin
Canvas.Brush.Color := BackgroundColor;
Canvas.FillRect(ClientRect);
end
else
begin
case FState of
bs_Down:
begin
DrawParentImage(parent, Canvas);
CreateMask(Canvas, ClientRect, FDownColor);
end;
bs_Normal:
begin
DrawParentImage(parent, Canvas);
end;
bs_Active:
begin
DrawParentImage(parent, Canvas);
CreateMask(Canvas, ClientRect, FActiveColor);
end;
end;
end;
// Drawing Borders
Canvas.Pen.Color := FBorderColor;
Canvas.MoveTo(0, 0);
if not FFlat then
begin
Canvas.LineTo(Width-1, 0);
Canvas.LineTo(Width-1, Height-1);
Canvas.LineTo(0, Height-1);
Canvas.LineTo(0, 0);
end;
// Drawing the Glyph
if (FGlyph.Graphic <> nil) and (not FGlyph.Graphic.Empty) then
begin
Canvas.Draw(FGlyphCoordinates.X, FGlyphCoordinates.Y, FGlyph.Graphic);
end;
end;
procedure TNCRSpeedButton.GlyphChanged(Sender: TObject);
begin
if (FGlyph.Graphic <> nil) and (not FGlyph.Graphic.Empty) then
begin
FGlyphCoordinates.OnChange := nil; // Prevent multiple invalidates
FGlyphCoordinates.X := (Width - FGlyph.Graphic.Width) div 2;
FGlyphCoordinates.Y := (Height - FGlyph.Graphic.Height) div 2;
FGlyphCoordinates.OnChange := CoordinatesChanged;
end;
Invalidate;
end;
procedure TNCRSpeedButton.CoordinatesChanged(Sender: TObject);
begin
Invalidate;
end;
procedure TNCRSpeedButton.CMMouseEnter(var Message: TMessage);
begin
inherited;
FState := bs_Active;
Invalidate;
end;
procedure TNCRSpeedButton.CMMouseLeave(var Message: TMessage);
begin
inherited;
FState := bs_Normal;
Invalidate;
end;
procedure TNCRSpeedButton.CMMouseDown(var Message: TMessage);
begin
inherited;
FState := bs_Down;
Invalidate;
end;
procedure TNCRSpeedButton.CMMouseUp(var Message: TMessage);
begin
inherited;
FState := bs_Active;
Invalidate;
end;
procedure TNCRSpeedButton.SetGlyph(aGlyph: TPicture);
begin
FGlyph.Assign(aGlyph);
end;
procedure TNCRSpeedButton.Resize;
begin
if (FGlyph.Graphic <> nil) and (not FGlyph.Graphic.Empty) then
begin
FGlyphCoordinates.OnChange := nil; // Prevent multiple invalidates
FGlyphCoordinates.X := (Width - FGlyph.Graphic.Width) div 2;
FGlyphCoordinates.Y := (Height - FGlyph.Graphic.Height) div 2;
FGlyphCoordinates.OnChange := CoordinatesChanged;
end;
inherited;
end;
procedure TNCRSpeedButton.SetGlyphCoordinates(aCoordinates: TGlyphCoordinates);
begin
FGlyphCoordinates.assign(aCoordinates);
end;
procedure TNCRSpeedButton.SetColor(aColor: TColor);
begin
FColor := aColor;
Invalidate;
end;
procedure TNCRSpeedButton.SetActiveColor(aActiveColor: TColor);
begin
FActiveColor := aActiveColor;
Invalidate;
end;
procedure TNCRSpeedButton.SetDownColor(aDownColor: TColor);
begin
FDownColor := aDownColor;
Invalidate;
end;
procedure TNCRSpeedButton.SetBorderColor(aBorderColor: TColor);
begin
FBorderColor := aBorderColor;
Invalidate;
end;
procedure TNCRSpeedButton.SetFlat(aValue: boolean);
begin
FFlat := aValue;
Invalidate;
end;
procedure TNCRSpeedButton.SetTransparency(aValue: boolean);
begin
FTransparent := aValue;
Invalidate;
end;
{TGlyphCoordinates}
procedure TGlyphCoordinates.SetX(aX: integer);
begin
FX := aX;
if Assigned(FOnChange) then
FOnChange(self);
end;
procedure TGlyphCoordinates.SetY(aY: integer);
begin
FY := aY;
if Assigned(FOnChange) then
FOnChange(self);
end;
function TGlyphCoordinates.GetX: integer;
begin
result := FX;
end;
function TGlyphCoordinates.GetY: integer;
begin
result := FY;
end;
procedure TGlyphCoordinates.assign(aValue: TPersistent);
begin
if aValue is TGlyphCoordinates then begin
FX := TGlyphCoordinates(aValue).FX;
FY := TGlyphCoordinates(aValue).FY;
end else
inherited;
end;
end.
The first part is about how the Glyph property of TSpeedButton works, as you seem to be asking that as a part of your problem.
While TSpeedButton's FGlyph field is declared as an TObject, you will find that in code it actually contains an instance of TButtonGlyph.
In the TSpeedButton constructor you will find the line FGlyph := TButtonGlyph.Create;
and the setter and getter for the Glyph property of TSpeedButton look like this:
function TSpeedButton.GetGlyph: TBitmap;
begin
Result := TButtonGlyph(FGlyph).Glyph;
end;
procedure TSpeedButton.SetGlyph(Value: TBitmap);
begin
TButtonGlyph(FGlyph).Glyph := Value;
Invalidate;
end;
So TSpeedButton's Glyph property actually accesses the Glyph property of the TButtonGlyph class, an internal class defined in Vcl.Buttons, which encapsulates - among other things - the actual TBitMap with following property
property Glyph: TBitmap read FOriginal write SetGlyph;
So the TButtonGlyph has an TBitMap field FOriginal and the setter is implemented like this:
procedure TButtonGlyph.SetGlyph(Value: TBitmap);
var
Glyphs: Integer;
begin
Invalidate;
FOriginal.Assign(Value);
if (Value <> nil) and (Value.Height > 0) then
begin
FTransparentColor := Value.TransparentColor;
if Value.Width mod Value.Height = 0 then
begin
Glyphs := Value.Width div Value.Height;
if Glyphs > 4 then Glyphs := 1;
SetNumGlyphs(Glyphs);
end;
end;
end;
At this point it is important how accepts .PNG is defined:
Being able to use the PNG image, with some trade-offs.
Fully supports PNG images
For the latter I believe the answer of Remy Lebeau is the best advice. The internal class TButtonGylph makes OOP approaches like inheritance with png capable class impossible as far as I see. Or even go further and do as Remy suggests in a comment: third-party component.
If trade-offs are acceptable however:
Note the FOriginal.Assign(Value); which can already help in using PNGs, as TPNGImage's AssignTo procedure knows how to assign itself to a TBitMap.
With the above known about the Glyph property, we can simply assign a PNG with the following code:
var
APNG: TPngImage;
begin
APNG := TPngImage.Create;
try
APNG.LoadFromFile('C:\Binoculars.png');
SpeedButton1.Glyph.Assign(APNG);
finally
APNG.Free;
end;
Due to differences between bitmap and PNG this might however ignore alpha channel of the PNG, but based on an answer from Andreas Rejbrand there is a partial solution for that:
var
APNG: TPngImage;
ABMP: TBitmap;
begin
APNG := TPngImage.Create;
ABMP := TBitmap.Create;
try
APNG.LoadFromFile('C:\Binoculars.png');
ABMP.SetSize(APNG.Width, APNG.Height);
ABMP.Canvas.Brush.Color := Self.Color;
ABMP.Canvas.FillRect(Rect(0, 0, ABMP.Width, ABMP.Height));
ABMP.Canvas.Draw(0, 0, APNG);
SpeedButton1.Glyph.Assign(APNG);
finally
APNG.Free;
ABMP.Free;
end;
end;
Related
Below is a transparent panel vcl
it works.
but I hope to draw solid border(other parts are still transparent)
Is there any hint?
Your comment welcome
unit aframek;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
ExtCtrls;
type
TAframekStyle = (
gsBlackness, gsDstInvert, gsMergeCopy, gsMergePaint, gsNotSrcCopy,
gsNotSrcErase, gsPatCopy, gsPatInvert, gsPatPaint, gsSrcAnd,
gsSrcCopy, gsSrcErase, gsSrcInvert, gsSrcPaint, gsWhiteness);
TAframek = class(TCustomControl)
private
FColor: TColor;
FStyle: TAframekStyle;
FOnPaint: TNotifyEvent;
procedure SetColor(Value: TColor);
procedure SetStyle(Value: TAframekStyle);
procedure CMCtl3DChanged(var Message: TMessage); message CM_CTL3DCHANGED;
procedure WMEraseBkgnd(var Message: TMessage); message WM_ERASEBKGND;
procedure WMWindowPosChanging(var Message: TWMWindowPosChanging); message WM_WINDOWPOSCHANGING;
protected
Buffer: TBitmap;
procedure CreateParams(var Params: TCreateParams); override;
procedure Paint; override;
procedure Resize; override;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
property Canvas;
published
property Align;
property Anchors;
property AutoSize;
property BiDiMode;
property BorderWidth;
property Color: TColor read FColor write SetColor;
property Ctl3D;
property Enabled;
property Style: TAframekStyle read FStyle write SetStyle default gsSrcAnd;
property Visible;
property OnClick;
property OnDblClick;
property OnEnter;
property OnExit;
property OnMouseDown;
property OnMouseMove;
property OnMouseUp;
property OnResize;
property OnPaint: TNotifyEvent read FOnPaint write FOnPaint;
end;
procedure Register;
implementation
procedure Register;
begin
RegisterComponents('aka', [TAframek]);
end;
function AframekStyleToInt(gs: TAframekStyle): LongInt;
begin
case gs of
gsBlackness : Result := cmBlackness;
gsDstInvert : Result := cmDstInvert;
gsMergeCopy : Result := cmMergeCopy;
gsMergePaint : Result := cmMergePaint;
gsNotSrcCopy : Result := cmNotSrcCopy;
gsNotSrcErase: Result := cmNotSrcErase;
gsPatCopy : Result := cmPatCopy;
gsPatInvert : Result := cmPatInvert;
gsPatPaint : Result := cmPatPaint;
gsSrcAnd : Result := cmSrcAnd;
gsSrcCopy : Result := cmSrcCopy;
gsSrcErase : Result := cmSrcErase;
gsSrcInvert : Result := cmSrcInvert;
gsSrcPaint : Result := cmSrcPaint;
gsWhiteness : Result := cmWhiteness;
else Assert(True, 'Error parameter in function AframeStyleToInt');
end;
end;
constructor TAframek.Create(AOwner: TComponent);
var
FMarkBrush: LOGBRUSH;
FMarkPen: HPEN;
FPenStyle: array[0..1] of Integer;
FStartAngle: Single;
begin
inherited Create(AOwner);
Buffer := TBitmap.Create;
ControlStyle := [csAcceptsControls, csCaptureMouse, csClickEvents,
csDoubleClicks, csReplicatable];
Width := 100;
Height := 100;
FStyle := gsSrcAnd;
ParentCtl3d := False;
Ctl3D := False;
ParentColor := False;
FColor := clWhite;
end;
destructor TAframek.Destroy;
begin
Buffer.Free;
inherited Destroy;
end;
procedure TAframek.Paint;
var
R: TRect;
rop: LongInt;
begin
R := Rect(0, 0, Width, Height);
Buffer.Width := Width;
Buffer.Height := Height;
Buffer.Canvas.Brush.Style := bsSolid;
Buffer.Canvas.Brush.Color := FColor;
Buffer.Canvas.FillRect(Rect(0, 0, Width, Height));
rop := AframekStyleToInt(FStyle);
StretchBlt(Buffer.Canvas.Handle, 0, 0, Width, Height,
Canvas.Handle, 0, 0, Width, Height, rop);
if Ctl3D then DrawEdge(Buffer.Canvas.Handle, R, BDR_RAISEDINNER, BF_RECT);
Buffer.Canvas.Pen.Mode := pmCopy;
Buffer.Canvas.Pen.Style := psSolid;
Canvas.Draw(0, 0, Buffer);
if Assigned(FOnPaint) then FOnPaint(Self);
end;
procedure TAframek.SetColor(Value: TColor);
begin
if Value <> FColor then
begin
FColor := Value;
RecreateWnd;
end;
end;
procedure TAframek.CreateParams(var Params: TCreateParams);
begin
inherited CreateParams(Params);
Params.ExStyle := Params.ExStyle + WS_EX_TRANSPARENT;
end;
procedure TAframek.WMWindowPosChanging(var Message: TWMWindowPosChanging);
begin
Invalidate;
inherited;
end;
procedure TAframek.WMEraseBkgnd(var Message: TMessage);
begin
Message.Result := 0;
end;
procedure TAframek.Resize;
begin
Invalidate;
inherited;
end;
procedure TAframek.CMCtl3DChanged(var Message: TMessage);
begin
inherited;
RecreateWnd;
end;
procedure TAframek.SetStyle(Value: TAframekStyle);
begin
if Value <> FStyle then
begin
FStyle := Value;
RecreateWnd;
end;
end;
end.
Looks like you did not draw the border excepting the case Ctrl3D property is True. But it gets False in constructor :)
If you want to draw the border in your own way, try to call TCanvas.Rectangle or TCanvas.FrameRect in Paint method after TCanvas.FillRect.
I have Delphi 7 and now installed Delphi XE2.
I'm not really experienced with Design, VCL etc. but I would like to have a button (with Caption!) and a simple background image (PNG). I have 3 pictures of custom buttons (1 for click, 1 for mouseoff and 1 for mouseover). I have tried almost everything but I can't seem to find a way to have a simple button with caption in the middle and the images in the background. Please help.
PS.: The button should NOT visually go down on click (this is already in the png image.)
You might adapt this tiny component, no need to install for testing
Test
procedure TForm1.MyOnClick( Sender: TObject );
begin
ShowMessage( 'Hallo' );
end;
procedure TForm1.Button1Click( Sender: TObject );
begin
with TImageButton.Create( self ) do
begin
Parent := self;
Images := Imagelist1;
Index := 0;
HoverIndex := 1;
DownIndex := 2;
Caption := 'test';
OnClick := MyOnClick;
Width := Imagelist1.Width;
Height := Imagelist1.Height;
Font.Size := 12;
Font.Style := [fsBold];
end;
end;
And code
unit ImageButton;
// 2013 bummi
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
ExtCtrls, StdCtrls,ImgList;
Type
TState = (MouseIn, MouseOut, Pressed);
TImageButton = class(TGraphicControl)
private
FChangeLink:TChangeLink;
FImages: TCustomImageList;
FDownIndex: Integer;
FIndex: Integer;
FHoverIndex: Integer;
FState: TState;
FCaption: String;
FOwner: TComponent;
FAutoWidth: Boolean;
procedure CMMouseEnter(var Message: TMessage); message CM_MOUSEENTER;
procedure CMMouseLeave(var Message: TMessage); message CM_MOUSELEAVE;
procedure CMTextChanged(var Message: TMessage); message CM_TEXTCHANGED;
procedure CMFontChanged(var Message: TMessage); message CM_FONTCHANGED;
procedure WMLBUTTONDOWN(var Message: TMessage); message WM_LBUTTONDOWN;
procedure WMLBUTTONUP(var Message: TMessage); message WM_LBUTTONUP;
procedure SetDownIndex(const Value: Integer);
procedure SetHoverIndex(const Value: Integer);
procedure SetIndex(const Value: Integer);
procedure SetImages(const Value: TCustomImageList);
procedure SetCaption(const Value: String);
procedure ImagelistChange(Sender: TObject);
procedure SetAutoWidth(const Value: Boolean);
procedure CheckAutoWidth;
protected
procedure Paint; override;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; Override;
published
property AutoWidth:Boolean read FAutoWidth Write SetAutoWidth;
property Caption;
property DownIndex: Integer read FDownIndex Write SetDownIndex;
property Font;
property HoverIndex: Integer read FHoverIndex Write SetHoverIndex;
property Images: TCustomImageList read FImages write SetImages;
property Index: Integer read FIndex Write SetIndex;
End;
procedure Register;
implementation
procedure TImageButton.ImagelistChange(Sender:TObject);
begin
invalidate;
CheckAutoWidth;
end;
Constructor TImageButton.create(AOwner: TComponent);
begin
inherited Create(AOwner);
FOwner := AOwner;
FState := MouseOut;
Width := 200;
Height := 200;
FChangeLink:=TChangeLink.Create;
FChangeLink.OnChange := ImagelistChange;
end;
Destructor TImageButton.Destroy;
begin
if Assigned(FImages) then FImages.UnRegisterChanges(FChangeLink);
FChangeLink.Free;
inherited Destroy;
end;
procedure TImageButton.Paint;
var
ico: TIcon;
idx: Integer;
DestRect: TRect;
L_Caption: String;
begin
inherited;
idx := -1;
if Assigned(FImages) then
begin
case FState of
MouseIn:
if FImages.Count > HoverIndex then
idx := HoverIndex;
MouseOut:
if FImages.Count > Index then
idx := Index;
Pressed:
if FImages.Count > DownIndex then
idx := DownIndex;
end;
if idx > -1 then
try
ico := TIcon.create;
FImages.GetIcon(idx, ico);
Canvas.Draw(0, 0, ico);
finally
ico.Free;
end;
end
else
begin
Canvas.Rectangle(ClientRect);
end;
Canvas.Brush.Style := bsClear;
DestRect := ClientRect;
L_Caption := Caption;
Canvas.Font.Assign(Font);
Canvas.TextRect(DestRect, L_Caption, [tfVerticalCenter, tfCenter, tfSingleLine]);
end;
procedure TImageButton.CheckAutoWidth;
begin
if FAutoWidth and Assigned(FImages) then
begin
Width := FImages.Width;
Height := FImages.Height;
end;
end;
procedure TImageButton.SetAutoWidth(const Value: Boolean);
begin
FAutoWidth := Value;
CheckAutoWidth;
end;
procedure TImageButton.SetCaption(const Value: String);
begin
FCaption := Value;
Invalidate;
end;
procedure TImageButton.SetDownIndex(const Value: Integer);
begin
FDownIndex := Value;
Invalidate;
end;
procedure TImageButton.SetHoverIndex(const Value: Integer);
begin
FHoverIndex := Value;
Invalidate;
end;
procedure TImageButton.SetImages(const Value: TCustomImageList);
begin
if Assigned(FImages) then FImages.UnRegisterChanges(FChangeLink);
FImages := Value;
if Assigned(FImages) then
begin
FImages.RegisterChanges(FChangeLink);
FImages.FreeNotification(FOwner);
CheckAutoWidth;
end;
Invalidate;
end;
procedure TImageButton.SetIndex(const Value: Integer);
begin
FIndex := Value;
Invalidate;
end;
procedure TImageButton.WMLBUTTONDOWN(var Message: TMessage);
begin
inherited;
FState := Pressed;
Invalidate;
end;
procedure TImageButton.WMLBUTTONUP(var Message: TMessage);
begin
inherited;
FState := MouseIn;
Invalidate;
end;
procedure TImageButton.CMFontChanged(var Message: TMessage);
begin
Invalidate;
end;
Procedure TImageButton.CMMouseEnter(var Message: TMessage);
Begin
inherited;
if (csDesigning in ComponentState) then
Exit;
if FState <> MouseIn then
begin
FState := MouseIn;
Invalidate;
end;
end;
Procedure TImageButton.CMMouseLeave(var Message: TMessage);
Begin
inherited;
if (csDesigning in ComponentState) then
Exit;
if FState <> MouseOut then
begin
FState := MouseOut;
Invalidate;
end;
end;
procedure TImageButton.CMTextChanged(var Message: TMessage);
begin
invalidate;
end;
procedure Register;
begin
RegisterComponents('Own', [TImageButton])
end;
end.
Will respect transparencies if use with PNG and Imagelist cd32Bit
You can inherit from TBitBtn and override CN_DRAWITEM message handler - this will create a fully normal button with focus,with any pictures you need as a background and with all window messages that buttons need (see BM_XXX messages). You can also implement a virtual method to do other kinds of buttons with just this method overriden.
Something like that:
TOwnerDrawBtn = class(TBitBtn)
private
procedure CNDrawItem(var Message: TWMDrawItem); message CN_DRAWITEM;
procedure CMFocusChanged(var Message: TMessage); message CM_FOCUSCHANGED;
protected
procedure DrawButton(const DrawItemStruct: TDrawItemStruct); virtual;
end;
procedure TOwnerDrawBtn.CNDrawItem(var Message: TWMDrawItem);
begin
DrawButton(Message.DrawItemStruct^);
Message.Result := Integer(True);
end;
procedure TOwnerDrawBtn.CMFocusChanged(var Message: TMessage);
begin
inherited;
Invalidate;
end;
procedure TOwnerDrawBtn.DrawButton(const DrawItemStruct: TDrawItemStruct);
var
Canvas: TCanvas;
begin
Canvas := TCanvas.Create;
try
Canvas.Handle := DrawItemStruct.hDC;
//do any drawing here
finally
Canvas.Handle := 0;
Canvas.Free;
end;
end;
You Can Simply Use TJvTransparentButton from JEDI-Project JVCL .
With this component you can use single imagelist for all events and all other buttons , more events with image state , more style, Caption , Glyph, PressOffset and ... .
I'm trying to display a truly alpha blended TPanel in Delphi XE2. I've found quite a few attempts online, but none of them work correctly.
What I'm trying to achieve is a 'semi modal' form. A form that is displayed over the top of other controls with a faded background in a similar manner to that seen in web browsers.
I've got it working in a basic form, but it suffers from the following problems:
A large amount of flicker when resizing the panel.
If a control is moved over the top of the panel it leaves a trail.
Here's my efforts thus far (based on some code I found here).
unit SemiModalFormU;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, ExtCtrls;
type
ISemiModalResultHandler = interface
['{0CC5A5D0-1545-4257-A936-AD777E0DAFCF}']
procedure SemiModalFormClosed(Form: TForm);
end;
TTransparentPanel = class(TCustomPanel)
private
FBackground: TBitmap;
FBlendColor: TColor;
FBlendAlpha: Byte;
procedure ColorBlend(const ACanvas: TCanvas; const ARect: TRect; const ABlendColor: TColor; const ABlendValue: Byte);
procedure SetBlendAlpha(const Value: Byte);
procedure SetBlendColor(const Value: TColor);
protected
procedure CaptureBackground;
procedure Paint; override;
procedure WMEraseBkGnd(var msg: TWMEraseBkGnd); message WM_ERASEBKGND;
procedure WMMove(var Message: TMessage); message WM_MOVE;
procedure WMParentNotify(var Message: TWMParentNotify); message WM_PARENTNOTIFY;
public
constructor Create(aOwner: TComponent); override;
destructor Destroy; override;
procedure ClearBackground;
procedure SetBounds(ALeft, ATop, AWidth, AHeight: Integer); override;
published
property BlendColor: TColor read FBlendColor write SetBlendColor;
property BlendAlpha: Byte read FBlendAlpha write SetBlendAlpha;
property Align;
property Alignment;
property Anchors;
end;
TSemiModalForm = class(TComponent)
strict private
FFormParent: TWinControl;
FBlendColor: TColor;
FBlendAlpha: Byte;
FSemiModalResultHandler: ISemiModalResultHandler;
FForm: TForm;
FTransparentPanel: TTransparentPanel;
FOldFormOnClose: TCloseEvent;
private
procedure OnTransparentPanelResize(Sender: TObject);
procedure RepositionForm;
procedure SetFormParent(const Value: TWinControl);
procedure OnFormClose(Sender: TObject; var Action: TCloseAction);
protected
procedure Notification(AComponent: TComponent; Operation: TOperation); override;
public
procedure ShowSemiModalForm(AForm: TForm; SemiModalResultHandler: ISemiModalResultHandler); virtual;
property ModalPanel: TTransparentPanel read FTransparentPanel;
published
constructor Create(AOwner: TComponent); override;
property BlendColor: TColor read FBlendColor write FBlendColor;
property BlendAlpha: Byte read FBlendAlpha write FBlendAlpha;
property FormParent: TWinControl read FFormParent write SetFormParent;
end;
implementation
procedure TTransparentPanel.CaptureBackground;
var
canvas: TCanvas;
dc: HDC;
sourcerect: TRect;
begin
FBackground := TBitmap.Create;
with Fbackground do
begin
width := clientwidth;
height := clientheight;
end;
sourcerect.TopLeft := ClientToScreen(clientrect.TopLeft);
sourcerect.BottomRight := ClientToScreen(clientrect.BottomRight);
dc := CreateDC('DISPLAY', nil, nil, nil);
try
canvas := TCanvas.Create;
try
canvas.handle := dc;
Fbackground.Canvas.CopyRect(clientrect, canvas, sourcerect);
finally
canvas.handle := 0;
canvas.free;
end;
finally
DeleteDC(dc);
end;
end;
constructor TTransparentPanel.Create(aOwner: TComponent);
begin
inherited;
ControlStyle := controlStyle - [csSetCaption];
FBlendColor := clWhite;
FBlendAlpha := 200;
end;
destructor TTransparentPanel.Destroy;
begin
FreeAndNil(FBackground);
inherited;
end;
procedure TTransparentPanel.Paint;
begin
if csDesigning in ComponentState then
inherited
end;
procedure TTransparentPanel.SetBounds(ALeft, ATop, AWidth, AHeight: Integer);
begin
if (Visible) and
(HandleAllocated) and
(not (csDesigning in ComponentState)) then
begin
FreeAndNil(Fbackground);
Hide;
inherited;
Parent.Update;
Show;
end
else
inherited;
end;
procedure TTransparentPanel.WMEraseBkGnd(var msg: TWMEraseBkGnd);
var
ACanvas: TCanvas;
begin
if csDesigning in ComponentState then
inherited
else
begin
if not Assigned(FBackground) then
Capturebackground;
ACanvas := TCanvas.create;
try
ACanvas.handle := msg.DC;
ACanvas.draw(0, 0, FBackground);
ColorBlend(ACanvas, Rect(0, 0, Width, Height), FBlendColor, FBlendAlpha);
finally
FreeAndNil(ACanvas);
end;
msg.result := 1;
end;
end;
procedure TTransparentPanel.WMMove(var Message: TMessage);
begin
CaptureBackground;
end;
procedure TTransparentPanel.WMParentNotify(var Message: TWMParentNotify);
begin
CaptureBackground;
end;
procedure TTransparentPanel.ClearBackground;
begin
FreeAndNil(FBackground);
end;
procedure TTransparentPanel.ColorBlend(const ACanvas: TCanvas; const ARect: TRect;
const ABlendColor: TColor; const ABlendValue: Byte);
var
BMP: TBitmap;
begin
BMP := TBitmap.Create;
try
BMP.Canvas.Brush.Color := ABlendColor;
BMP.Width := ARect.Right - ARect.Left;
BMP.Height := ARect.Bottom - ARect.Top;
BMP.Canvas.FillRect(Rect(0,0,BMP.Width, BMP.Height));
ACanvas.Draw(ARect.Left, ARect.Top, BMP, ABlendValue);
finally
FreeAndNil(BMP);
end;
end;
procedure TTransparentPanel.SetBlendAlpha(const Value: Byte);
begin
FBlendAlpha := Value;
Paint;
end;
procedure TTransparentPanel.SetBlendColor(const Value: TColor);
begin
FBlendColor := Value;
Paint;
end;
{ TSemiModalForm }
constructor TSemiModalForm.Create(AOwner: TComponent);
begin
inherited;
FBlendColor := clWhite;
FBlendAlpha := 150;
FTransparentPanel := TTransparentPanel.Create(Self);
end;
procedure TSemiModalForm.SetFormParent(const Value: TWinControl);
begin
FFormParent := Value;
end;
procedure TSemiModalForm.ShowSemiModalForm(AForm: TForm;
SemiModalResultHandler: ISemiModalResultHandler);
begin
if FForm = nil then
begin
FForm := AForm;
FSemiModalResultHandler := SemiModalResultHandler;
FTransparentPanel.Align := alClient;
FTransparentPanel.BringToFront;
FTransparentPanel.Parent := FFormParent;
FTransparentPanel.BlendColor := FBlendColor;
FTransparentPanel.BlendAlpha := FBlendAlpha;
FTransparentPanel.OnResize := OnTransparentPanelResize;
AForm.Parent := FTransparentPanel;
FOldFormOnClose := AForm.OnClose;
AForm.OnClose := OnFormClose;
RepositionForm;
AForm.Show;
FTransparentPanel.ClearBackground;
FTransparentPanel.Visible := TRUE;
end;
end;
procedure TSemiModalForm.OnFormClose(Sender: TObject; var Action: TCloseAction);
begin
FForm.OnClose := FOldFormOnClose;
try
FForm.Visible := FALSE;
FSemiModalResultHandler.SemiModalFormClosed(FForm);
finally
FForm.Parent := nil;
FForm := nil;
FTransparentPanel.Visible := FALSE;
end;
end;
procedure TSemiModalForm.Notification(AComponent: TComponent;
Operation: TOperation);
begin
inherited Notification(AComponent, Operation);
if (Operation = opRemove) then
begin
if AComponent = FFormParent then
SetFormParent(nil);
end;
end;
procedure TSemiModalForm.OnTransparentPanelResize(Sender: TObject);
begin
RepositionForm;
end;
procedure TSemiModalForm.RepositionForm;
begin
FForm.Left := (FTransparentPanel.Width div 2) - (FForm.Width div 2);
FForm.Top := (FTransparentPanel.Height div 2) - (FForm.Height div 2);
end;
end.
Can anybody help me with the problems or point me to an alpha blend panel that already exists?
Thanks for all your suggestions. I've taken the input and created a new component that does exactly what I need. Here's what it looks like:
The comment that pointed me in the right direction was the one by NGLN that I upvoted. If you post it as the answer I'll accept it.
I tried to add the component code to this answer, but StackOverflow wouldn't format it correctly. However, you can download the source and a full demo application here.
The component provides the following functionality:
The semi modal form is a child of the main form. This means that it
can be tabbed to just like the other controls.
The overlay area is drawn correctly with no artefacts.
The controls under the overlay area are automatically disabled.
The semi modal form/overlay can be shown/hidden if required e.g.
switching tabs.
A SemiModalResult is passed back in an event.
There are still a number of small issues that I would like to iron out. If anybody knows how to fix them, please let me know.
When the parent form is moved or resized it needs to call the
ParentFormMoved procedure. This allows the component to
resize/reposition the overlay form. Is there any way to hook into
the parent form and detect when it is moved?
If you mimimise the main form, then restore it, the overlay form appears immediately, then the main form is animated back to it's previous position. Is there a way to detect when the main form has finished animating?
The rounded corners of the semi modal window are not too pretty. I'm
not sure there's much that can be done about this as it's down to the
rectangular region.
Your code does not show the form modally, and I wonder why you would not. But then, maybe I do not understand the term semi modal.
In any case, I think the idea to create a half-transparent form on which to show the actual dialog will do just fine:
function ShowObviousModal(AForm: TForm; AParent: TWinControl = nil): Integer;
var
Layer: TForm;
begin
if AParent = nil then
AParent := Application.MainForm;
Layer := TForm.Create(nil);
try
Layer.AlphaBlend := True;
Layer.AlphaBlendValue := 128;
Layer.BorderStyle := bsNone;
Layer.Color := clWhite;
with AParent, ClientOrigin do
SetWindowPos(Layer.Handle, HWND_TOP, X, Y, ClientWidth, ClientHeight,
SWP_SHOWWINDOW);
Result := AForm.ShowModal;
finally
Layer.Free;
end;
end;
Usage:
procedure TForm1.Button1Click(Sender: TObject);
begin
FDialog := TForm2.Create(Self);
try
if ShowObviousModal(FDialog) = mrOk then
Caption := 'OK';
finally
FDialog.Free;
end;
end;
I need to change the visual style of my delphi form controls inorder to show them from a .Net environment. To do this, I need to change the colors of delphi controls to blue ($00FCF5EE). I have used "TButton" controls widely which doesn't have a "Color" property.So, instead of changing the buttons to speed buttons, I have tried a different approach by introducing a parent form and inheriting all the other forms from this parent form. In the parent form, I have a class helper to change the color of buttons. Here is the code: (I am using Delphi 2007)
TButtonHelper=class helper for TButton
private
procedure doChangeColor;
public
procedure DrawChangeColor;
end;
TParentForm = class(TForm)
public
procedure AfterConstruction; override;
end;
And in the implementation section, I have
procedure TButtonHelper.doChangeColor;
var
SaveIndex: Integer;
FCanvas:TCanvas;
rect:TRect;
begin
if csDestroying in ComponentState then exit;
FCanvas:=TCanvas.Create;
SaveIndex := SaveDC(Self.Handle);
FCanvas.Lock;
try
FCanvas.Handle := Handle;
FCanvas.Font := Font;
FCanvas.Brush := self.Brush;
FCanvas.Brush.Color:=$00FCF5EE;
FCanvas.FillRect(BoundsRect);//Omitting the code to draw the text
finally
FCanvas.Handle := 0;
FCanvas.Unlock;
RestoreDC(handle, SaveIndex);
FCanvas.Free;
end;
end;
procedure TButtonHelper.DrawChangeColor;
begin
doChangeColor;
self.Repaint;
end;
procedure TParentForm.AfterConstruction;
var
i : Integer;
wc: TControl;
begin
inherited;
for i := 0 to self.ControlCount - 1 do begin
wc:=Controls[i];
if wc is TButton then
TButton(wc).DrawChangeColor;
end;
end;
But this doesn't work. Although the doChangeColor method is executed, it is not changing the color of the button.Please let me know what I am missing here.
Thanking you all,
Pradeep
here's a class that adds color properties to the standard TButton:
unit u_class_colorbutton;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, StdCtrls, Buttons, ExtCtrls;
type
TColorButton = class(TButton)
private
ShowBackColor : Boolean;
FCanvas : TCanvas;
IsFocused : Boolean;
FBackColor : TColor;
FForeColor : TColor;
FHoverColor : TColor;
procedure SetBackColor(const Value: TColor);
procedure SetForeColor(const Value: TColor);
procedure SetHoverColor(const Value: TColor);
protected
procedure CreateParams(var Params: TCreateParams); override;
procedure WndProc(var Message : TMessage); override;
procedure SetButtonStyle(Value: Boolean); override;
procedure DrawButton(Rect: TRect; State: UINT);
procedure CMEnabledChanged(var Message: TMessage); message CM_ENABLEDCHANGED;
procedure CMFontChanged(var Message: TMessage); message CM_FONTCHANGED;
procedure CNMeasureItem(var Message: TWMMeasureItem); message CN_MEASUREITEM;
procedure CNDrawItem(var Message: TWMDrawItem); message CN_DRAWITEM;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
published
property BackColor : TColor read FBackColor write SetBackColor default clBtnFace;
property ForeColor : TColor read FForeColor write SetForeColor default clBtnText;
property HoverColor: TColor read FHoverColor write SetHoverColor default clBtnFace;
end;
procedure Register;
implementation
constructor TColorButton.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
ShowBackColor := True;
FCanvas := TCanvas.Create;
BackColor := clBtnFace;
ForeColor := clBtnText;
HoverColor := clBtnFace;
end;
destructor TColorButton.Destroy;
begin
FreeAndNil(FCanvas);
inherited Destroy;
end;
procedure TColorButton.WndProc(var Message : TMessage);
begin
if (Message.Msg = CM_MOUSELEAVE) then
begin
ShowBackColor := True;
Invalidate;
end;
if (Message.Msg = CM_MOUSEENTER) then
begin
ShowBackColor := False;
Invalidate;
end;
inherited;
end;
procedure TColorButton.CreateParams(var Params: TCreateParams);
begin
inherited CreateParams(Params);
with Params do
Style := Style or BS_OWNERDRAW;
end;
procedure TColorButton.SetButtonStyle(Value: Boolean);
begin
if Value <> IsFocused then
begin
IsFocused := Value;
Invalidate;
end;
end;
procedure TColorButton.CNMeasureItem(var Message: TWMMeasureItem);
begin
with Message.MeasureItemStruct^ do
begin
itemWidth := Width;
itemHeight := Height;
end;
end;
procedure TColorButton.CNDrawItem(var Message: TWMDrawItem);
var
SaveIndex: Integer;
begin
with Message.DrawItemStruct^ do
begin
SaveIndex := SaveDC(hDC);
FCanvas.Lock;
try
FCanvas.Handle := hDC;
FCanvas.Font := Font;
FCanvas.Brush := Brush;
DrawButton(rcItem, itemState);
finally
FCanvas.Handle := 0;
FCanvas.Unlock;
RestoreDC(hDC, SaveIndex);
end;
end;
Message.Result := 1;
end;
procedure TColorButton.CMEnabledChanged(var Message: TMessage);
begin
inherited;
Invalidate;
end;
procedure TColorButton.CMFontChanged(var Message: TMessage);
begin
inherited;
Invalidate;
end;
procedure TColorButton.SetBackColor(const Value: TColor);
begin
if FBackColor <> Value then
begin
FBackColor:= Value;
Invalidate;
end;
end;
procedure TColorButton.SetForeColor(const Value: TColor);
begin
if FForeColor <> Value then
begin
FForeColor:= Value;
Invalidate;
end;
end;
procedure TColorButton.SetHoverColor(const Value: TColor);
begin
if FHoverColor <> Value then
begin
FHoverColor:= Value;
Invalidate;
end;
end;
procedure TColorButton.DrawButton(Rect: TRect; State: UINT);
var Flags, OldMode: Longint;
IsDown, IsDefault, IsDisabled: Boolean;
OldColor: TColor;
OrgRect: TRect;
NewCaption : string;
begin
NewCaption := Caption;
OrgRect := Rect;
Flags := DFCS_BUTTONPUSH or DFCS_ADJUSTRECT;
IsDown := State and ODS_SELECTED <> 0;
IsDisabled := State and ODS_DISABLED <> 0;
IsDefault := State and ODS_FOCUS <> 0;
if IsDown then Flags := Flags or DFCS_PUSHED;
if IsDisabled then Flags := Flags or DFCS_INACTIVE;
if (IsFocused or IsDefault) then
begin
FCanvas.Pen.Color := clWindowFrame;
FCanvas.Pen.Width := 1;
FCanvas.Brush.Style := bsClear;
FCanvas.Rectangle(Rect.Left, Rect.Top, Rect.Right, Rect.Bottom);
InflateRect(Rect, - 1, - 1);
end;
if IsDown then
begin
FCanvas.Pen.Color := clBtnShadow;
FCanvas.Pen.Width := 1;
FCanvas.Brush.Color := clBtnFace;
FCanvas.Rectangle(Rect.Left, Rect.Top, Rect.Right, Rect.Bottom);
InflateRect(Rect, - 1, - 1);
end
else
begin
DrawFrameControl(FCanvas.Handle, Rect, DFC_BUTTON, Flags);
end;
if IsDown then OffsetRect(Rect, 1, 1);
OldColor := FCanvas.Brush.Color;
if ShowBackColor then
FCanvas.Brush.Color := BackColor
else
FCanvas.Brush.Color := HoverColor;
FCanvas.FillRect(Rect);
FCanvas.Brush.Color := OldColor;
OldMode := SetBkMode(FCanvas.Handle, TRANSPARENT);
FCanvas.Font.Color := ForeColor;
if IsDisabled then
DrawState(FCanvas.Handle, FCanvas.Brush.Handle, nil, Integer(NewCaption), 0,
((Rect.Right - Rect.Left) - FCanvas.TextWidth(NewCaption)) div 2,
((Rect.Bottom - Rect.Top) - FCanvas.TextHeight(NewCaption)) div 2,
0, 0, DST_TEXT or DSS_DISABLED)
else
begin
InflateRect(Rect, -4, -4);
DrawText(FCanvas.Handle, PChar(NewCaption), - 1, Rect, DT_WORDBREAK or DT_CENTER);
end;
SetBkMode(FCanvas.Handle, OldMode);
if (IsFocused and IsDefault) then
begin
Rect := OrgRect;
InflateRect(Rect, - 4, - 4);
FCanvas.Pen.Color := clWindowFrame;
FCanvas.Brush.Color := clBtnFace;
DrawFocusRect(FCanvas.Handle, Rect);
end;
end;
procedure Register;
begin
RegisterComponents('Standard', [TColorButton]);
end;
initialization
RegisterClass(TColorButton); // needed for persistence at runtime
end.
You can hack it into your application easily:
find/replace all TButton references to TColorButton
inside your .pas and .dfm files.
You can set separate colors for background, font and hovering.
If you want add styling application wide, maybe it is better to create a GUI with a library that has native support like DevExpress, TMS, ...
Personally, I like DevExpress the most but that's a matter of personal taste.
How can I make the view like in the Yellow rectangle. Using TPanel + Color? If yes what about an indent of the text from the left?
Thanks for help and advices!
The simplest way is to use a TPanel. Set ParentBackground to false, BevelOuter to bvNone, Font.Color to clWhite, Font.Style to [fsBold] and the Color to the background colour you want. Then simply put one or two spaces in front of your text in the Caption property, like ' This is an ordinary TPanel.'.
A more elegant soution is to write a custom control. This is really easy. Example:
unit CaptionBar;
interface
uses
Windows, SysUtils, Classes, Controls, Graphics;
type
TCaptionBar = class(TCustomControl)
private
FColor: TColor;
FCaption: TCaption;
FEllipsis: boolean;
FIndent: integer;
procedure SetCaption(const Value: TCaption);
procedure SetColor(const Value: TColor);
procedure SetEllipsis(const Value: boolean);
procedure SetIndent(const Value: integer);
protected
procedure Paint; override;
public
constructor Create(AOwner: TComponent); override;
published
property Font;
property Anchors;
property Align;
property Caption: TCaption read FCaption write SetCaption;
property Color: TColor read FColor write SetColor default clSkyBlue;
property Ellipsis: boolean read FEllipsis write SetEllipsis default true;
property Indent: integer read FIndent write SetIndent default 4;
end;
procedure Register;
implementation
procedure Register;
begin
RegisterComponents('Rejbrand 2009', [TCaptionBar]);
end;
{ TCaptionBar }
constructor TCaptionBar.Create(AOwner: TComponent);
begin
inherited;
FIndent := 4;
FColor := clSkyBlue;
FEllipsis := true;
end;
procedure TCaptionBar.Paint;
const
Ellipsis: array[boolean] of cardinal = (0, DT_END_ELLIPSIS);
var
r: TRect;
begin
inherited;
Canvas.Brush.Color := FColor;
Canvas.FillRect(ClientRect);
r := ClientRect;
r.Left := r.Left + FIndent;
Canvas.Font.Assign(Font);
DrawText(Canvas.Handle,
PChar(FCaption),
length(FCaption),
r,
DT_SINGLELINE or DT_LEFT or DT_VCENTER or Ellipsis[FEllipsis]);
end;
procedure TCaptionBar.SetCaption(const Value: TCaption);
begin
if not SameStr(FCaption, Value) then
begin
FCaption := Value;
Invalidate;
end;
end;
procedure TCaptionBar.SetColor(const Value: TColor);
begin
if FColor <> Value then
begin
FColor := Value;
Invalidate;
end;
end;
procedure TCaptionBar.SetEllipsis(const Value: boolean);
begin
if FEllipsis <> Value then
begin
FEllipsis := Value;
Invalidate;
end;
end;
procedure TCaptionBar.SetIndent(const Value: integer);
begin
if FIndent <> Value then
begin
FIndent := Value;
Invalidate;
end;
end;
end.
Drop a Label into the panel, indented (set the Left property > 0) and set the panel color correctly.