I have created a component with TFrame as ancestor with the following code:
type
TCHAdvFrame = class(TFrame)
private
{ Private declarations }
FOnShow : TNotifyEvent;
FOnCreate : TNotifyEvent;
protected
procedure CMShowingChanged(var M: TMessage); message CM_SHOWINGCHANGED;
public
{ Public declarations }
constructor Create(AOwner: TComponent) ; override;
published
property OnShow : TNotifyEvent read FOnShow write FOnShow;
property OnCreate : TNotifyEvent read FOnCreate write FOnCreate;
end;
implementation
{$R *.dfm}
{ TCHAdvFrame }
procedure TCHAdvFrame.CMShowingChanged(var M: TMessage);
begin
inherited;
if Assigned(OnShow) then
begin
ShowMessage('onShow');
OnShow(self);
end;
end;
constructor TCHAdvFrame.Create(AOwner: TComponent);
begin
ShowMessage('OnCreate1');
inherited ;
ShowMessage('OnCreate2');
if Assigned(OnCreate) then
begin
ShowMessage('OnCreate3');
OnCreate(self);
end;
I have registered the new component and did some tests. ShowMessage('OnCreate1'); and ShowMessage('OnCreate2'); are correctly executed but not ShowMessage('OnCreate3');
This prevents to add code during the implementation of a new instance of TCHAdvFrame.
Why is it and how can I solve this ?
A frame is streamed in as part of its ultimate owner's constructor. Typically that will be a form. The form processes the .dfm file. It encounters new objects and creates them. Then it sets the properties of the newly created object. So, the frame's properties are set after its constructor returns.
This is the reason that TFrame does not have an OnCreate event. There is simply no way for the event to be fired because the event by necessity is assigned too late. The VCL designers omitted this event for the very same reason that led you to ask this question. So I do suspect that you likewise should not add this event.
How to solve this? Hard to say for sure unless we had a more detailed description of the problem. Perhaps you could override the frame's Loaded method to good effect. Or perhaps all you need to do is let consumers of your component override the constructor in their derived frames.
Related reading: http://delphi.about.com/od/delphitips2007/qt/tframe_oncreate.htm
Related
I'm trying to extend the collection "Buttons" of type "TcxEditButtons". The purpose is to add an "OnClick" event and a "Shortcut" property on all buttons of a legacy TcxButtonEdit component. I started by overwriting the "Properties" property with the code below:
type
TMycxDBButtonEditProperties = class(TcxCustomButtonEditProperties)
private
FButtons: TMycxEditButtons;
procedure SetButtons(const Value: TMycxEditButtons);
function GetButtons: TMycxEditButtons;
public
constructor Create(AOwner: TPersistent); override;
procedure AfterConstruction; override;
published
property Buttons: TMycxEditButtons read GetButtons write SetButtons;
end;
...
in my component i do this
...
type
TMycxDBButtonEdit = class(TcxCustomButtonEdit)
FProperties: TMycxDBButtonEditProperties;
published
property Properties: TMycxDBButtonEditProperties read FProperties write SetProperties;
The problem is somewhat obvious: the collection is available for editing, but does not reflect the actual ancestral property "Buttons". The question is: How do I make my collection affect the buttons on the component?
I tried to understand and apply what is described in the links below the support of Dev Express, but without success (incompetence)
https://www.devexpress.com/Support/Center/Question/Details/Q136143/creating-custom-tcxbuttonedit
https://www.devexpress.com/Support/Center/Question/Details/Q35461/do-you-have-information-on-creating-own-tcxcustomedit-descendant
https://www.devexpress.com/Support/Center/Question/Details/A483/how-to-hide-default-button-s-in-a-dropdown-editor-or-add-extra-buttons
Based on the #nil comment, I got the expected result. Below is the code snippet for those with the same type of need.
type
TZcxEditButton = class (TcxEditButton)
...
published
property Shortcut: TShortCut read FShortcut write SetShortcut;
property OnClick: TNotifyEvent read FOnClick write SetOnClick;
end;
type
TZcxEditButtons = class(TcxEditButtons)
public
class function GetButtonClass: TcxEditButtonClass; override;
end;
type
TZcxButtonEditProperties = class(TcxButtonEditProperties)
public
class function GetButtonsClass: TcxEditButtonsClass; override;
end;
type
TZcxButtonEdit = class(TcxButtonEdit)
public
class function GetPropertiesClass: TcxCustomEditPropertiesClass; override;
end;
implementation
class function TZcxEditButtons.GetButtonClass: TcxEditButtonClass;
begin
Result := TZcxEditButton;
end;
class function TZcxButtonEditProperties.GetButtonsClass: TcxEditButtonsClass;
begin
Result := TZcxEditButtons;
end;
class function TZcxButtonEdit.GetPropertiesClass: TcxCustomEditPropertiesClass;
begin
Result := TZcxButtonEditProperties;
end;
Note: I accept suggestions for improvement
You should extend your new class from main class TcxEditButton .
It is not a good way to extend Property Class TcxCustomButtonEditProperties.
So create a new class, extended from TcxEditButton and add your new Methods and property to it.
I want to override the OnPaint-Event of the TBCDProgressBar Component. It's a component for Lazarus (freepascal), I tagged Delphi on purpose because there are no differences when overriding events, and I want as much help as I can get.
TBCDProgressBar has no OnPaint event in its decleration:
TBCDProgressBar = class(TCDProgressBar)
private
FBCThemeManager: TBCThemeManager;
procedure SetFBCThemeManager(AValue: TBCThemeManager);
public
constructor Create(AOwner: TComponent); override;
published
property ThemeManager: TBCThemeManager read FBCThemeManager write SetFBCThemeManager;
end;
My first approach was to trace down TBCDProgressBar until I would find the OnPaint-Event.
Result was:
TCDProgressBar = class(TCDControl)
TCDControl = class(TCustomControl)
TCustomControl = class(TWinControl)
And finally in TCustomControl I found property OnPaint: TNotifyEvent read FOnPaint write FOnPaint;
I don't understand how I can override OnPaint from TCustomControl so that it affects the TBCDProgressBar.
Edit://
I didn't know you can override it as usual, so here is the solution for anyone having the same problem:
TBCDProgressBarWithOnPaint = class(TBCDProgressBar)
protected
procedure Paint; override;
end;
procedure TBCDProgressBarWithOnPaint.Paint;
begin
inherited;
// drawing a line on Progressbar
Canvas.Pen.Color:=clRed;
Canvas.Line(200,0,200,20);
end;
If it is visible (protected, public or published) and virtual, then you can override it no matter how far up the tree it is, just as if it were declared in TProgressBar.
how can i get dynamic values for my component's property variable
in my component i have a field named ColorDefault and i want to be able to set its value dynamically in program.
Original code
var // global
_V_TB_DefaultColor: TColor
type
TMyClass = class
...
property ColorDefault: tcolor read _V_TB_DefaultColor write FDefaultColor;
//[dcc32 Error] MyButton.pas(85): E2168 Field or method identifier expected
...
end;
Edit:
I did as below as tom described but color is not changing according to the global variable, color stays as when it is complied, for example my global color was clyellow and i complied my component and place it on the form and after that i changed the global color variable to clwhite and when i run the program it is still clyellow
type
TTestClass = class(TPanel)
private
{ Private declarations }
protected
{ Protected declarations }
FColorDefault:tcolor;
public
{ Public declarations }
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
function GetGlobalColorVariable:TColor;
published
{ Published declarations }
property DefaultColor:TColor read GetGlobalColorVariable write FColorDefault;
end;
procedure Register;
implementation
procedure Register;
begin
RegisterComponents('MyComponents', [TTestClass]);
end;
constructor TTestClass.Create(AOwner: TComponent);
begin
ColorInitiate;
inherited;
color:=DefaultColor;
end;
destructor TTestClass.Destroy;
begin
inherited;
end;
function TTestClass.GetGlobalColorVariable: TColor;
begin
result:=_V_TB_DefaultColor;
end;
end.
Looking at your question it seems that you are dealing with two problems.
First problem is how to access some global variable using property.
You can do this by using Getter method of your property like so:
type
TTestClass = class(TPanel)
private
{ Private declarations }
protected
{ Protected declarations }
public
{ Public declarations }
constructor Create(AOwner: TComponent); override;
function GetDefaultColor: TColor;
published
{ Published declarations }
property DefaultColor: TColor read GetDefaultColor;
end;
var // global
_V_TB_DefaultColor: TColor
implementation
function TTestClass.GetGlobalColorVariable: TColor;
begin
result := _V_TB_DefaultColor;
end;
That is like you did in your question edit. This will always make your DefaultColor property to return the same value as it is stored in your global _V_TB_DefaultColor variable.
But do note that this won't detect when _V_TB_DefaultColor variable was changed. So if you want to update your components after the change you need to execute some updating procedure for each of them yourself.
Also bare in mind that using global variables like this is not a good practice.
If you want certain property of all of your components to have the same value the it would be much better to declare that property as class property like in the code bellow.
type
TTestClass = class(TPanel)
private
{ Private declarations }
class var FDefaultColor: TColot;
protected
{ Protected declarations }
public
{ Public declarations }
constructor Create(AOwner: TComponent); override;
class function GetDefaultColor: TColor;
published
{ Published declarations }
class property DefaultColor: TColor read FDefaultColor write FDefaultColor;
end;
Now changing your DefaultColor in one component will change DefaultColor for all components of the same class. But bare in mind that you will still have to execute proper procedure for each of your components to update itself. That won't be done automatically.
Now your second problem is to detect the change of your DefaultColor variable and update your component/s accordingly.
Now if you use global variable there is no mechanism to detect this. But if you are using class procedure then you can at least write a setter method that will either execute update methods of all of your components that needs to be updated or send them necessary notification so they can perform necessary update by themselves.
How to implement this?
One way would be to loop through all your forms components checking their type and executing necessary update procedure. But that can be quite slow if you have lots of other components that you need to check if they are correct ones.
Another probably better approach would be adding your components on separate list so you don't need to be type checking as you know that such list only contains the right components.
You neeed to review the documentation about properties
http://docwiki.embarcadero.com/RADStudio/XE7/en/Properties
Your code
property ColorDefault:tcolor read _V_TB_DefaultColor write FDefaultColor;
is not approved by the compiler because _V_TB_DefaultColor is not a field or method of your class. The property should probably be declared as:
property ColorDefault:tcolor read FDefaultColor write FDefaultColor;
To set FDefaultColor equal to the global variable _V_TB_DefaultColor you need
MyClass.ColorDefault := _V_TB_DefaultColor;
at a suitable place in your code, f.ex in the constructor of your class.
Note, that to change the color of your component, you need to assign a new value to the ColorDefault property. Changing the value of your global variable _V_TB_DefaultColor will not automagically change the value of the property.
In your revised code you added
function TTestClass.GetGlobalColorVariable: TColor;
begin
result:=_V_TB_DefaultColor;
end;
That will not do anything if you dont assign the result of the function to something. Since the function is a member of TTestCalss I believe you want to
set the Color property directly in this function:
function TTestClass.GetGlobalColorVariable: TColor;
begin
Color:=_V_TB_DefaultColor;
end;
If this is the case, you can change it to a procedure because you dont use the return value:
procedure TTestClass.GetGlobalColorVariable;
begin
Color:=_V_TB_DefaultColor;
end;
thanks everyone,
with all the suggestions what i do is using a function to get the value from global variable and using windows messages as trigger to apply the new values to component
I need to write a TQuery descendant with an override on onBeforePost event where I should check the state if its dsInsert or dsEdit or not... and allow or not the post to proceed, but I need someone with experience with custom components to double-check it.
It's been long since I created my components and I'm a bit rusty. Can you take a look at what I got and tell me if I am doing it right?
Here is my code for the component
unit MxQuery;
interface
uses
SysUtils, Classes, DB, DBTables;
type
TMxQuery = class(TQuery)
procedure DoBeforePost; override;
private
{ Private declarations }
protected
{ Protected declarations }
public
{ Public declarations }
published
{ Published declarations }
constructor Create(AOwner:TComponent); override;
end;
procedure Register;
implementation
constructor TMxQuery.Create(AOwner:Tcomponent);
begin
inherited create(AOwner);
end;
procedure Register;
begin
RegisterComponents('Samples', [TMxQuery]);
end;
procedure TMxQuery.DoBeforePost;
begin
case self.DataSource.State of
dsEdit,dsInsert:
begin
//Do nothing or other stuff
end;
else
begin
self.DataSource.DataSet.Cancel;
Abort;
end;
end;
inherited;
end;
end.
Thanks
Imho, this question fits https://codereview.stackexchange.com/ much better.
Anyway, let me point something out:
Your code should be working.
As #SirRufo said, you unnecessarily raised visibility of method DoBeforePost to published. Look for this this method in declaration TQuery or one of its ancestors. Override the method with the same visibility found there.
Constructor TMxQuery.Create is not needed, as it does not add anything.
I have an assignment to create a game in Delphi and since the board will be the size 7x7 i wanted to do it by deriving my own component from TImage. Due to the fact that I wanted to have position of all tiles in the board in the array and I wanted to use Create method to do it but whenever I tried I have encountered EAccessViolation, while calling .Create(self)
Here is my component's code:
unit iles1;
interface
SysUtils, Classes, Controls, ExtCtrls;
type
Tiles1 = class(TImage)
private
FPlayer:Boolean; //determines whether it is an empty field or a player
FTeam:Boolean; //determines the team the tile belogns to
FBall:Boolean; //posession of the ball
{FBackLight : whether it is available to interact with this component,
with the method on click after one of the tiles has already been chosen,
if it is not lit but belongs to the same team, it is flagged as chosen
but not as lit, this field is used to determine whether i can pass a ball
to this direction or swap places with other player from the same team}
FBackLight:Boolean;
FChosen:Boolean; //whether the player decided to click on it
{FPostion determines where it is in a table, it ranges from
36 to 0 where its position divided by 10 determines the column
and position mod 10 determines the row}
FPosition:Byte;
{ Private declarations }
protected
{ Protected declarations }
public
constructor Create(AOwner : TComponent); override;
{ Public declarations }
published
property Team: boolean
read FTeam
write FTeam;
property Ball: boolean
read FBall
write FBall;
property Player:boolean
read FPlayer
write FPlayer;
property BackLight:boolean
read FBackLight
write FBackLight;
property Chosen:boolean
read FChosen
write FChosen;
property Position:byte
read FPosition
write FPosition;
end;
{ property Ball: Boolean;
//read FHasBall
//write FSetBall;
end;}
{ Published declarations }
procedure Register;
implementation
procedure Register;
begin
RegisterComponents('Samples', [Tiles1]);
end;
{Creator procedure calling the Timage creator
and setting parent to self(impossible here, then i will do it in
main window), visible to true
}
constructor Tiles1.Create(AOwner:TComponent);
begin
inherited;
FPlayer:=false;
FTeam:=false;
FBall:=false;
FBackLight:=false;
FChosen:=false;
FPosition:=0;
end;
end.
And here i have my main menu method that uses it:
procedure TForm1.FormCreate(Sender: TObject);
var x,y:Integer;
begin
for y:=1 to INAROW do begin
for x:=1 to INAROW do begin
tiles[x,y].Create(self);
tiles[x,y].Parent:=self;
tiles[x,y].Visible:=true;
tiles[x,y].Top:=(y-1)*(GAPBETWEEN+TILES1HEIGHT)+GAPTOP;
tiles[x,y].Left:=GAPLEFT+(x-1)*(GAPBETWEEN+TILES1WIDTH);
tiles[x,y].Width:=TILES1WIDTH;
tiles[x,y].Height:=TILES1HEIGHT;
tiles[x,y].Position:=10*x+y;
tiles[x,y].BackLight:=false;
tiles[x,y].Ball:=false;
tiles[x,y].Player:=false;
tiles[x,y].Chosen:=false;
end;
end;
setAlphaTeam;
setBetaTeam;
setTiles;
end;
tiles[X,Y] := Tiles1.Create(self);
assuming tiles is an array of Tiles1.
Constructors are effectively class methods, you call them on the class, not the instance.
You are getting an access violation because tiles[X,Y] is nil. If you commented out the create line, you'd get it trying to set the Parent Property.