![]() |
|
Published 1998-04-01 Printer-friendly version
This is my fourth article on reports. One would think that I would get tired writing about things that others in the programming community see as a necessary evil at best. Well, I think that reports are a mundane part of the job myself. My view on the importance of reports has been well stated in earlier articles. In those articles, I showed how we can do things that the templates don't do on their own. Now, we have C4 and OOP. So, do we have to learn all over again how to make complex reports? The answer to that is 'Not really'. The main question to be answered here is "Where are my embed points"? What I am going to talk about today are the objects (Classes) that are used in reports, and answer the question of embed points using the generated code as an example. I'm sorry, but there won't be any goodies this time (that will come later). This article will serve as a 'Primer' into C4 ABC Reports. Then next time, we will get our hands dirty and see about some complex reporting issues and some template stuff to make our job easier.
There are some things that must be learned when dealing with the new ABC classes. However, most of what we know about Clarion reports is going to remain the same. If anything, some the aspects become easier. For instance, the code necessary for giving our users the ability to select Print Preview was not much before - about 5 lines of code, but now we do not have to write a single line of code. There is a property in the ReportManager class used with the reports called SkipPreview. All we do is use the Set ABC Property code template supplied to select the SkipPreview property of the ReportManager class in our report. This is given the object name of ThisWindow. We just add the name of some global variable we use for selection of Print Preview and there you have it. Dynamic user selection of Print Preview. This will become a little clearer as we continue this article and each Class used by the report procedure is discussed.
The classes used in a report procedure are: ReportManager, ProcessClass, and PrintPreviewClass. These classes are used with the object names of: ThisWindow, ThisReport, and Previewer respectively. Each performs a different function with regards to the report, but all work together to produce a report with a progress window and Print Preview.
This is the main object of our reports. It is where the report starts and finishes. Lets look at some of the methods in this object and try and find the embed points that we used to be familiar with. The following code extracted from a generated report shows the beginning of the report:
CODE GlobalResponse = ThisWindow.Run()
This is the generated code that encompasses the entire report procedure we create when generating a report. Two lines - and one is the code statement. Notice that we are still using GlobalResponse. Everything else is a call to a method in one of the objects used by our report procedure. Since every report uses the same classes, all of this code is only compiled into our program once and not each report procedure like we had in the past with the standard clarion templates. Now, lets look at the method ThisWindow.Run(). At least as much as it pertains to embed points. I have left the comments in to show where our embeds will be placed.
ThisWindow.Run PROCEDURE() Re turnValue BYTE,AUTO ! Start of "WindowManager Method Data Section" ! [Priority 5000] ! End of "WindowManager Method Data Section" CODE ! Start of "WindowManager Method Executable Code Section" ! [Priority 2500] ReturnValue = PARENT.Run() ! [Priority 7500] ! End of "WindowManager Method Executable Code Section" RETURN ReturnValue
The two embed points we have here are before and after the call to PARENT.Run(). This is where it will call the Run method of the parent report manager class. If we do not put any embedded code here, then this code does not even get generated. This is here strictly for our benefit when using the embeditor. We would use the first embed point to perform any functionality that does not need, or use, any of the objects, properties or methods, but is needed to know. For instance, say we have a report based on a date range. I would put the call to the window to input the ranges in this method at the Priority 2500 embed point. The code would be something like this:
! [Priority 2500]
IF NOT GetDateRange()
RETURN RequestCancelled
END
The GetDateRange function would be designed to return True if dates were entered and OK was pressed, or False if Cancel was pressed. Then I would just return with the value of RequestCancelled. Remember our only line of procedure code was GlobalResponse = ThisWindow.Run(). So our procedure would cancel and return that status in GlobalResponse. In this way, nothing gets initialized or processed unless we entered the dates and pressed OK.
The next logical point for embedding code would be in the Init method of ThisWindow. Almost all classes have an Init method. This is where the object is initialized, and in ThisWindow.Init, other objects used in our report have their Init method called to initialize those objects. A typical Init method for this window looks something like this:
ThisWindow.Init PROCEDURE()
ReturnValue BYTE,AUTO
! Start of "WindowManager Method Data Section"
! [Priority 5000]
! End of "WindowManager Method Data Section"
CODE
! Start of "WindowManager Method Executable Code Section"
! [Priority 2500]
SELF.Request = GlobalRequest
! [Priority 4950]
ReturnValue = PARENT.Init()
! [Priority 5050]
IF ReturnValue THEN RETURN ReturnValue.
SELF.FirstField = ?Progress:Thermometer
SELF.VCRRequest &= VCRRequest
SELF.Errors &= GlobalErrors
! [Priority 5600]
CLEAR(GlobalRequest)
CLEAR(GlobalResponse)
! [Priority 6500]
Relate:Hardware.Open
FilesOpened = True
! [Priority 7800]
OPEN(ProgressWindow)
SELF.Opened=True
! [Priority 8030]
ThisReport.Init( Process:View,Relate:Hardware,
?Progress:PctText, Progress:Thermometer,
BYTES(Hardware) / SIZE(Hardware.Record))
ThisReport.AddSortOrder()
SELF.AddItem(?Progress:Cancel,RequestCancelled)
SELF.Init(ThisReport,Report,Previewer)
?Progress:UserString{Prop:Text}=''
Relate:Hardware.SetQuickScan(1,Propagate:OneMany)
! [Priority 8505]
Previewer.AllowUserZoom=True
! [Priority 8800]
SELF.SetAlerts()
! [Priority 10000]
! End of "WindowManager Method Executable Code Section"
RETURN ReturnValue
Novtice that this method has some code in it besides the call to the PARENT.Init() method. This is the code generated by the template that takes the parameters we set out in our report properties screen, such as file to report on, other files, any range checking, quick scan, print previewing, etc. Now, some of our old embed points are located in this method, with different priorities. Let's discuss a few of the more commonly used points.
| Position | Priority |
| Before Opening Files | 6500 |
| After Opening Files | 7800 |
| Before Opening Progress Window | 7800 |
| After Opening Progress Window | 8030 |
| Before Opening Report | 8030 |
| After Opening Report | 8505 |
If we think about things logically, with OOP, these are the only logical places to put these embeds. Before, with our procedural code, this was accomplishing the same thing, initializing the report. Now, it is all in the same method, just different priorities. There are many other methods used in the ReportManager (ThisWindow) class. The next ones we will discuss, though, are in the ProcessClass (ThisReport).
First, let's discuss the purpose of these two classes. ThisWindow (ReportManager) is used for the overall processing of the report procedure. It manages the report procedure. The actual reporting is done with ThisReport (ProcessClass). Note that it is the ProcessClass that is used also in the Process Procedure for batch processing. After all, what is a report? It is a batch process with the output going to a printer instead of a file. The needs are the same. That is the beauty of OOP. Similar processes can be accomplished with the same code. Now let's look at the main embed point for reports. The old names were Before Printing Detail, and After Printing Detail. They fall in a method of the object named ThisReport.
ThisReport.TakeRecord PROCEDURE() ReturnValue BYTE,AUTO ! Start of "Process Manager Method Data Section" ! [Priority 3500] SkipDetails BYTE ! [Priority 8500] ! End of "Process Manager Method Data Section" CODE ! Start of "Process Manager Method Executable Code Section" ! [Priority 500] ! [Priority 4000] ReturnValue = PARENT.TakeRecord() ! [Priority 5500] PRINT(RPT:detail) ! [Priority 8000] ! End of "Process Manager Method Executable Code Section" RETURN ReturnValue
Now lets think about the name of this method. TakeRecord. This is the method that actually does something with the record that was just read. In a report, you usually want to print the record that was just read. In other procedures, you would be doing other things with the record, but in any case, TakeRecord is the method to do something. In this case - reports - we want to print the record. There are only two lines of code in this method besides the return statement. Parent.TakeRecord calls the parent method for any processing that the class does for itself. In this case, it just returns (look in the abreport.clw file for ProcessManager.TakeRecord). It generates in our procedure because the template puts the Print statement in it. If this were a Process Procedure, it would use the same method, but have a statement re-writing the record, or deleting it if that was what we chose.
There are 4 set priorities for embedding code here, but we really only need to worry about two of them - priority 5500 for any code we want before printing the detail line, and priority 8000 for any code after printing the detail line. And this corresponds to, guess what? Before Printing Detail and After Printing Detail. So, there we have our old embed points redefined to fit OOP. But the actual functionality is the same.
Next, if we had some house cleaning to do, we traditionally placed this code in one of the following: Before Closing Report, After Closing Report, Before Closing Files, After Closing Files, and End Of Procedure. This basically gave us everywhere we needed to do any kind of house cleaning or post processing our report needed. Now, lets look at where we might want to put that code.
There is a standard name for a method of an object that closes out that object. It destroys it or 'Kills' it. That method, no matter what type of object, is the Kill method. Now, guess what. ThisWindow.Kill is where the files are closed, so it is here that we want our code that may have been in Before and After closing files. It would be Priority 5600 and 6500 respectively. Now, for the Before and After Closing Report, we would use the TakeCloseEvent method for the ReportManager object, ThisWindow. ThisWindow.TakeCloseEvent. So, Before Closing Report would be priority 2500, before it calls the Parent.TakeCloseEvent where the closing actually happens. After Closing Report is now priority 7500, or after it calls the parent method. Now, for End Of Procedure. If the code we want to add needs to execute after everything else is done, guess what? This takes us back to our first sample method. ThisWindow.Run. Only now, we want to place our code at priority 7500, or after the Parent.Run statement.
You may have noticed that when we want to place code before something happens, we have mostly used priorities below 5000. Then, when we wanted to place code after something happens, we mostly used priorities above 5000. So, somewhere around what would be priority 5000, the 'Something' happens. This is usually a call to the parent method. So, lower than 5000 is before the parent call, and higher than 5000 is after the parent call.
Now that we have learned where most of our important embed points are, the actual embedded code that we put into our reports hasn't really changed. After all, it's still Clarion. Clarion code is the same whether you use the ABC templates or the Standard templates. There are not two languages. So, complex reports are not really any harder or that much different than before.
Well, if you have muddled through this 'Primer' into the embed points, and where they moved to, we're ready for the next article where we will re-visit the Fred and Barney Sales company analysis reports. Using detail filters, queues, and the other things we did. It will be the same report, but using the new ABC classes. (And maybe a tip or two on deriving). We might even tackle the question: Just what does 'Virtual' mean anyway? (I think that is very powerful magic.)
Copyright © 1999-2008 by CoveComm Inc. All Rights Reserved. Reproduction in any form without the express written consent of CoveComm Inc., except as described in the subscription agreement, is prohibited.
Clarion Magazine ISSN 1718-9942
One year: $189
(includes all back issues since '99)
Renewals from $139
Two years: $289
Renewals from $239