CueText equivalent for a TMemo - delphi

I have this Delphi code to set the cue text of a control on my form:
procedure TfrmMain.SetCueText(edt: TWinControl; cueText: string);
const
ECM_FIRST = $1500;
EM_SETCUEBANNER = ECM_FIRST + 1;
begin
SendMessage(edt.Handle,EM_SETCUEBANNER,0,
LParam(PWideChar(WideString(cueText))));
end;
I want the same effect on a TMemo, but the MSDN document says:
You cannot set a cue banner on a
multiline edit control or on a rich
edit control.
Is there a standard way to have a cuetext effect on a TMemo, or do I have to fiddle with the OnEnter/OnExit events and roll my own?

You can hack the TMemo Control
TMemo With TextHint Single Line Version
type
TMemo = class(StdCtrls.TMemo)
private
FTextHint: string;
FTextHintFont: TFont;
protected
FCanvas : TCanvas;
procedure WMPaint(var Message: TWMPaint); message WM_PAINT;
public
constructor Create(AOwner : TComponent); override;
destructor Destroy; override;
published
property TextHint: string read FTextHint write FTextHint;
property TextHintFont: TFont read FTextHintFont write FTextHintFont;
end;
TForm1 = class(TForm)
Memo1: TMemo;
private
public
end;
constructor TMemo.Create(AOwner: TComponent);
begin
inherited;
FCanvas := TControlCanvas.Create;
FTextHintFont := TFont.Create;
FTextHintFont.Color := clGrayText;
TControlCanvas(FCanvas).Control := Self;
end;
destructor TMemo.Destroy;
begin
FreeAndNil(FTextHintFont);
FreeAndNil(FCanvas);
inherited;
end;
procedure TMemo.WMPaint(var Message: TWMPaint);
begin
inherited;
if (Text = '') and (not Focused) then
begin
FCanvas.Font := FTextHintFont;
FCanvas.TextOut(1, 1, FTextHint); //Note : is not multiline
end;
end;
To set the TextHint property
Memo1.TextHint:='Enter your comments here';
TMemo With TextHint MultiLine Version
type
TMemo = class(StdCtrls.TMemo)
private
FTextHint: TStrings;
FTextHintFont: TFont;
protected
FCanvas : TCanvas;
procedure WMPaint(var Message: TWMPaint); message WM_PAINT;
public
constructor Create(AOwner : TComponent); override;
destructor Destroy; override;
published
property TextHint: TStrings read FTextHint write FTextHint;
property TextHintFont: TFont read FTextHintFont write FTextHintFont;
end;
constructor TMemo.Create(AOwner: TComponent);
begin
inherited;
FTextHint := TStringList.Create;
FCanvas := TControlCanvas.Create;
FTextHintFont := TFont.Create;
FTextHintFont.Color := clGrayText;
TControlCanvas(FCanvas).Control := Self;
end;
destructor TMemo.Destroy;
begin
FreeAndNil(FTextHintFont);
FreeAndNil(FCanvas);
FTextHint.Clear;
FreeAndNil(FTextHint);
inherited;
end;
procedure TMemo.WMPaint(var Message: TWMPaint);
Var
i : integer;
TextHeight : Integer;
begin
inherited;
if (Text = '') and (not Focused) then
begin
FCanvas.Font := FTextHintFont;
TextHeight:=FCanvas.TextHeight('MLZ'); //Dummy Text to determine Height
for i := 0 to FTextHint.Count - 1 do
FCanvas.TextOut(1, 1+(i*TextHeight), FTextHint[i]);
end;
end;
Memo1.TextHint.Add('Enter your comments here Line 1');
Memo1.TextHint.Add('Enter your comments here Line 2');
Memo1.TextHint.Add('Enter your comments here Line 3');
Bye.

Related

Create a button that accepts .PNG images as Glyph

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;

Adding Canvas to TScrollBox

I am trying to do simple thing: Add a Canvas property on the TScrollBox descendant. I have read this article
but my ScrollBox descendant simply does not draw on the canvas. May anybody tell me, what is wrong?
TfrmScrollContainer = class (TScrollBox)
private
FCanvas: TCanvas;
FControlState:TControlState;
procedure WMPaint(var Message: TWMPaint); message WM_PAINT;
protected
procedure Paint;
procedure PaintWindow(DC: HDC); override;
property Canvas: TCanvas read FCanvas;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
end;
... this is exact copy, how TWincontrol turns to TCustomControl (but it fails somewhere)
constructor TfrmScrollContainer.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
FCanvas := TControlCanvas.Create;
TControlCanvas(FCanvas).Control := Self;
end;
destructor TfrmScrollContainer.Destroy;
begin
FCanvas.Free;
inherited Destroy;
end;
procedure TfrmScrollContainer.WMPaint(var Message: TWMPaint);
begin
Include(FControlState, csCustomPaint);
inherited;
Exclude(FControlState, csCustomPaint);
end;
procedure TfrmScrollContainer.PaintWindow(DC: HDC);
begin
FCanvas.Lock;
try
FCanvas.Handle := DC;
try
TControlCanvas(FCanvas).UpdateTextFlags;
Paint;
finally
FCanvas.Handle := 0;
end;
finally
FCanvas.Unlock;
end;
end;
procedure TfrmScrollContainer.Paint; // this is not executed (I do not see any ellipse)
begin
Canvas.Brush.Color:=clRed;
Canvas.Ellipse(ClientRect);
end;
Thanx
You are not including csCustomPaint to ControlState, you're including it to the similarly named field you declared. Your field is not checked, the ascendant control does not know anything about it. To solve, replace
procedure TfrmScrollContainer.WMPaint(var Message: TWMPaint);
begin
Include(FControlState, csCustomPaint);
inherited;
Exclude(FControlState, csCustomPaint);
end;
with
procedure TfrmScrollContainer.WMPaint(var Message: TWMPaint);
begin
ControlState := ControlState + [csCustomPaint];
inherited;
ControlState := ControlState - [csCustomPaint];
end;
Alternatively your scroll box may parent any control for your custom painting to work. The inherited WM_PAINT handler checks to see the control count and if it's not 0 it calls the paint handler instead of delivering the message to the default handler.

Delphi Button with Background Image

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 ... .

why Text clip in TEdit doesn't get changed when it is created first time [closed]

This question is unlikely to help any future visitors; it is only relevant to a small geographic area, a specific moment in time, or an extraordinarily narrow situation that is not generally applicable to the worldwide audience of the internet. For help making this question more broadly applicable, visit the help center.
Closed 10 years ago.
Hi I'm trying to build an TEdit control with TButton to have Buttoned Edit but the problem is that the text clip gets under the Button and some latter doesn't appear because the button is over it. how to fix that? please note that when I call UpdateEditMargins (which is the procedure to adjust the text clip)
Here is my code:
unit YazButtonedEdit;
interface
uses
SysUtils, Classes, Controls, StdCtrls, Buttons, Messages, Windows, Forms;
type
TYazButtonedEdit = class(TCustomEdit)
private
FEditButton: TBitBtn;
FButtonWidth: Integer;
FButtonVisible: Boolean;
procedure WMSize(var Message: TMessage); message WM_SIZE;
procedure SetButtonVisible(const Value: Boolean);
procedure GetEditButtonClick(const Value: TNotifyEvent);
function SetEditButtonClick: TNotifyEvent;
procedure SetButtonWidth(const Value: Integer);
protected
procedure RefreshButton;
procedure CreateParams(var Params: TCreateParams); override;
procedure WndProc(var Message: TMessage); override;
public
procedure Notification(AComponent: TComponent; Operation: TOperation); override;
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
procedure UpdateEditMargins;
published
property ButtonWidth: Integer read FButtonWidth write SetButtonWidth;
property ButtonVisible: Boolean read FButtonVisible write SetButtonVisible;
property OnEditButtonClick: TNotifyEvent read SetEditButtonClick write GetEditButtonClick;
end;
procedure Register;
implementation
procedure Register;
begin
RegisterComponents('KH-Controls', [TYazButtonedEdit]);
end;
{ TYazButtonedEdit }
constructor TYazButtonedEdit.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
ControlStyle := ControlStyle - [csSetCaption];
FEditButton := TBitBtn.Create(self);
with FEditButton do begin
Parent := self;
TabStop := false;
Visible := true;
OnClick := OnEditButtonClick;
end;
end;
procedure TYazButtonedEdit.CreateParams(var Params: TCreateParams);
begin
inherited CreateParams(Params);
Params.Style := Params.Style or WS_CLIPCHILDREN;
end;
destructor TYazButtonedEdit.Destroy;
begin
FEditButton.Free;
inherited;
end;
procedure TYazButtonedEdit.GetEditButtonClick(const Value: TNotifyEvent);
begin
FEditButton.OnClick := Value;
end;
procedure TYazButtonedEdit.RefreshButton;
begin
FEditButton.Width := ButtonWidth;
FEditButton.Height := Height - 4;
FEditButton.Visible := ButtonVisible;
UpdateEditMargins;
end;
procedure TYazButtonedEdit.SetButtonVisible(const Value: Boolean);
begin
if FButtonVisible <> Value then
begin
FButtonVisible := Value;
RefreshButton;
end;
end;
procedure TYazButtonedEdit.SetButtonWidth(const Value: Integer);
begin
if FButtonWidth <> Value then
begin
FButtonWidth := Value;
RefreshButton;
end;
end;
function TYazButtonedEdit.SetEditButtonClick: TNotifyEvent;
begin
Result := FEditButton.OnClick;
end;
procedure TYazButtonedEdit.WMSize(var Message: TMessage);
begin
RefreshButton;
end;
procedure TYazButtonedEdit.WndProc(var Message: TMessage);
var
LLeft, LTop: Integer;
begin
case Message.Msg of
CN_CTLCOLORSTATIC,
CN_CTLCOLOREDIT:
if FEditButton.Visible then
begin
LLeft := FEditButton.Left;
LTop := FEditButton.Top;
ExcludeClipRect(Message.WParam, LLeft + 1, LTop + 1,
FEditButton.Width + FEditButton.Left - 1, FEditButton.Height - 1);
end;
end;
inherited;
end;
procedure TYazButtonedEdit.UpdateEditMargins;
var
LMargin, RMargin: Integer;
begin
if HandleAllocated then
begin
LMargin := 0;
RMargin := 0;
if FEditButton.Visible then
LMargin := FEditButton.Width + 2;
SendMessage(Handle, EM_SETMARGINS, EC_LEFTMARGIN or EC_RIGHTMARGIN, MakeLong(LMargin, RMargin));
Invalidate;
end;
end;
procedure TYazButtonedEdit.Notification(AComponent: TComponent; Operation: TOperation);
begin
inherited Notification(AComponent, Operation);
if Operation = opRemove then
begin
if AComponent = FEditButton then
begin
RefreshButton;
end;
end;
end;
end.
Call it after the edit window is created, ideally in CreateWnd method. So, add the following:
type
TYazButtonedEdit = class(TCustomEdit)
...
protected
procedure CreateWnd; override;
...
end;
implementation
procedure TYazButtonedEdit.CreateWnd;
begin
inherited;
UpdateEditMargins;
end;

Prevent Delphi IDE creating component icons at design time

I have created a custom control TOuterControl that is the parent for multiple TInnerControls.
Everything is working fine except that the IDE is creating icons for each the child TInnerControl's (InnerControl1 and InnerControl2 in the screenshot). How do I prevent the IDE from generating the icons?
unit TestControl;
interface
Procedure Register;
implementation
Uses
Classes,
Controls,
SysUtils,
DesignEditors,
DesignIntf,
VCLEditors;
Type
TOuterControl = Class;
TInnerControl = Class(TComponent)
Protected
FOuterControl : TOuterControl;
function GetParentComponent: TComponent; Override;
Function HasParent : Boolean; Override;
procedure SetParentComponent (Value: TComponent); Override;
End;
TOuterControl = Class(TCustomControl)
Protected
FInnerControls : TList;
Procedure Paint; Override;
Public
Constructor Create(AOwner : TComponent); Override;
Procedure AddInnerControl(AInnerControl : TInnerControl);
procedure GetChildren(Proc: TGetChildProc; Root: TComponent); override;
End;
TOuterControlEditor = Class(TDefaultEditor)
Public
Procedure ExecuteVerb(Index : Integer); Override;
Function GetVerb (Index : Integer) : String; Override;
Function GetVerbCount : Integer; Override;
End;
procedure TOuterControl.AddInnerControl(AInnerControl: TInnerControl);
begin
AInnerControl.FOuterControl := Self;;
FInnerControls.Add(AInnerControl);
Invalidate;
end;
constructor TOuterControl.Create(AOwner: TComponent);
begin
inherited;
FInnerControls := TList.Create;
end;
procedure TOuterControl.GetChildren(Proc: TGetChildProc; Root: TComponent);
var
I : Integer;
begin
inherited;
For I := 0 To FInnerControls.Count - 1 Do
Proc(FInnerControls[I]);
end;
procedure TOuterControl.Paint;
begin
inherited;
Canvas.FillRect(ClientRect);
Canvas.TextOut(0,0, Format('Inner Control Count = %d', [FInnerControls.Count]));
end;
function TInnerControl.GetParentComponent: TComponent;
begin
Result := FOuterControl;
end;
function TInnerControl.HasParent: Boolean;
begin
Result := True;
end;
procedure TInnerControl.SetParentComponent(Value: TComponent);
begin
If Value Is TOuterControl Then
If FOuterControl <> Value Then
Begin
FOuterControl := TOuterControl(Value);
FOuterControl.AddInnerControl(Self);
End;
end;
procedure TOuterControlEditor.ExecuteVerb(Index: Integer);
Var
OuterControl : TOuterControl;
InnerControl : TInnerControl;
begin
inherited;
OuterControl := TOuterControl(Component);
If Index = 0 Then
Begin
InnerControl := TInnerControl.Create(OuterControl.Owner);
OuterControl.AddInnerControl(InnerControl);
End;
end;
function TOuterControlEditor.GetVerb(Index: Integer): String;
begin
Result := 'Add Inner';
end;
function TOuterControlEditor.GetVerbCount: Integer;
begin
Result := 1;
end;
Procedure Register;
Begin
RegisterComponents('AA', [TOuterControl]);
RegisterComponentEditor(TOuterControl, TOuterControlEditor);
End;
Initialization
Classes.RegisterClasses([TInnerControl]);
end.
You can prevent them from appeaing on the form with:
RegisterNoIcon([TInnerControl]);
More info on RegisterNoIcon can be found at http://docwiki.embarcadero.com/VCL/e/index.php/Classes.RegisterNoIcon
It's a little confusing having classes with a name that end with "Control" that aren't normal visual controls though.
If TInnerControl is meant to be used only inside a TOuterControl, then you should call SetSubComponent(True) during/after the TInnerControl's creation.
When you create the inner controls, you tell them that their owner is the form (the owner of the outer control). Therefore, the form draws them, just like it draws all the other components it owns. You probably want the outer control to own the inner ones.

Resources