constructor gets called twice
Hi,
I created a footer component for RAP. Basically when a user drops such a
footer on the report, it creates a TppRegion and 3 system variables and a
TppLabel in the TppRegion. The create is like this:
----------------------------------------------------------
constructor TRbFooter.Create(aOwner: TComponent);
begin
inherited Create(aOwner);
Self.Height := 0.25;
Self.Width :=8;
Self.Pen.Style := psClear;
CreateSystemVariablesAndTppLabel; --> gets called again
Self.ParentWidth := True;
end;
----------------------------------------------------------
It works fine in Design tab when drop the component on the report. But when
I switch to Preview and then switch back to Design, the Create constructor
gets called again and I get duplicated system variables and TppLabels.
Any idea about how to fix it?
I created a footer component for RAP. Basically when a user drops such a
footer on the report, it creates a TppRegion and 3 system variables and a
TppLabel in the TppRegion. The create is like this:
----------------------------------------------------------
constructor TRbFooter.Create(aOwner: TComponent);
begin
inherited Create(aOwner);
Self.Height := 0.25;
Self.Width :=8;
Self.Pen.Style := psClear;
CreateSystemVariablesAndTppLabel; --> gets called again
Self.ParentWidth := True;
end;
----------------------------------------------------------
It works fine in Design tab when drop the component on the report. But when
I switch to Preview and then switch back to Design, the Create constructor
gets called again and I get duplicated system variables and TppLabels.
Any idea about how to fix it?
This discussion has been closed.
Comments
1. When a report that includes RAP is previewed, the report definition is
saved to a temp stream - just before preview. When returning to the Design
workspace from Preview, then the report definition is loaded from the temp
stream so that is it restored to its prior state. This is necessary because
there is the possibility the RAP code modified the report layout while the
report was generating.
2. Your custom region component - like all RB components must be
persistent - be able to be read and written to/from a stream. RB uses
standard Delphi object peristence. This is used for the above and for saving
the report layout as part of a .dfm or to an .rtm file.
When your component is written to a stream - all the components are saved -
the Region, SystemVariable, Label etc. When your component is loaded from
stream, the constructor fires and creates additional SystemVariable and
Label components.
In your custom region class - overrde the public AfterDesignerCreate method
and call CreateSystemVariablesAndTppLabel from that method, rather than
from the constructor. The AfterDesignerCreate method is called by the report
designer, when the component is added to the report layout. (RB's
TppTeeChart components use this technique).
3. When you create the SystemVariables and Label, make sure that the Owner
is the same as the Owner of your component. And set the
SystemVariable.Region and Label.Region to be your customer component.
mySystemVariable.Region := TppSystemVariable.Create(Owner);
mySystemVariable.Region := Self;
Best regards,
Nard Moseley
Digital Metaphors
www.digital-metaphors.com
That works. Except one thing- I switch to Preview, then back to Design,
then back to Preivew again. Here I get a Access Violation error at
SaveReportState in procedure SetPreviewPageActive in ppDsgner.pas.
I know it is saving it to a stream. But why the first time saving to
stream works while the second time doesn't? Is it something related to my
component?
Looking forward to your help!
Thanks!
- One option is to post the source code to the entire custom component class
here and I can review it. I might be able to determine what is causing the
error from looking at your code
- The other option is create a simple, minimal example project that we can
run here in the Delphi debugger and send to support@digital-metaphors.com in
zip format
Best regards,
Nard Moseley
Digital Metaphors
www.digital-metaphors.com
//BEGIN OF COMPONENT------------------------------------------------------
unit rrRbFooter;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
inifiles, ExtCtrls,
ppRegion, ppClass, ppBands, ppSubRpt, ppTypes, ppVar, ppCtrls, ppcomm;
type
{ TrrRbFooter }
TrrRbFooter = class(TppRegion)
private
//FReportName: TppDBText;
FReportName: TppSystemVariable;
FPageNo: TppSystemVariable;
FReportSource: TppLabel;
FDateTime : TppSystemVariable;
FReportNameVisible : Boolean;
FPageNoVisible : Boolean;
FReportSourceVisible :Boolean;
FDateTimeVisible :Boolean;
FReportNameVarType :TppVarType;
FPageNoVarType :TppVarType;
FReportSourceCaption :String;
FDateTimeVarType :TppVarType;
FFooterFont :TFont;
FFooterFontSize :Integer;
FFontSynChronous:Boolean;
FOnGetText :TppGetTextEvent;
//SynchroFontTimer:TTimer;
function GetPageNovisible:Boolean;
Procedure SetPageNoVisible(Value:Boolean);
function GetReportNameVisible:Boolean;
Procedure SetReportNameVisible(Value:Boolean);
function GetReportSourceVisible:Boolean;
Procedure SetReportSourceVisible(Value:Boolean);
function GetDatetimeVisible:Boolean;
Procedure SetDateTimeVisible(Value:Boolean);
function GetReportNameVarType:TppVarType;
procedure SetReportNameVarType(Value:TppVarType);
function GetPageNoVarType:TppVarType;
procedure SetPageNoVarType(Value:TppVarType);
function GetDateTimeVarType:TppVarType;
procedure SetDateTimeVarType(Value:TppVarType);
function GetReportScCaption:String;
Procedure SetReportScCaption(Value:String);
function GetFooterFont:TFont;
procedure SetFooterFont(Value:TFont);
Procedure SetFooterFontSize(Value:Integer);
Procedure SetFontSynchronous(Value:Boolean);
procedure InitMmbObjs;
Procedure SetObjPosition(Sender: Tobject);
procedure OnObjGetText(Sender: TObject; var Text: String);
//Procedure OnSynChroFontTimer(Sender: TObject);
protected
//function GetCaption: String; override;
public
constructor Create(aOwner: TComponent); override;
destructor Destroy; override;
procedure Generate; override;
procedure AfterDesignerCreate; override;
Procedure ResetObjPosition;
procedure SynChroObjFont;
published
property ReportNameVisible : Boolean read GetReportNameVisible write
SetReportNameVisible default True;
property PageNoVisible : Boolean read GetPageNovisible write
SetPageNoVisible default True ;
property ReportSourceVisible :Boolean read GetReportSourceVisible
write SetReportSourceVisible default True;
property DateTimeVisible :Boolean read GetDatetimeVisible write
SetDateTimeVisible default True;
property ReportSourceCaption :String read GetReportScCaption write
SetReportScCaption ;
property Font :TFont read GetFooterFont write SetFooterFont;
property FooterFontSize :Integer read FFooterFontSize write
SetFooterFontSize Default 8;
property FontSynChronous:Boolean read FFontSynChronous write
SetFontSynChronous;
end;
implementation
{$R rrRbFooter.res}
{ TrrRbFooter.Create }
{------------------------------------------------------------------------------}
constructor TrrRbFooter.Create(aOwner: TComponent);
begin
inherited Create(aOwner);
Self.Height := 0.3;
self.BottomOffset:=0;
Self.Width :=8;
Self.Pen.Style := psClear;
Self.ParentWidth := True;
Self.FontSynChronous := True;
end;
{------------------------------------------------------------------------------}
{ TrrRbFooter.Destroy }
destructor TrrRbFooter.Destroy;
begin
try
if FReportName<>nil then
begin
FreportName.Free;
end;
if FPageNo<>nil then
begin
FPageNo.Free;
end;
if FReportSource<>nil then
begin
FReportSource.Free;
end;
if FDateTime<>nil then
begin
FDateTime.Free;
end;
if Assigned(FFooterFont) then
begin
FFooterFont.Free;
FFooterFont := nil;
end;
except
end;
inherited Destroy;
end;
procedure TrrRbFooter.Generate;
begin
inherited Generate;
end;
procedure TrrRbFooter.InitMmbObjs;
begin
//FReportName := TppDBText.Create(Self.Owner);
FReportName:=TppSystemVariable.Create(self.Owner);
FReportName.Region := Self;
//FReportName.DataPipeline:=FReportName.Band.Report.Template.DatabaseSettings.DataPipeline;
//FReportName.DataField:='Name';
FReportName.VarType := vtDocumentName;
FReportName.UserName:= 'ReportName';
FReportName.AutoSize := True;
FReportName.OnGetText := OnObjGetText;
//Self.AddObject(FReportName);
//self.Caption := FloatToStr(Self.Width);
FPageNo := TppsystemVariable.Create(Self.Owner);
FpageNo.Region := Self;
FpageNo.VarType := vtPageSet;
FPageNo.UserName :='PageNo';
FpageNo.AutoSize := True;
FPageNo.OnGetText := OnObjGetText;
//self.AddObject(FpageNo);
FReportsource := TppLabel.Create(Self.Owner);
FReportsource.Region := Self;
FReportsource.UserName :='Reportsource';
FReportsource.Caption := 'RentRight Property Management Software';
FReportsource.AutoSize := True;
FReportSource.OnGetText := OnObjGetText;
//Self.AddObject(FReportsource);
FDateTime := TppsystemVariable.Create(Self.Owner);
FDateTime.Region := Self;
FDateTime.VarType := vtDateTime;
FDateTime.UserName :='DateTime';
FDateTime.AutoSize := True;
FDateTime.OnGetText := OnObjGetText;
//Self.AddObject(FdateTime);
FDateTime.Width:= 1.06;
SetFooterFontSize(8);
end;
Procedure TrrRbFooter.ResetObjPosition;
begin
if FReportName<>nil then
SetObjPosition(FReportName);
if FPageNo<>nil then
SetObjPosition(FPageNo);
if FReportSource<>nil then
SetObjPosition(FReportsource);
if FDateTime<>nil then
SetObjPosition(FdateTime);
end;
Procedure TrrRbFooter.SetObjPosition(Sender: Tobject);
var
ObjTop:Single;
begin
if Self.Height>FReportName.Height then
ObjTop := Self.Top+(Self.Height-FReportName.Height)/2
else
ObjTop := Self.Top+0.01;
//if TppDBText(Sender)=FReportName then
if TppSystemVariable(Sender)=FReportName then
begin
FReportName.AutoSize := True;
FReportName.Top := ObjTop;
FReportName.Left:= Self.Left +0.01;
end
else if TppsystemVariable(Sender)=FPageNo then
begin
FpageNo.AutoSize := True;
FpageNo.Top := ObjTop;
FpageNo.Left:= (Self.Width-FpageNo.Width-self.Left)/2;
end
else if TppLabel(Sender)=FReportsource then
begin
FReportsource.AutoSize := True;
FReportsource.Top := ObjTop;
FReportsource.Left:= (Self.Width-FReportsource.Width-self.Left)*3/4;
end
else if TppsystemVariable(Sender)=FdateTime then
begin
FDateTime.AutoSize := True;
FDateTime.Top := ObjTop;
//FDateTime.Width:= 1.05;
FDateTime.Left:= Self.Width-FDateTime.Width-self.Left-0.01;
end;
end;
procedure TrrRbFooter.OnObjGetText(Sender: TObject; var Text: String);
begin
SetObjPosition(Sender);
end;
function TrrRbFooter.GetFooterFont:Tfont;
begin
if FFooterFont=nil then
FFooterFont := Tfont.Create;
Result := FFooterFont;
end;
procedure TrrRbFooter.SetFooterFont(Value:TFont);
begin
if FFooterFont=nil then
FFooterFont := Tfont.Create;
FFooterFont.Assign(Value);
end;
Procedure TrrRbFooter.SetFooterFontSize(Value:Integer);
begin
FFooterFontSize :=Value;
if FReportName<>nil then
FReportName.Font.Size := Value ;
if FPageNo<>nil then
FPageNo.Font.Size:= Value;
if FReportSource<>nil then
FReportSource.Font.Size := Value ;
if FDateTime<>nil then
FDateTime.Font.Size:= Value;
ResetObjPosition;
end;
procedure TrrRbFooter.SynChroObjFont;
begin
if FReportName<>nil then
FReportName.Font.Assign(Self.Font);
if FPageNo<>nil then
FpageNo.Font.Assign(Self.Font);
if FReportsource<>nil then
FReportSource.Font.Assign(Self.Font);
if FDateTime<>nil then
FDateTime.Font.Assign(Self.Font);
FooterFontSize:= Font.Size;
ResetObjPosition;
end;
Procedure TrrRbFooter.SetFontSynchronous(Value:Boolean);
begin
FFontSynChronous := Value;
if Value then
SynChroObjFont;
end;
function TrrRbFooter.GetPageNoVisible:Boolean;
begin
Result := FPageNo.Visible;
FPageNoVisible :=FPageNo.Visible;
end;
Procedure TrrRbFooter.SetPageNoVisible(Value:Boolean);
begin
FPagenovisible:= Value;
if FPageNo<>nil then
FPageNo.Visible := Value;
end;
function TrrRbFooter.GetReportNameVisible:Boolean;
begin
Result := FReportName.Visible;
FReportNameVisible :=FReportName.Visible;
end;
Procedure TrrRbFooter.SetReportNameVisible(Value:Boolean);
begin
FReportNameVisible:= Value;
if FReportName<>nil then
FReportName.Visible := Value;
end;
function TrrRbFooter.GetReportSourceVisible:Boolean;
begin
Result := FReportSource.Visible;
FReportSourceVisible :=FReportSource.Visible;
end;
Procedure TrrRbFooter.SetReportSourceVisible(Value:Boolean);
begin
FReportSourceVisible:= Value;
if FReportSource<>nil then
FReportSource.Visible := Value;
end;
function TrrRbFooter.GetDatetimeVisible:Boolean;
begin
Result := FDateTime.Visible;
FDateTimeVisible :=FDateTime.Visible;
end;
Procedure TrrRbFooter.SetDateTimeVisible(Value:Boolean);
begin
FDateTimeVisible:= Value;
if FDateTime<>nil then
FDateTime.Visible := Value;
end;
function TrrRbFooter.GetReportNameVarType:TppVarType;
begin
{ if FreportName<>nil then
FReportNameVarType := FReportName.VarType;
Result := FReportNameVarType; }
end;
procedure TrrRbFooter.SetReportNameVarType(Value:TppVarType);
begin
{ FReportNameVarType := Value;
if FReportName<>nil then
FReportName.VarType := FReportNameVarType; }
end;
function TrrRbFooter.GetPageNoVarType:TppVarType;
begin
if FpageNo<>nil then
FpagenoVarType := FPageNo.VarType;
Result := FpageNoVarType;
end;
procedure TrrRbFooter.SetPageNoVarType(Value:TppVarType);
begin
FPageNoVarType := Value;
if FPageNo<>nil then
FPageNo.VarType := FPageNoVarType;
end;
function TrrRbFooter.GetDateTimeVarType:TppVarType;
begin
if FDateTime<>nil then
FDateTimeVarType := FDateTime.VarType;
Result := FDateTimeVarType;
end;
procedure TrrRbFooter.SetDateTimeVarType(Value:TppVarType);
begin
FDateTimeVarType := Value;
if FDateTime<>nil then
FDateTime.VarType := FDateTimeVarType;
end;
function TrrRbFooter.GetReportScCaption:String;
begin
if FReportSource<>nil then
FReportSourceCaption := FReportSource.Caption
else
FReportSourceCaption :='';
Result := FReportSourceCaption ;
end;
Procedure TrrRbFooter.SetReportScCaption(Value:String);
begin
FReportSourcecaption := Value;
if FReportSource<>nil then
begin
FReportSource.Caption := Value;
SetobjPosition(FReportsource);
end;
end;
procedure TrrRbFooter.AfterDesignerCreate;
begin
inherited;
InitMmbObjs;
end;
initialization
{ We register this component so that it will appear in its own toolbar in
the
ReportDesigner }
ppRegisterComponent(TrrRbFooter, 'Standard Components', 11, 0,
'rrRbFooter', HInstance);
finalization
ppUnRegisterComponent(TrrRbFooter);
end.
//END OF COMPONENT------------------------------------------------------
1. Child object destruction
The child objects will be destroyed for you. In the Destroy method, remove
all of the existing code. Typically the most appropriate way to handle child
object references is to override the Notification method - which is part of
the Delphi VCL Component architecture. This method will be called as each
child object is free'd and you can write code to nil out the appropriate
child object reference.
2. Child object Loading
The child objects will be read from the object stream and added to the
Region.Objects[] array. Add an override for the Loaded method. After the
report definition is read from the stream, the Delphi streaming system calls
Loaded on each component. In the Loaded method you need to resolve the
references your component's child objects. Here is some code that will
accomplish this.
procedure TrrRbFooter.Loaded;
var
lsUserName: String;
liIndex: Integer;
begin
inherited;
for liIndex := 0 to ChildCount-1 do
begin
lsUserName := Objects[liIndex].UserName;
if (lsUsername = 'ReportName') then
FReportName := TppSystemVariable(Objects[liIndex])
else if (lsUsername = 'PageNo') then
FPageNo := TppSystemVariable(Objects[liIndex])
else if (lsUsername = 'Reportsource') then
FReportsource := TppLabel(Objects[liIndex])
else if (lsUsername = 'DateTime') then
FDateTime := TppSystemVariable(Objects[liIndex]);
end;
end;
Best regards,
Nard Moseley
Digital Metaphors
www.digital-metaphors.com
I delete the destructor Destroy and add Loaded override (I copy the exact
code as yours). The access violation still comes up. I put a break point in
Loaded. It stops when I switch from Preview to Design. But the ChildCount is
0. I guess that's why I got access voilation.
Maybe somehow all the children of this region are not saved in the stream.
So when it tries to recover the region from the stream, the children are
gone.
Anyway, the problem still exists. How can I fix that?
Bin
Try using ObjectCount rather than ChildCount -. my mistake.
I made a simple test case here using ObjectCount and it works properly -
references are restored.
Best regards,
Nard Moseley
Digital Metaphors
www.digital-metaphors.com