Reputation: 491
I need to add 2 new properties to TSpeedButton. Although the properies are correctly displayed at object inspector and its values stored at DFM file, the "create" method at runtime keep getting the properties as "nil".
What´s wrong ?
Here is the customized component code:
unit ulbSpeedButton;
interface
uses
Winapi.Windows, Winapi.Messages, System.Classes, Vcl.Controls, Vcl.Forms, Vcl.Graphics,
Vcl.StdCtrls, Vcl.ExtCtrls, Winapi.CommCtrl, Vcl.ImgList,
Vcl.Themes, System.Generics.Collections, Vcl.Buttons;
type
tlbSpeedButton = class(TSpeedButton)
private
fImageList : TImageList;
fImageIndex : Integer;
function GetImageIndex:Integer;
function GetImageList:TImageList;
procedure SetImageIndex(aIndex:Integer);
procedure SetImageList(aImageList:TImageList);
protected
public
constructor Create(AOwner: TComponent); override;
published
property ImgIndex : Integer read fImageIndex write SetImageIndex;
property ImgList : TImageList read GetImageList write SetImageList;
end;
procedure Register;
implementation
procedure Register;
begin
RegisterComponents('Leo Bruno', [tlbSpeedButton]);
end;
{ tlbSpeedButton }
constructor tlbSpeedButton.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
if ((Assigned(fImageList)) and (fImageList.Count > 0)) then
fImageList.GetBitmap(fImageIndex,Self.Glyph);
end;
function tlbSpeedButton.GetImageIndex: Integer;
begin
Result := fImageIndex;
end;
function tlbSpeedButton.GetImageList: TImageList;
begin
Result := fImageList;
end;
procedure tlbSpeedButton.SetImageIndex(aIndex: Integer);
begin
if fImageIndex <> aIndex then
begin
fImageIndex := aIndex;
Invalidate;
end;
end;
procedure tlbSpeedButton.SetImageList(aImageList: TImageList);
begin
if fImageList <> aImageList then
begin
fImageList := aImageList;
Invalidate;
end;
end;
end.
Upvotes: 2
Views: 1227
Reputation: 597941
In addition to what KenWhite said, the two property setters should be updating the Glyph
(in case the properties need to be updated in code after DFM streaming, or even just at design-time). Just make sure to make them check the ComponentState
property for the csLoading
flag so they don't update the Glyph
during DFM streaming, since Loaded()
will handle that.
And don't forget to call FreeNotification()
on the assigned TImageList
, since it is external to the button and could potentially be freed before the button is freed.
Try this:
unit ulbSpeedButton;
interface
uses
Winapi.Windows, Winapi.Messages, System.Classes, Vcl.Controls, Vcl.Forms, Vcl.Graphics,
Vcl.StdCtrls, Vcl.ExtCtrls, Winapi.CommCtrl, Vcl.ImgList,
Vcl.Themes, System.Generics.Collections, Vcl.Buttons;
type
tlbSpeedButton = class(TSpeedButton)
private
fImageList : TCustomImageList;
fImageIndex : Integer;
procedure SetImageIndex(aIndex: Integer);
procedure SetImageList(aImageList: TCustomImageList);
procedure UpdateGlyph;
protected
procedure Loaded; override;
procedure Notification(AComponent: TComponent; Operation: TOperation); override;
public
constructor Create(AOwner: TComponent); override;
published
property ImgIndex : Integer read fImageIndex write SetImageIndex default -1;
property ImgList : TCustomImageList read fImageList write SetImageList;
end;
procedure Register;
implementation
procedure Register;
begin
RegisterComponents('Leo Bruno', [tlbSpeedButton]);
end;
{ tlbSpeedButton }
constructor tlbSpeedButton.Create(AOwner: TComponent);
begin
inherited;
fImageIndex := -1;
end;
procedure tlbSpeedButton.Loaded;
begin
inherited;
UpdateGlyph;
end;
procedure tlbSpeedButton.Notification(AComponent: TComponent; Operation: TOperation);
begin
inherited;
if (Operation = opRemove) and (AComponent = fImageList) then
begin
fImageList := nil;
UpdateGlyph;
end;
end;
procedure tlbSpeedButton.UpdateGlyph;
begin
if csLoading in ComponentState then Exit;
if Assigned(fImageList) and (fImageIndex >= 0) and (fImageIndex < fImageList.Count) then
fImageList.GetBitmap(fImageIndex, Self.Glyph)
else
Self.Glyph := nil;
Invalidate;
end;
procedure tlbSpeedButton.SetImageIndex(aIndex: Integer);
begin
if fImageIndex <> aIndex then
begin
fImageIndex := aIndex;
UpdateGlyph;
end;
end;
procedure tlbSpeedButton.SetImageList(aImageList: TImageList);
begin
if fImageList <> aImageList then
begin
if Assigned(fImageList) then fImageList.RemoveFreeNotification(Self);
fImageList := aImageList;
if Assigned(fImageList) then fImageList.FreeNotification(Self);
UpdateGlyph;
end;
end;
end.
Upvotes: 6
Reputation: 125749
You can't access the imagelist from the component's Create
event; it occurs before the other content has been streamed in from the .DFM file. The button has to be created before it's properties can be set, and the Create
event happens at that time.
You need to move your code that accesses the imagelist to an overridden Loaded
method instead, which happens after the entire content has been streamed in.
type
tlbSpeedButton = class(TSpeedButton)
private
fImageList : TImageList;
fImageIndex : Integer;
function GetImageIndex:Integer;
function GetImageList:TImageList;
procedure SetImageIndex(aIndex:Integer);
procedure SetImageList(aImageList:TImageList);
protected
procedure Loaded; virtual; override;
public
constructor Create(AOwner: TComponent); override;
published
property ImgIndex : Integer read fImageIndex write SetImageIndex;
property ImgList : TImageList read GetImageList write SetImageList;
end;
implementation
constructor Create(AOwner: TComponent);
begin
inherited;
end;
procedure TlbSpeedButton.Loaded;
begin
inherited;
if Asssigned(fImageList) and (fImageList.Count > 0) and
(fImageIndex > -1) then
fImageList.GetBitmap(fImageIndex, Self.Glyph);
end;
// The rest of your code
end;
Upvotes: 4