Recent

Author Topic: My own components - initializing proprties an events called at startup  (Read 4972 times)

Birger52

  • Sr. Member
  • ****
  • Posts: 309
Been getting acquainted with Lazarus.

So now getting back to my own components.
This simple thing, is a ownerdrawn TPanel, to use as a gauge - that can also function as a setter.
(attached mygauge.zip)

Functions as expected.

But I would like to f.ex. set bevels different than the default for the TPanel. I can do it in editor when component is placed - but would like to have it as a default, different from TPanels.
I would also like to remove the Caption (set it empty).
I have tried settng them in Create, but that doesn't work.
So how is the right way to do that?

I am also a bit puzzeled - propeties have read, write and default sections.
But the default seem to never be used?
In the attached there is a default value of 20 for position - but position is alwas 0 as default, when the component is placed in IDE.
So how does one change default values - and what does the default for a property actually do?


I created an application, to test. (Attached project1.zip)

There is an OnChanged event in the component.
And I'm getting odd errors with that...
Labels (on the form!) do not exist (=nil) and variables and properties in gauge are not set (access violations).
It seems the event is actually raised and attempted handled during creation of the gauge - and even before the other components on the form are created.
How does one avoid that?
In the attached zip, I test for the existense of the other components (well - one of them), and it works. But I think that shouldn't be nescessary.
So any explanation and/or suggestions on how to avoid that?
Lazarus 2.0.8 FPC 3.0.4
Win7 64bit
Playing and learning - strictly for my own pleasure.

Bart

  • Hero Member
  • *****
  • Posts: 5275
    • Bart en Mariska's Webstek
Re: My own components - initializing proprties an events called at startup
« Reply #1 on: September 15, 2018, 01:15:00 pm »
Set properties after inherited create.

Code: Pascal  [Select][+][-]
  1. constructor TMyComponent.Create(AOwner: TComponent);
  2. begin
  3.   inherited Create(AOwner);
  4.   BevelInner := bvRaised;

Publish properties that have another default value that it's ancestor like
Code: Pascal  [Select][+][-]
  1.   published
  2.     property BevelInner: TPanelBevel default bvRaised;

Bart

wp

  • Hero Member
  • *****
  • Posts: 11857
Re: My own components - initializing proprties an events called at startup
« Reply #2 on: September 15, 2018, 03:51:36 pm »
So now getting back to my own components.
This simple thing, is a ownerdrawn TPanel, to use as a gauge - that can also function as a setter.
(attached mygauge.zip)
I assume that you do this for practicing. Then it's ok. But if not, you should not re-invent the wheel. There are several ready-made gauge components somewhere in the internet, also for Lazarus, e.g. the one in the Industrial package which you can install via Online-Package-Manager.

One point to be mentioned: I think it is not a good idea to derive the new component from TPanel because TPanel can be a container for other components. It is probably not what you want that the user of your component will be able to add a TTreeView (just an example) to your component. I would derive it from TCustomControl or TWinControl, you would have to redo the sunken/raised border. But you have full control and don't have to work around properties inherited from the wrong ancestor.

I would also like to remove the Caption (set it empty).
I have tried settng them in Create, but that doesn't work.
So how is the right way to do that?
I think this is not done easily because - to my knowledge - the default Caption text is inserted by the property editor. So you must write your own property editor for this component which prevents its ancestor from doing so (http://wiki.lazarus.freepascal.org/How_To_Write_Lazarus_Component#Property_editors). Maybe it is easy here, but in general writing property editors is really advanced work with lots of trouble in debugging because the property editor is installed into the IDE, so, you must debug the IDE! Another reason for not inheriting from TPanel.

I am also a bit puzzeled - propeties have read, write and default sections.
But the default seem to never be used?
The default section is only important for the streaming process. It tells the reader/writer to not read/write a property value if the current value is equal to the default value - this way the size of the lfm file and the activity running when the component is read from the lfm is reduced. Another reason for adding a default value is when a new property is added to the component then you can keep the component usable in older versions of your package, at least unless the new property keeps its default value - because the property does not appear in the lfm.

Some properties cannot be declared as "default" (floating point value, strings, records). In this case you can appy the attribute "stored" and write a function like "IsPropertyStored" which returns false when the current property value should not be written to the lfm.

In the attached there is a default value of 20 for position - but position is alwas 0 as default, when the component is placed in IDE.
So how does one change default values - and what does the default for a property actually do?
Declaring a property with a default value says only that the property is not to be streamed when the property has the value specified after the "default", in the followin fragment 20:
Code: Pascal  [Select][+][-]
  1. type
  2.   TMyGaugePanel = class(...)
  3.     property GaugeWidth : integer read FGaugeWidth write SetGaugeWidth default 20;
But it does not set the default value. Therefore, applying the "default" word to the property declaration means always that you must also set the default value in the constructor of the component:
Code: Pascal  [Select][+][-]
  1. constructor TMyGaugePanel.Create(AOwner: TComponent);
  2. begin
  3.   inherited Create(AOwner);
  4.   ...
  5.   FGaugeWidth := 20;
  6. end

There is an OnChanged event in the component.
And I'm getting odd errors with that...
Labels (on the form!) do not exist (=nil) and variables and properties in gauge are not set (access violations).
It seems the event is actually raised and attempted handled during creation of the gauge - and even before the other components on the form are created.
How does one avoid that?
Although your component seems to work fine for me (I did not do thorough testing though), I could imagine that this can happen. Because when the lfm is read the properties are set to the values found in the lfm, and it may happen that the events associated with the setters are fired. Of course, it is possible that the other controls are not yet created when this occurs. A remedy is that you insert an "if (csLoading in ComponentState) then exit" into the Change procedure such that the event is not fired. For example:
Code: Pascal  [Select][+][-]
  1. procedure TMyGaugePanel.GaugeChange;
  2. begin
  3.   if csLoading in ComponentState then exit;
  4.   Refresh;
  5.   if Assigned(OnChange) then OnChange(Self);
  6. end;
 

howardpc

  • Hero Member
  • *****
  • Posts: 4144
Re: My own components - initializing proprties an events called at startup
« Reply #3 on: September 15, 2018, 04:40:51 pm »
As wp writes, you are better off not descending from TPanel.
Attached is a simplified version of your component which inherits from TCustomControl, and has a better implementation of OnChange.
It comes with a test project that instantiates the component at runtime. This is an essential precursor to installing the component in the IDE. Because if it misbehaves when instantiated dynamically, it will surely cause problems in the IDE, maybe locking it up or crashing Lazarus.So debug components first, before trying to install them.
To remove the Caption if it is set by default to the component name you can change a ControlStyle flag in the component's constructor
Code: Pascal  [Select][+][-]
  1. ControlStyle := ControlStyle - [csSetCaption];

Birger52

  • Sr. Member
  • ****
  • Posts: 309
Re: My own components - initializing proprties an events called at startup
« Reply #4 on: September 16, 2018, 03:03:50 pm »
@wp
Getting wiser is the main purpose ;)
Stopped back then when the prise for Delphi 3 from Borland was too high.
And a lot of things have changed (as far as I can remeber).

Thanks for clearing up my "default" mistake.

I changed ancestor to TCustomPanel.
So there are complaints about the bevels - but once cleared, it wors just fine.

I am having some troubles about the paint.
First off, I was using the Color propert of the panel.
It seems to exist in TCustom Panel also, but is not published somehow, and all my attempts to be the one to determine which color is to be used, seem to be ignored. (Compiles just fine, and shows fine in IDE - but not when the testprogram is run...)
Also tried to draw the bevels myself in paint - but it seems to ignore the white color...

Implemented the ComponentState in OnChange - and that also makes a difference. No more calls, until everything is loaded.
(A bit puzzeling - unless the csLoading in ComponentState is not cleared untill all components are loaded...)
(I had tried this, but with checking for csCreating - but that still give the errors).

Thank you for your input!!


@Bart
There are three numbers.
1) GaugeWith, which is the sice of the colored area in pixels.
2) A position, which gives or sets the size of the width in %.
3) A Value, and accompanying max and min values, that are user defined. This can also be read or set programmatically.
So your version may be "better" - but it does not do the same...
I did read your code.
Don't get the point of setting Canvas color when setting the Color property - it is changed in Paint anyway.
But using rect insted af coordinates, makes the code more readabel.

Thank you.

« Last Edit: September 16, 2018, 09:02:21 pm by Birger52 »
Lazarus 2.0.8 FPC 3.0.4
Win7 64bit
Playing and learning - strictly for my own pleasure.

Birger52

  • Sr. Member
  • ****
  • Posts: 309
Re: My own components - initializing proprties an events called at startup
« Reply #5 on: September 16, 2018, 09:18:41 pm »
TCustom Panel, does not allow for use of the regualr Color property.
I tried implementing - and there is a SetColor procedure.
Overwriting it, does not allow for setting my own FColor, tho. It works in IDE - but not at runtime...

And the bevel was a real mistery.
Left and top lines are drawn just fine, but right and bottom are not.
Canvas is apparently a lot bigger than the component it is placed in.
So
witth canvas do begin
....
end;
does not use the right values for Width and Height.

(Inconsistensy between doc - http://wiki.freepascal.org/Canvas_line and real life.
A line from (Width, 0) to (Width, Height) is drawn outside visible area as is on from (0, Height) to (Width,Height)
they need to be  (Width-1, 0) to (Width-1, Height-1) and (0, Height-1) to (Width-1,Height-1) respectively.)

« Last Edit: September 16, 2018, 10:02:19 pm by Birger52 »
Lazarus 2.0.8 FPC 3.0.4
Win7 64bit
Playing and learning - strictly for my own pleasure.

howardpc

  • Hero Member
  • *****
  • Posts: 4144
Re: My own components - initializing proprties an events called at startup
« Reply #6 on: September 17, 2018, 12:47:11 am »
(Inconsistensy between doc - http://wiki.freepascal.org/Canvas_line and real life.
A line from (Width, 0) to (Width, Height) is drawn outside visible area as is on from (0, Height) to (Width,Height)
they need to be  (Width-1, 0) to (Width-1, Height-1) and (0, Height-1) to (Width-1,Height-1) respectively.)
You have to remember that pixels are numbered starting at zero. So a control of Width 20 is 20 pixels wide, and the pixels are numbered from 0 to 19.
So drawing on pixel 20 (the value of Width) is drawing on a pixel that is just outside the control.
Hence the need to draw the line at the right edge of the control from (Width-1,0) to (Width-1, Height-1).
Likewise the left edge line is drawn from (0, 0) to (0, Height-1).
There is a coordinate system starting at 1, rather than 0. In that system the left edge would be (1,1) to (1,Height); and the right edge would be (Width, 1) to (Width, Height), and a line from (0, 0) to (0, Height) would be drawn outside the control.

Pascal uses the zero-based rather than the 1-based pixel numbering system.

Birger52

  • Sr. Member
  • ****
  • Posts: 309
Re: My own components - initializing proprties an events called at startup
« Reply #7 on: September 17, 2018, 08:44:09 am »
Yes I know about zero-based.
Was merely pointing out, that the one(s) who wrote the documentation aparently does not.
 ;)
 ;D
Lazarus 2.0.8 FPC 3.0.4
Win7 64bit
Playing and learning - strictly for my own pleasure.

howardpc

  • Hero Member
  • *****
  • Posts: 4144
Re: My own components - initializing proprties an events called at startup
« Reply #8 on: September 17, 2018, 09:42:52 am »
Unfortunately the Lazarus wiki is not documentation.
It contains information by myriads of people, some better informed than others, and it has no overall maintainer or gatekeeper who filters out incorrect or misleading statements.
Sometimes subsequent readers will add corrections... sometimes not.
If you see things that you know are wrong or misleading in the wiki, please correct them. It is unlikely anyone else will do so. Those who know what is correct are unlikely to look up the wiki for information they already know.

Birger52

  • Sr. Member
  • ****
  • Posts: 309
Re: My own components - initializing proprties an events called at startup
« Reply #9 on: September 17, 2018, 05:24:22 pm »
OK - I tough someone was looking over them.

Tried to change add comment, but I'm not allowed it says.

Where does one find documentation?
Lazarus 2.0.8 FPC 3.0.4
Win7 64bit
Playing and learning - strictly for my own pleasure.

wp

  • Hero Member
  • *****
  • Posts: 11857
Re: My own components - initializing proprties an events called at startup
« Reply #10 on: September 17, 2018, 05:50:32 pm »
You only must register to the wiki, then you can edit wiki articles by yourself.

I amended the articles that you referred to. In fact, this was a terrible article, in addition to what you noted it had typical beginners mistakes:
  • Never paint on a form in an OnClick event
  • Never use a form variable in the form's code
« Last Edit: September 17, 2018, 06:52:47 pm by wp »

Birger52

  • Sr. Member
  • ****
  • Posts: 309
Re:[S] My own components - initializing proprties an events called at startup
« Reply #11 on: September 19, 2018, 01:50:40 am »
Thanks for info.
Have created an account with wiki. ;)


« Last Edit: September 19, 2018, 01:57:13 am by Birger52 »
Lazarus 2.0.8 FPC 3.0.4
Win7 64bit
Playing and learning - strictly for my own pleasure.

 

TinyPortal © 2005-2018