Link to home
Start Free TrialLog in
Avatar of PeterLarsen
PeterLarsen

asked on

Is this the right way to do it ?

I have the following structure in my sample program :

TFirstForm = class(Tform) //Application.MainForm
TSecondForm = class(Tform)
TThirdForm = class(TSecondForm) //inherited
TFourthForm = class(TSecondForm) //inherited

On TSecondForm i have placed a component - and this component is inherited along with others classes/components on TSecondForm.

When i change properties on the component i would like to know if i change properties on the ancestor or the inherited component.

This is what i have found :

In my component :

TMyComponent = class(tcomponent)
 private
  ....
 protected
  .....
 public
  procedure DefineProperties(Filer: TFiler); override;
 published
  ...
end;

implementation

procedure TMyComponent.DefineProperties(Filer: TFiler);
begin
if filer.Ancestor<>nil then
if Owner.InheritsFrom(tcomponent(filer.Ancestor).Owner.ClassType= true then
 ShowMessage('Inherited')
else
 ShowMessage('NOT Inherited');
end;

Can i trust this method - or is there a better way to do this ??

Regards
Peter
Avatar of Stefaan
Stefaan

Hi,

I see that I'm not the only one who is using the Form Inheritance in Delphi.  I've found that it is very good and it provides lots of Code-Reuse without rewriting it.

Now for your question, I'll try to give you some info on it.

Lets say you have put a TPanel on the TSecondForm (you didn't state which component you put there).  On the TSecondForm you set the folowing Properties for the TPanel :

Align   -> alTop
Caption -> 'Form Title'
Color   -> clTeal
Font    -> Times New Roman, Bold, Size 16 and Color Navy

Now you have created the inherited forms TThirdForm and TFourthForm which inherit from TSecond form.

As you will know the TThirdForm and TFourthForm will contain a TPanel with the same properties as the one you have created on TSecondForm (Same align, caption, color, font, ...).

To Explain how it works we'll leave the panel on TThirdForm the way it is as on TSecondForm.  On the TFouthForm we will change the color to clBtnFace.

If you now change the color of the panel on the TSecondForm to clYellow, you will see that the color will change on the TThirdForm automatically to clYellow, but on the TFourthForm it will stay to clBtnFace.

In fact the panel on TThirdForm and TFourthForm will be inherited from TSecondForm, and this will stay like that until you specify it yourself.  So as we changed the Color property of the Panel on the TFourthForm, Delphi will change the property on the TFourtForm but not on the TSecondForm (this will always remain the same, except if you change them on the TSecondForm itself).

In short, the properties of the Component you placed on TSecondForm will be inherited to TThirdForm and TFourtForm.  If you modify the properties of the component on TSecondForm the TThirdForm and TFourthForm will automatically inherit the new properties, unless you have specified the properties on the Descendant form itself (on TThirdForm or TFourthForm).  Changing the properties of the component on the Descendant form (TThirdForm and TFourtForm) will not change anything to the properties on the Ancestor form (TSecondForm), it will just make sure that the component doesn't use the inherited properties but the properties specified on the descendant form.

This all sounds difficult, but I hope my explanation will give you some more insight on the matter.  If you need more info on this subject, please post some further questions here or add a comment and I'll do my best to help you.

If you try it out using the same setup I explained above, you will see what I mean.

Best regards,


Stefaan
Avatar of PeterLarsen

ASKER

Hi Stefaan and thanks,

I have placed the component on TSecondForm.

I know it is like you explained and that is exactly my point/problem.

When the user change properties on the ancestor form it also change the descendant. But if the user change the properties on the descendant form the changes on the ancestor form will no longer be inherited.

In my component i have 2 TStringLists. One list for information made on the ancestor (global informations) and one list for information made on the descentans (local informations).

When the user change the lines in the component i would like the component to store the information in the locally or the globally stringlist.
To do this the component most know if the changes is made on the ancestor og the descentant.

By using the example above, the component can decide where to store the information.

My question is - is it possible to do this or is there a better way ?

Regards
Peter
Hi guys, good discussion.

Stefaan,
I also use form inheritance a lot and also find it *very* usefull, glad to see there is others:)

Peter,
If I understand your problem correctly you have a base form with two descendants: TSecondForm(Base), TThird and TFourthForm(inherrited). You also have 2 StringLists which store local and global data. Now, if a change is made to the StringList (global data) in TThirdForm you want those changes to be reflected in TFourthForm.
Is that correct?

Regards
Ben:)
Hi Ben,

Very close :-)

The 2 TStringList i'm talking about exist in a component. And this component is placed on TSecondForm.

Changes in the global TStringList may only occur in TSecondForm at design time.

So if i want to change the information in the global TStringlist (from the component on TFourthForm) and i want the changes to be inherited to all descentants, i have to do it through the ancestor component (on TSecondForm).

Like this :

Var
 AncestorComponent : TMyComponent;
.......
procedure TMyComponent.DefineProperties(Filer: TFiler);
begin
if filer.Ancestor<>nil then
if owner.InheritsFrom(tcomponent(filer.Ancestor).Owner.ClassType) = true then
 AncestorComponent:=Filer.Ancestor;
end;
.......
Procedure TMyComponent.SaveInGlobalList(......);
begin
AncestorComponent.GlobalTStringList.Assign(....);
AncestorComponent.Modified;
end;

As you say - it's very important not to change the properties on the descentant components because it's stop the changes on the ancestor from being inherited to it's descentants.

In my application i need some properties to have the same value on all components. Only some of the properties may be changed locally.

/Peter
This is getting more complicated than I thought ;-)

I tried using the IsStored function which can be found in the TypInfo unit, but with no luck until now.  The IsStored function should return True when the property is stored (thus not inherited anymore), but apparently it doesn't do what I want it to do.

I'll try a different approach tomorow to see if I can come up with anything.

Best regards,


Stefaan

P.S. : Karoo, glad to see you jump in.  We don't get a lot of discussions on this topic, since most people don't use this feature (I think).  It should be promoted a bit ;-)
Sorry, I meant the IsStoredProp function, not IsStored.
Thanks for your time :-)
Peter,

>>...change the properties on the descentant components...stop the changes on the ancestor from being inherited
yep.

Just for interest sake.
Followed the TSecondForm with descendants example with Memo1 and 3 strings on TSecondForm.

TSecondForm.dfm viewed as text look like this:
object SecondForm: TSecondForm
  ..some form properties..
  ..
  object Memo1: TMemo
    ..some memo properties..
    Lines.Strings = (
      'String1'
      'String2'
      'String3')
    TabOrder = 0
  end
end

//inherited TThirdForm and did not change any properties at design time
TThirdForm.dfm as text:
inherited ThirdForm: TThirdForm
  Caption = 'Form3'
  OldCreateOrder = True
  PixelsPerInch = 96
  TextHeight = 13
end

//inherrited TFourthForm and added one string to Memo1 at design time
TFourthForm.dfm as text:
inherited FourthForm: TFourthForm
  Caption = 'Form4'
  OldCreateOrder = True
  PixelsPerInch = 96
  TextHeight = 13
  inherited Memo1: TMemo
    Lines.Strings = (
      'String1'
      'String2'
      'String3'
      'String4')
  end
end

notice that the Memo1.Lines is copied for TFourthForm.
Now, i can still change the color property of Memo1 in TSecondForm and the change will be reflected in all the descendants, but changes to Lines will only be reflected in TThirdForm.

Played a bit with the csAncestor ComponentState property,
possible DesignTime sollution for you:
Created a descendant from TMemo and added a property MyLines, if the csDesigning is in the ComponentState then it is possible to check csAncestor.

type
  TMyMemo = class(TMemo)
  private
    FMyLines: TStrings;
  protected
    procedure SetMyLines(Value: TStrings);
  published
    property MyLines: TStrings read FMyLines write SetMyLines;
....
...
..

procedure TMyMemo.SetMyLines(Value: TStrings);
begin
  if not (csAncestor in ComponentState) then
    FMyLines.Assign(Value)
  else
    raise Exception.Create('Cannot update, created in ancestor');
end;

which means that this Memo would not allow the user to add MyLines at Design time if the component was inherrited.
does this help in any way?
still checking runtime sollutions.

hmmm, to throw a stone in the bush : do you guys think it is feasable to restrict properties based on whether or not the parent is inherrited?

Stefaan,
>>It should be promoted a bit
absolutely!, i've saved days on development and testing time by using Form inheritance.


best of luck
Ben:)
I have tried csAncestor and did not find it usefuld because the cdAncestor never is set - don't know why !!

  if csAncestor in self.ComponentState then
   t:=t+'csAncestor-';
  if csDesigning in self.ComponentState then
   t:=t+'csDesigning-';
  if csDestroying in self.ComponentState then
   t:=t+'csDestroying-';
  if csFixups in self.ComponentState then
   t:=t+'csFixups-';
  if csFreeNotification in self.ComponentState then
   t:=t+'csFreeNotification-';
  if csInline in self.ComponentState then
   t:=t+'csInline-';
   showmessage(t);

>>..hmmm, to throw a stone in the bush : do you guys think it is feasable to restrict properties based on whether or not the parent is inherrited?

Yes ...
In my example the user may set global settings and local settings in the TStringlists.
Whenever the user change the information on TSecondForm it is important that the information is inherited to its descentans.
But he may also choose to create/change some information locally.
An example of a setting could be : "OnlyMasterMayClose = Yes".
It's very much like INI files - Keys and Values.
The component is designed to make sure that a global setting don't exist as a local property (setting).

By doing it this way (having the TStringlists published but controlled), the properties is automatically saved in the program and i may control the settings from one point.

Regards
Peter
Peter,

csAncestor never set - very interesting

from delphi help - sure you've seen this:
csAncestor      Set if the component was introduced in an ancestor form. Only set if csDesigning is also set.

where do you check csAncestor? which method of your component?

regards
Ben.
Thats my problem with csAncestor. I dont know where to check for this.
I have tried different places, but with no luck.

/Peter
post some code on how you implemented the StringLists in your component.

bout my previous feasability q, ok - i understand now why you implement it in this way, seems like a pretty cool comp:)

Regards
Ben.
I will do that on monday. Happy new year.
Peter :-)
Hi,

Just to let you guys know, I did some further testing with my example.  I created a Form (TForm1 - Form1) which has a TPanel on it (Panel1).  I set the panel's Color property to clTeal.  I also added a button to the form.

Then I made a descendant form TForm2 which inherits from TForm1.  On that form I modified the Color property of the panel to clGreen.

On the Ancestor form I put the folowing code in the OnClick event handler of the TButton1 :

procedure TForm1.Button1Click(Sender: TObject);
begin
  if Self.Panel1.Color = Form1.Panel1.Color then
    showmessage ('Panel Color is the Inherited one')
  else
    showmessage ('Panel Color is another one')
end;

It seems to work.  If I cange the color of the TPanel on the TForm2 clicking on the button will tell me I specified another color than the one inherited from the TForm1.Panel1.  If it stays the same, then it tells me that it's the same.

This is kind of logic.  Since the code is written on TForm1.Button1's Click handler, the TForm2.Button1's click handler will be inherited too.  But if it will be executed from TForm2, the self will reference TForm2 and not TForm1.

You could achieve something similar with your component, you should modify the code to something like :

if Self.MyComponent.MyProperty = SecondForm.MyComponent.MyProperty then
begin
  { Add the information to the Global StringList }
end
else
begin
  { Add the Infromation to the Local String List }
end;

This should actually do the trick.

I hope this will be of some help to you.

Best regards, and a Happy New-Year.

Stefaan
Has some of you made any experience with inheriting of Components with TCollection properties?
Is there any documentation of streaming the properties of inherited forms?
Hi to u all,

Stefaan:
I think there is a problem in your code.
You have to put the form class in the uses clause of the component before it will work - right ??

You dont know where the component will be placed - so therefore you can't hardcode any class reference.

Prmcomp:
Don't know much about TCollection.
The Delphi help don't include much about streaming of properties. See TWriter and TReader in the help file or go to www.borland.com/devsupport/delphi/faq.

Ben:
If you like i can email the PAS files to you.

Regards
Peter
Peter,

eh007@usa.net

Regards
Ben.
Tanks, PeterLarsen. Delphi does some strange things while loading an inherited form the methods loaded and updated are not called like in a normal TForm instance.
Hi Ptmcomp,

I would love to answer your question, but i think you will get a bettet answer if you post the lines in a new question.

/PL
You're right I'll post a question. If a similar problem to solve. If a TDISQLTable which contains a TCollection with many cols (TCollectionItems). Now there are on diffrent inherited forms and I have always to change all components because the descands have more cols than the origin form.
Regards, ptm.
Hi Peter,

Eeu, I thought it was an inherited form you were talking about.  If you put the code I mentioned in the Ancestor Form, it should work.  Since the other two forms are descendants of the Ancestor form, they will already have a reference to the Ancestor form, no need to code it again.  Of course, the code I showed should be in the Ancestor form, not in one of the Descendants ! ! !

If you put the code in the Descendant forms, I don't think it will work.

Best regards,


Stefaan


P.S. : Sorry for the late replies, but I'm overloaded with work these days;
Hi Stefaan,

Thanks for your valuable time and sorry if you waste the time :-(

It is true that i'm talking about inherited forms. But it is in the component i would like to see who's the ancestor component and who's descentans.

If you look into my code sample in the beginning, you will notice that it is code for component writing :-)

Regards
Peter
Hi,

So you want to know if your component's porperties are the same as it's ancestors properties ?

If that is the case, you can use the same technique.

If on the other hand, you want to achieve something else, then I think I've missed something ;-)


Best regards,


Stefaan
I think we have finished the discussion here. If you dont agree with that, please let me know !!
ASKER CERTIFIED SOLUTION
Avatar of Stefaan
Stefaan

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Well, you spent a lot of time with this. And i would like this discussion to stay here.

Regards
Peter
Thanks for the points, but it wasn't necessary.

Anyway, if I can be of any assistance, you can contact me by e-mail at : Stefaan.Lesage@glo.be

Best regards,


Stefaan