views:

322

answers:

2

Hi,

I am trying to create a component like TButtonedEdit of Delphi 2009. It is a custom TEdit that has 2 buttons in the left and right.

In my version, I use 2 TSpeedButton objects for the left and right buttons.

Please take a look my simple code below.

It is ok to install it and I can see it in the component pallete.

But, for some unknown reason, I cannot save my application. As soon as I added the component, and start changing properties or writing event, Delphi will immediately quit (crashed?).

I dont know whats wrong ... but this is my first component which I am sure not right.

Could you please find out what are the problems?

It seems that Delphi 7.0 is having problem saving the .dfm if I use this component.

When I added this component to a form, save it, Delphi will ask to save "Unit1.pas", then immediately quit.

Thank you.

unit MyButtonedEdit;

interface

uses
  Windows, Buttons, Classes, Controls, Forms, Graphics, Messages, StdCtrls;

type
  TMyCustomButtonedEdit = class(TCustomEdit)
  private
     FLeftButton: TSpeedButton;
     FRightButton: TSpeedButton;

     procedure LeftButtonClick(Sender: TObject);
     procedure RightButtonClick(Sender: TObject);

     function  GetLeftGlyph: TBitmap;
     function  GetRightGlyph: TBitmap;

     procedure SetLeftGlyph(const g: TBitmap);
     procedure SetRightGlyph(const g: TBitmap);

  protected
     procedure CreateParams(var Params: TCreateParams); override;
     procedure DoLeftButtonClick; virtual; abstract;
     procedure DoRightButtonClick; virtual; abstract;
     function  GetEnabled: boolean; override;
     procedure SetEnabled(e: boolean); override;
     procedure WMSize(var Message: TWMSize); message WM_SIZE;
     property  LeftButton: TSpeedButton read FLeftButton write FLeftButton;
     property  RightButton: TSpeedButton read FRightButton write FRightButton;
     property  Enabled: boolean read GetEnabled write SetEnabled;
     property  LeftGlyph: TBitmap read GetLeftGlyph write SetLeftGlyph;
     property  RightGlyph: TBitmap read GetRightGlyph write SetRightGlyph;

  public
     constructor Create(AOwner: TComponent); override;
     destructor Destroy; override;
  published
  end;


  TMyButtonedEdit = class(TMyCustomButtonedEdit)
  private
     FOnLeftButtonClick: TNotifyEvent;
     FOnRightButtonClick: TNotifyEvent;
  protected
     procedure DoLeftButtonClick; override;
     procedure DoRightButtonClick; override;
  public
  published
     property LeftButton;
     property RightButton;
     property AutoSelect;
     property BorderStyle;
     property Color;
     property Ctl3d;
     property DragCursor;
     property DragMode;
     property Enabled;
     property Font;
     property LeftGlyph;
     property RightGlyph;
     property HideSelection;
     property ParentColor;
     property ParentCtl3D;
     property ParentFont;
     property ParentShowHint;
     property PopupMenu;
     property ReadOnly;
     property ShowHint;
     property TabOrder;
     property Text;
     property Visible;
     property OnLeftButtonClick: TNotifyEvent read FOnLeftButtonClick write FOnLeftButtonClick;
     property OnRightButtonClick: TNotifyEvent read FOnRightButtonClick write FOnRightButtonClick;       
     property OnChange;
     property OnClick;
     property OnDblClick;
     property OnDragDrop;
     property OnDragOver;
     property OnEndDrag;
     property OnEnter;
     property OnExit;
     property OnKeyDown;
     property OnKeyPress;
     property OnKeyUp;
     property OnMouseDown;
     property OnMouseMove;
     property OnMouseUp;
     property OnStartDrag;
  end;  

procedure Register;

implementation

procedure Register;
begin
   RegisterComponents('MyComponents',[TMyButtonedEdit]);
end;

{ TMyCustomButtonedEdit }

constructor TMyCustomButtonedEdit.Create(AOwner: TComponent);
begin
  inherited;
  ControlStyle := ControlStyle - [csSetCaption];

  FLeftButton := TSpeedButton.Create(self);
  with FLeftButton do begin
     Parent := self;
     TabStop := false;
     Visible := true;
     OnClick := LeftButtonClick;
  end;

  FRightButton := TSpeedButton.Create(self);
  with FRightButton do begin
     Parent := self;
     TabStop := false;
     Visible := true;
     OnClick := RightButtonClick;
  end;
end;

destructor TMyCustomButtonedEdit.Destroy;
begin
  FLeftButton.Free;
  FRightButton.Free;
  inherited;
end;

procedure TMyCustomButtonedEdit.CreateParams(var Params: TCreateParams);
begin
  inherited CreateParams(Params);
  Params.Style := Params.Style or WS_CLIPCHILDREN;
end;

function TMyCustomButtonedEdit.GetEnabled: boolean;
begin
  result := inherited Enabled;
end;

function TMyCustomButtonedEdit.GetLeftGlyph: TBitmap;
begin
  result := FLeftButton.Glyph;
end;

function TMyCustomButtonedEdit.GetRightGlyph: TBitmap;
begin
  result := FRightButton.Glyph;
end;

procedure TMyCustomButtonedEdit.LeftButtonClick(Sender: TObject);
begin
  DoLeftButtonClick;
  SetFocus;
end;

procedure TMyCustomButtonedEdit.RightButtonClick(Sender: TObject);
begin
  DoRightButtonClick;
  SetFocus;
end;

procedure TMyCustomButtonedEdit.SetEnabled(e: boolean);
begin
  inherited Enabled := e;
  FLeftButton.Enabled := e;
  FRightButton.Enabled := e;
end;

procedure TMyCustomButtonedEdit.SetLeftGlyph(const g: TBitmap);
begin
  FLeftButton.Glyph := g;
end;

procedure TMyCustomButtonedEdit.SetRightGlyph(const g: TBitmap);
begin
  FRightButton.Glyph := g;
end;

procedure TMyCustomButtonedEdit.WMSize(var Message: TWMSize);
var
  b: integer;
begin
  if (BorderStyle = bsSingle) and not Ctl3d then
     b := 1
  else
     b := 0;
  FLeftButton.Top    := b;
  FLeftButton.Height := ClientHeight - b * 2;
  FLeftButton.Width  := FLeftButton.Height;
  FLeftButton.Left   := b;

  FRightButton.Top    := b;
  FRightButton.Height := ClientHeight - b * 2;
  FRightButton.Width  := FRightButton.Height;
  FRightButton.Left   := ClientWidth - FRightButton.Width - b;
end;

{ TMyButtonedEdit }

procedure TMyButtonedEdit.DoLeftButtonClick;
begin
  inherited;
  if Assigned(FOnLeftButtonClick) then
    FOnLeftButtonClick(Self);
end;

procedure TMyButtonedEdit.DoRightButtonClick;
begin
  inherited;
  if Assigned(FOnRightButtonClick) then
    FOnRightButtonClick(Self);
end;

end.
A: 

It's a long time since I did any component development, but I seem to remember that the owner of a component should normally be the parent's owner, not the parent itself, so

 FLeftButton := TSpeedButton.Create(self);

should be:

 FLeftButton := TSpeedButton.Create( AOwner);
anon
+7  A: 

Your problem is you call to (Edit - I said Set but it is GET) get Enabled. Fittingly the error you are actually recieving before the crash is

Project Project1a.exe raised exception class EStackOverflow with message 
'Stack overflow'. Process stopped. Use Step or Run to continue.

You've got yourself into an infinite loop.

To debug components the best thing to do is create them at run time. This is much easier to debug then than trying at design time. To debug at run time I did this.

var
BE : TMyButtonedEdit;
begin
BE :=  TMyButtonedEdit.Create(self);
be.Parent := self;
be.Visible := true;

When I created your control it took a long time then I got the stackoverflow error which usually means an infinite loop. I still haven't quite figured out why but I'm working on that.

Solution.

You cannot inherit properties (well you can - see comments), so your call

inherited Enabled;

was actually calling itself. what you need to do is

 inherited GetEnabled;

Thanks for the mental workout.

Toby Allen
Same goes for the SetEnabled
Lars Truijens
Actually, you can call inherited properties. It's the only way to do it sometimes if the accessor is private, not protected. In this case, the problem is that the accessor is virtual. (Thankfully, you can't have private virtual methods, or this could be a real mess...) ;)
Mason Wheeler