delphi component property dynamic value - delphi

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

Related

Extend items from the "Buttons" collection in "TcxButtonEdit" in Delphi VCL

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.

Code not executed in TFrame.Create

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

Delphi7Personal EAccessViolation while creating component derived from TImage

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.

Why use property in a class?

I was just wondering about why should I use property in a class instead of "normal" variables (class attributes?). What I mean is this:
TSampleClass = class
public
SomeInfo: integer;
end;
TPropertyClass = class
private
fSomeInfo: integer;
public
property SomeInfo: integer read fSomeInfo write fSomeInfo;
end;
What is the big difference? I know that I can define getter and setter methods for getting or saving the property respectively, but that is possible even without the variable being a "property".
I tried searching for why to use it, but nothing useful came up, so I'm asking here.
Thank you
This is just a very simple example of a specific case, but still, it is a very common case.
If you have a visual control, you might need to repaint the control when you change a variable/property. For instance, let's say your control has a BackgroundColor variable/property.
The simplest way of adding such a variable/property is to let it be a public variable:
TMyControl = class(TCustomControl)
public
BackgroundColor: TColor;
...
end;
And in the TMyControl.Paint procedure, you paint the background using the value of the BackgroundColor. But this doesn't do it. Because if you change the BackgroundColor variable of an instance of the control, the control doesn't repaint itself. Instead, the new background colour will not be used until the next time the control redraws itself for some other reason.
So you have to do it like this:
TMyControl = class(TCustomControl)
private
FBackgroundColor: TColor;
public
function GetBackgroundColor: TColor;
procedure SetBackgroundColor(NewColor: TColor);
...
end;
where
function TMyControl.GetBackgroundColor: TColor;
begin
result := FBackgroundColor;
end;
procedure TMyControl.SetBackgroundColor(NewColor: TColor);
begin
if FBackgroundColor <> NewColor then
begin
FBackgroundColor := NewColor;
Invalidate;
end;
end;
and then the programmer using the control has to use MyControl1.GetBackgroundColor to obtain the colour, and to use MyControl1.SetBackgroundColor to set it. That's awkward.
Using properties, you can have the best of both worlds. Indeed, if you do
TMyControl = class(TCustomControl)
private
FBackgroundColor: TColor;
procedure SetBackgroundColor(NewColor: TColor);
published
property BackgroundColor: TColor read FBackgroundColor write SetBackgroundColor;
end;
...
procedure TMyControl.SetBackgroundColor(NewColor: TColor);
begin
if FBackgroundColor <> NewColor then
begin
FBackgroundColor := NewColor;
Invalidate;
end;
end;
then
from the programmer's point of view, he can both read and set the background colour using a single identifier, the MyControl1.BackgroundColor property, and
the control is repainted when he sets it!
There are real-life advantages:
Properties can be changed to be read/write/read'n'write easily, without need to hassle with separate Getters and Setters all over the code;
Properties can be made public/published in child classes by just adding one line in initialization section;
Properties are more friendly when it comes to setting fields, compare "Label.Font.SetSize(14)" with "Label.Font.Size := 14", you can align ":=" with tabs/spaces and code will be much more readable;
EDIT: Another thing I thought of, properties force you to limit Get/Set methods to only 1 parameter, which is good for OOP. Compare that to some over-engineered functions:
GetItem(Index:integer; ForcedIndex:boolean=false):TItem //Forced index to get any value
GetItem(Index:integer; out Res:PItem):boolean //Result signals if out pointer is valid
I know that I can define getter and setter methods for getting or saving the property respectively, but that is possible even without the variable being a "property".
Well, no. Setters and getters are just normal methods that are called as such only once they are used as the read and write members of a property. Not having a property means not having a getter or a setter, even if they are named as such.
Furthermore; setters and getters are typically declared private or protected. So being able to call them when you use a public field instead of using a public property would require to move those methods to the public section.
Also, a big difference between fields and properties is the ability to be published and thus can be used in the object inspector. Fields (of other types then class or interface) can not be declared as published.
Properties can also be of great importance - or be usefull - in inheritance. Technically, you can't override a property, but you can mimic override in several ways. Some examples where property Name can be called from TDescendant, each with its own purpose:
1) Abstraction:
TBase = class(TObject)
protected
function GetName: String; virtual; abstract;
procedure SetName(const Value: String); virtual; abstract;
public
property Name: String read GetName write SetName;
end;
TDescendant = class(TBase)
private
FName: String;
protected
function GetName: String; override;
procedure SetName(const Value: String); override;
end;
2a) Protection (like Krom mentioned, ):
TBase = class(TObject)
private
FName: String;
function GetName: String;
procedure SetName(const Value: String);
protected
property Name: String read GetName write SetName;
end;
TDescendant = class(TBase)
public
property Name;
end;
2b)
TBase = class(TObject)
private
FName: String;
protected
function GetName: String;
procedure SetName(const Value: String);
end;
TDescendant = class(TBase)
public
property Name: String read GetName write SetName;
end;
By combinination of the above, you could change the behaviour of properties for descendant classes.
It is just a good programming practice to isolate the very "innards" of your class from the outside world. In addition, information about published properties are stored into RTTI generated for the class and can be accessed by their name, enumerated etc. This feature is used for example when reading a form from its serialized resource form.
One of main reason of using properties (regardless of it's more OO) is the validation of the input, for example if you need to limit the age of an employee class to be in valid range like 18..40
TEmp = class
private
FName: string;
FAge: Integer;
procedure SetAge(const Value: Integer);
procedure SetName(const Value: string);
published
property Name:string read FName write SetName;
property Age:Integer read FAge write SetAge;
end;
.....
procedure TEmp.SetAge(const Value: Integer);
begin
if not (Value in [18..40]) then
raise Exception.Create('Age must be between 18 and 40')
else
FAge := Value;
end;
You cant monitor the change in a variable without a property.
your read/writes for property dont have to be a variable they can be functions. And then you can manage the "onChange" of a property.
eg
TmyChange = procedure(Sender: Tobject) of object;
private
Fchange : TmyChange;
public
property SomeInfo: integer read getFoo write setFoo;
property onChange : TmyChange read Fchange write Fchange;
function getFoo : integer
begin
return localFoo;
end;
function setFoo (value : integer)
begin
// validate incoming value
localFoo=value;
if assigned(Fchange) then Fchange(self);
end;

How do I code a property with sub-properties? (redux)

I am sure that I got a good answer to my previous question because I have previously had a great deal of help on other questions from the guys who posted there.
But I am obviously doing something wrong, because when I copy the example code what the object inspector shows me for the MyProp property is a single text input field. I was expecting to see something that looks like the Font property, with Pitch, font family, etc i.e I expect to see a tree structure but I don't see the Color, Height or Width properties of the MyProp property.
Any ideas? Again, I copied that code exactly.
Edit: I forgot to mention (in this question) that I am using TMS scripter pro, which allows users to design forms at run time and provides its own object inspector, but that is probably derived from standard Delphi stuff, I guess.
Anyway, it appears that I am too dumb to code Delphi as I simply can't get this to work.
Edit: TMS assure me that if the class with "sub-properties) is descended from TPresistent then it will appear in the object inspector with sub-properties, just like Font, Anchors, etc
When I use this code, the "Warning" property appears as a text field in the object inspector and has no sub-properties
unit IntegerEditBox;
// An edit box which only accepts integer values and warns if the value is not in a certain range
interface
uses
SysUtils, Classes, Controls, StdCtrls,
EditBox_BaseClass;
type
TWarning = Class(TPersistent)
private
FWarningBelowValue : Integer;
FWarningAboveValue : Integer;
FWarningEmailTo : String;
FWarningSmsTo : String;
published
property WarningBelowValue : Integer read FWarningBelowValue write FWarningBelowValue;
property WarningAboveValue : Integer read FWarningAboveValue write FWarningAboveValue;
property WarningEmailTo : String read FWarningEmailTo write FWarningEmailTo;
property WarningSmsTo : string read FWarningSmsTo write FWarningSmsTo;
end;
TIntegerEditBox = class(TEditBox_BaseClass)
private
FWarning : TWarning;
procedure WriteValue(const newValue : Integer);
protected
// The new property which w/e introduce in this class
FValue : Integer;
public { Public declarations }
Constructor Create(AOwner: TComponent); override; // This constructor uses defaults
property Text;
published { Published declarations - available in the Object Inspector at design-time }
property Hint;
// Now our own properties, which we are adding in this class
property Value : Integer read FValue write WriteValue;
property Warning : TWarning read FWarning write FWarning ;
end; // of class TIntegerEditBox()
procedure Register;
implementation
uses
Dialogs;
procedure Register;
begin
RegisterComponents('Standard', [TIntegerEditBox]);
end;
Constructor TIntegerEditBox.Create(AOwner: TComponent);
begin
inherited; // Call the parent Create method
Hint := 'Only accepts a number|Only accepts a number'; // Tooltip | status bar text
Mandatory := True;
Value := 0;
Text := IntToStr(Value);
end;
procedure TIntegerEditBox.WriteValue(const newValue : Integer);
begin
Text := IntToStr(newValue);
end;
end.
The original version of the demo code neglected to create an instance of the property object.
constructor TMyControl.Create(AOwner: TComponent)
begin
inherited;
FMyProp := TCustomType.Create;
end;
Don't forget to free it in the destructor.
Remy's comment on that answer points out that the property needs to be assigned differently. The property's write accessor shouldn't write directly to the field. Instead, it should have a setter method that works like this:
procedure TMyControl.SetMyProp(const Value: TCustomType);
begin
FMyProp.Assign(Value);
end;
That also highlights the requirement that the property class's Assign method be implemented, or else you'll get strange error messages like "Cannot assign a TCustomType to a TCustomType." A simple implementation could go like this:
procedure TCustomType.Assign(Source: TPersistent);
begin
if Source is TCustomType then begin
Color := TCustomType(Source).Color;
Height := TCustomType(Source).Height;
Width := TCustomType(Source).Width;
end else
inherited;
end;

Resources