Dynamic Supreport Loading
Hi support-team,
Continuing on the story to get DSL working _with_ datapipelines in the
enduser environment.
Using StartOfMainReport() to load the template file is in these cases too
late.
After testing a bit, the Loaded() method seems a place to load the template
file. Early enough to get the datapipeline working if you want to print or
preview a report e.g. run-time.
The problem now is how to get the subreport to show the correct content 'at
design-time' e.g. in the designer.
The way it works now: The user adds a template component to it's report and
assigns the DefaultProperty with the template name. This is the name of a
report (template) that is found in the database (rb_item) in a
system-folder. While typing this template name my event handler is called. I
update the designer with the new template name which is reflected by the
subreport tabs.
1. I would like to have some event if he leaves the edit field. At that time
I could load the template from the database and check if the template name
is correct. Another way would be to create a custom form called from the
context menu of the template component (but I haven't looked into that L)
2. When the user types an incorrect template name I generate an exception.
When debugging this exception is raised but at run-time it looks like this
exception is eaten by RB resulting in an empty report. The user has no way
to correct his error.
3. How do I notify the designer that something in my DSL has changed so the
'do you want so save the changes dialog' is showed?
If it would clarify things I could e-mail you the component.
Using RB10.01 and D7 Pro.
Regards,
Jeroen R?ttink
Continuing on the story to get DSL working _with_ datapipelines in the
enduser environment.
Using StartOfMainReport() to load the template file is in these cases too
late.
After testing a bit, the Loaded() method seems a place to load the template
file. Early enough to get the datapipeline working if you want to print or
preview a report e.g. run-time.
The problem now is how to get the subreport to show the correct content 'at
design-time' e.g. in the designer.
The way it works now: The user adds a template component to it's report and
assigns the DefaultProperty with the template name. This is the name of a
report (template) that is found in the database (rb_item) in a
system-folder. While typing this template name my event handler is called. I
update the designer with the new template name which is reflected by the
subreport tabs.
1. I would like to have some event if he leaves the edit field. At that time
I could load the template from the database and check if the template name
is correct. Another way would be to create a custom form called from the
context menu of the template component (but I haven't looked into that L)
2. When the user types an incorrect template name I generate an exception.
When debugging this exception is raised but at run-time it looks like this
exception is eaten by RB resulting in an empty report. The user has no way
to correct his error.
3. How do I notify the designer that something in my DSL has changed so the
'do you want so save the changes dialog' is showed?
If it would clarify things I could e-mail you the component.
Using RB10.01 and D7 Pro.
Regards,
Jeroen R?ttink
This discussion has been closed.
Comments
I created a Design Control class for the template component.
1. Is solved. New question is can I disable the defaultproperty control?
Setting it to '' doesn't disable it. No big deal.
2. Solved. Replaced exception with with a messagebox that is only displayed
by the designerclass.
3. Solved by calling PropertyChange() when a custom property changes
A new problem arises however. The basis is that in the Loaded() method a
call is made to LoadFromDatabase() to load the content of the subreport
including any DADE objects. The existing subreport content is removed an
replaced by the new layout. The old DADE objects however are not removed.
The same effect when loading a subreport in the designer by the way.
How can I replace existing DADE objects by the new ones if the name matches?
Any other working solution is of cause also welcome.
Regards,
Jeroen R?ttink
1. One option is access the DataView via the DataPipeline.DataView property.
2. There is a single TdaDataModule for the entire report. When a subreport
is loaded, its DataViews are merged with the main report's TdaDataModule.
You could perhaps access the TdaDataModule and then iterate over its
DataViews[] array.
uses
daDataModule: TdaDataModule;
lDataModule := daGetDataModule(myReport);
Best regards,
Nard Moseley
Digital Metaphors
www.digital-metaphors.com
Looks good.
The QueryDataView classes created by the Query Designer and Query Wizard
always have a single DataPipeline, so DataPipelines[0] is what you want.
However a DataView can have multiple datapipelines. For an example, see
RBuilder\Demos\EndUser\Custom DataViews.
Best regards,
Nard Moseley
Digital Metaphors
www.digital-metaphors.com
Situation:
Report containing a template (Dynamic Loading Subreport) with an attached
dataview. This dataview is also referenced by a dbtext object on the
mainreport. As part of the dynamic loading process I remove dataviews that
already exist in the report and re-assign datapipelines of rcl components
that reference the old, to be removed, dataviews.
This works ok the first time I preview a report from the report explorer but
not the second time. The code that looks for and replaces datapipelines is
not finding any rcl components that should be reassigned while it is finding
these components the first time.
What is different the second run?
procedure ReLinkReportComponents( aReport : TppCustomReport;
aOldDataView, aNewDataView :
TdaDataView );
var liBand: Integer;
liObject: Integer;
lObject: TppComponent;
lDataView: TdaDataView;
liDataPipeline: integer;
begin
for liBand := 0 to aReport.BandCount-1
do begin
for liObject := 0 to aReport.Bands[liBand].ObjectCount-1
do begin
lObject := aReport.Bands[liBand].Objects[liObject];
if lObject.IsDataAware
then begin
if ( lObject.DataPipeline.DataView = aOldDataView ) // <== always
false the second run
then begin
// find the index of the used datapipeline (a dataview can
// have more datapipelines. see RBuilder\Demos\EndUser\Custom
DataViews)
lDataView := TdaDataView(lObject.DataPipeline.DataView);
liDataPipeline := lDataView.IndexOfChild(lObject.DataPipeline);
try
lObject.DataPipeline :=
aNewDataView.DataPipelines[liDataPipeline];
except
lObject.DataPipeline := nil;
end;
end;
end;
// call this method recursively if it's a subreport
if lObject is TppSubReport
then ReLinkReportComponents( (lObject as TppSubReport).Report,
aOldDataView, aNewDataView );
end;
end;
end;
Regards,
Jeroen R?ttink
I do not know what the difference is.
Where are you calling your ReLinkReportComponents method from?
Best regards,
Nard Moseley
Digital Metaphors
www.digital-metaphors.com
Each time you select Preview, the Designer saves the report definition to a
temp stream and then reloads the report definition from stream when you
leave the Preview workspace. This is necessary because a report can contain
RAP code that changes the report layout during generation.
Best regards,
Nard Moseley
Digital Metaphors
www.digital-metaphors.com
application that loads the report from database. If this report contains a
dynamic loading subreport with DADE objects it seems that the second time I
load this report into a ppReport object, it gives errors. I can sent you my
code if this would help. This is an error I then sometimes get:
Access violation at address FFFFFFFF. Read of address FFFFFFFF.
Exception class: EAccessViolation
Exception address: FFFFFFFF
----------------------------------------------------------------------------
------------------------
Stack list
[FFFFFFFF]
[00404AD7] System.@HandleAnyException + $33
[006F6FF0] ppRelatv.TppRelative.SetParent + $24
[008F6526] daQueryDataView.TdaQueryDataView.SetParent + $A
[006F6AEC] ppRelatv.TppRelative.Destroy + $18
[008DAA39] daDataView.TdaCustomDataView.Destroy + $1D
[008F5E82] daQueryDataView.TdaQueryDataView.Destroy + $4A
[0091B2B0] daIBExpress.TdaIBXQueryDataView.Destroy (Line 639,
"daIBExpress.pas" + 4) + $7
[0042FDF2] Classes.TComponent.DestroyComponents + $46
[0042FBF7] Classes.TComponent.Destroy + $47
[0048C31B] Controls.TControl.Destroy + $9B
[00404570] System.TObject.FreeInstance + $C
[0040497A] System.@ClassDestroy + $2
[00433E8E] Graphics.TBrush.Destroy + $1E
[0048F9B1] Controls.TWinControl.Destroy + $B9
[004A72A8] Forms.TScrollingWinControl.Destroy + $28
[004A82A3] Forms.TCustomForm.Destroy + $AB
[004A82BE] Forms.TCustomForm.Destroy + $C6
[006E3E0A] uJRForm.TjrForm.Destroy (Line 83, "uJRForm.pas" + 7) + $7
[00A04D65] uPrinten.TDlgPrinten.Destroy (Line 158, "uPrinten.pas" + 8) + $7
[004045B8] System.TObject.Free + $8
[00A06A7F] uIPrintProvider.TPrintProvider.Print (Line 183,
"uIPrintProvider.pas" + 14) + $3
[00A06AA8] uIPrintProvider.TPrintProvider.Print (Line 186,
"uIPrintProvider.pas" + 17) + $1C
[00A228EC] udbOutline.TdbOutline.actPrintExecute (Line 564, "udbOutline.pas"
+ 4) + $8
Also the message 'Cannot generate report. ORGANISATIE: Could not open
DataSet. Database not assigned' is sometimes generated. This is always the
second run. First run after start-up always works correct.
Can you create an example that uses the DBDemos data?
I installed Interbase 7.5, after installing D2006 and Interbase will not
run.
Best regards,
Nard Moseley
Digital Metaphors
www.digital-metaphors.com