Using Crystal Reports in Applications

  • Visual DataFlex and Crystal Reports

  • Crystal RDC Object Model Overview

  • Visual DataFlex Crystal RDC Object Model Overview

  • Crystal RDC Imported Classes

  • Integrating your Application with Crystal Reports

  • The Crystal Report Classes

  • Formatting Considerations

  • Error Handling

  • RDC Advanced Programming

  • Converting from CrystalReport to cCrystal


Visual DataFlex and Crystal Reports

Reports created using the Crystal Report Designer may be integrated into your Visual DataFlex application. Classes are provided that allow you to “wrap” a report inside an object. In the simplest case, this wrapper object specifies the report name and output destination. When the appropriate message is sent to this object, the report is output.

In addition to simply running a report, these classes allow you to control many of the report’s settings dynamically. For example, you can dynamically:

  • set printing options (paper size, orientation, print device, etc.)

  • set export options (set export style (word processing, spread sheet, etc.), set export destination, etc.)

  • set output order (sort order)

  • set record filters (record and global selection formulas)

  • set formulas

  • select the location of database tables

  • select dynamic data sources (i.e., data is provided by your application)

This is just a partial list of report customizations that can be defined at runtime; there are many more. Crystal Reports provides a complete interface, which allows you to control all aspects of a report. This interface is called the Report Design Component (RDC).

The Crystal RDC uses ActiveX Controls (COM) to interact with a report. The entire RDC interface is directly imported into Visual DataFlex in the form of a Class library. This library of Crystal classes (all of these classes start with the name “cCrystal”) is used within your application to manage your reporting needs.

As you will see, the RDC interface is large, consisting of many classes with many methods. In addition to providing the complete Crystal RDC COM interface, the Visual DataFlex classes have been enhanced with methods that make it much easier to perform some of the most commonly required tasks. This gives you the best of all worlds – a simple interface is provided for most of your reporting needs, but when required you have access to the complete RDC interface.

This section of the Help will describe how to use Crystal Reports with your application. Examples will be provided showing how to create simple reports. Examples will also be provided showing how you can use the RDC interface to customize your reports.

It is not the goal to refer to all of the features in the Crystal class library here. The class reference provides more information about the classes and their interfaces.

It is out of the scope of this section to describe all the features and abilities of the RDC. Instead, you will find information on how to use the Crystal RDC interface to output existing reports from within your Visual DataFlex applications. Please refer to your Crystal RDC documentation for more information on the RDC.

What Developers Need

If you are going to use Crystal Reports in your application, you will, of course, need a copy of Crystal Reports XI. This is the tool you will use to create your reports. The RDC components are provided as part of the Crystal Reports Developer Edition.

If your application uses embedded tables, you will also need the Crystal Reports Connectivity Kit 11 in order to build reports that will access your embedded tables.

If you do not have those components, you can receive a copy of Crystal Reports XI for DataFlex (that includes both Crystal Reports XI and the Connectivity Kit) for evaluation by just filling out the form available at:

www.dataaccess.com/crystal4dfevaluationform.asp.

What Deployment Sites Need

IF YOU HAVE CRYSTAL REPORTS XI FOR DATAFLEX INSTALLED

If the workstation in question has the Crystal Reports XI for DataFlex installed, all of the Crystal Reports and Conectivity Kit components are already on the machine, and you simply need to tell your application how to access them. Add their location to your PATH environment variable. The default location for these files is "c:\program files\common files\business objects\3.0\bin".

IF YOU DO NOT HAVE CRYSTAL REPORTS FOR DATAFLEX INSTALLED

If the workstation in question does not have Crystal Reports XI for DataFlex installed, you will need to install:

The Crystal Reports XI RDC runtime

Installing the RDC runtime is done through the use of Windows Installer merge modules that are supplied with your Crystal Reports Developer license. The merge modules files are on the Crystal Reports XI CD and also on Business Objects' website.

The merge modules you need to install are:

  • CrystalReports11_rdc_reportengine.msm

  • CrystalReports11_rdc_runtime.msm

  • CrystalReports11_rdc_license.msm

  • CrystalReports11_rdc_designtime.msm -- this merge module is labeled (optional) in the Crystal Reports XI doc, but it is required for using Visual DataFlex's RDC implementation.

For complete instructions on how to use merge_modules, refer to:

http://support.businessobjects.com/fix/merge_modules.asp

The Connectivity Kit runtime files

The best location for the Connectivity Kit runtime files is the Programs folder of your application.

Additional Crystal Reports runtime files (depending on the report interface your application uses).

If your application is still using the Print Engine API (crpe32.dll) interface (through the CrystalReports class), besides installing the Crystal Reports RDC runtime and the Connectivity Kit runtime files, you also need to install two additional Crystal Reports runtime files into your application's Programs folder: crpe32.dll .and crpe32_res_en.dll.

If your application already uses the COM (RDC) interface (through the cCrystal class, which is a new class in VDF 11.1), besides installing the Crystal Reports RDC runtime files, you only need to install the Connectivity Kit runtime files into your application's Programs folder.

Crystal Reports Connectivity Kit Runtime Files

The following is a list of the required Connectivity Kit runtime files and their locations in the development machine:

CommonDir\Bin\crdb_p2bdfapi.dll

CommonDir\Bin\p2bdfapi.dll

WindowsSystemDir\apidll.dll

WindowsSystemDir\df_collate.cfg

Additional Crystal Reports Runtime Files

The following is a list of the additional Crystal Reports runtime files and their locations in the development machine:

CommonDir\Bin\crpe32.dll

CommonDir\Bin\crpe32_res_en.dll

Where

  • CommonDir Refers to the full path where Crystal installed its common components. The default is C:\Program Files\Common Files\Business Objects\3.0

  • WindowsSystemDir Refers to the full path to the Windows System directory, the directory in which Windows system files are installed on the current PC. The default for this on Windows XP is C:\WINDOWS\System32.

You can read the details on how to distribute your reports at:

http://support.businessobjects.com/library/kbase/articles/c2017252.asp

See Also

FlexCOM 2 Overview


Crystal RDC Object Model Overview

Overview Diagram of the Report Designer Object Model

The diagram above displays the object model of the RDC provided by Crystal Reports. Each item shown in the diagram is a COM object. Some of these are COM objects while others are COM collections. A collection contains a group of objects of the same type (It is basically an array of Dispatch IDs). For each COM object, there is a corresponding Visual DataFlex class.

In the Crystal RDC Object Model Overview, there are two main branches. The branch on the left focuses on the printed objects of a report. Accessing these objects gives you a lower level of control to change the report output. The branch on the right focuses more on the data connection and report properties. This right side of the object model is the side developers will tend to use. Accessing these objects give you the ability to set formulas, sort order, selection criteria, etc.

These objects are automation objects. To use an automation object within Visual DataFlex you must:

  1. Create a DataFlex object based on the appropriate COM class.

  2. Get an ID (Dispatch ID) from the COM object and assign this ID to your DataFlex object.

  3. When the object is no longer needed, destroy it.

If this object model and the use of automation objects appear to be a bit overwhelming, don't worry. Visual DataFlex provides a simple-to-use interface for the common functions you will be using. This interface also handles the creation of objects, attachment of Dispatch ID's and destruction of objects for most of the objects you will be using. Developers wanting to go beyond the standard interface can do so by following the examples provided in the RDC Advanced Programming section.

For more information on collections and the RDC object model, please refer to the Crystal RDC documentation.

See Also

Visual DataFlex Crystal RDC Object Model Overview


Visual DataFlex Crystal RDC Object Model Overview

Overview Diagram of the Visual DataFlex Report Designer Object Model

This diagram represents the Visual DataFlex object model. The cCrystal class is the class that is used to control all of the other classes. All of the objects, except cCrystal, are COM objects. Some of the classes listed here have helper interfaces. These interfaces are used to perform the most commonly required tasks. They also free you from having to deal with creating COM objects and attaching their Dispatch ID.

This diagram represents the standard interface – the interface you are most likely to need. The remaining COM objects not listed are available, but are considered advanced programming.

See Also

RDC Advanced Programming


Crystal RDC Imported Classes

The Crystal RDC COM classes have been imported into Visual DataFlex from the following DLLs:

  • craxddrt.dll - cCrystalcraxddrt.pkg

  • cdo32.dll - cCrystalCDO32.pkg

  • crviewer.dll - cCrystalActiveXReportViewer.pkg

These classes were imported using the FlexCom2 class generator. The naming convention used with these classes is a little different than that used with other classes generated through FlexCom2.

Class Prefix

The prefix used when importing these classes is cCrystalXxx instead of the standard cComXxx (where Xxx is the original class name). For example: cCrystalReport in the Visual DataFlex help corresponds to Report in the Crystal RDC documentation. This organizes the help so all the various Crystal COM objects are grouped together. It also allows developers to import these same classes if they do not wish to use the classes provided with Visual DataFlex.

Constant Prefix

All constants used by the RDC classes are prefixed with “cr”. The standard OLE prefix for constants was not used because Crystal already provided a prefix of cr for all of their constants. Constants are defined at the top of the package file they are used within. You should also be able to locate them in the Crystal RDC documentation.

Alias Data Type

Many of the Crystal COM methods use alias data types. An alias data type is where a constant is created to represent a data type.

This is an example of an alias data type and some constants:

Define CRSortDirection for Integer

Define crAscendingOrder for 0

Define crDescendingOrder for 1

Define crOriginalOrder for 2

Define crSpecifiedOrder for 3

CRSortDirection is an alias for the data type Integer. The possible integer values that you can use are listed directly below the alias data type. In this case, there are four constants. You can use either the constant or its integer equivalent.

There are methods that use this alias data type:

// Gets sort direction.

Function ComSortDirection Returns CRSortDirection

Handle hDispatchDriver

CRSortDirection retVal

Get phDispatchDriver to hDispatchDriver

Get InvokeComMethod of hDispatchDriver 222 OLE_VT_I4 to retVal

Function_Return retVal

End_Function

// Sets sort direction.

Procedure Set ComSortDirection CRSortDirection value

Handle hDispatchDriver

Get phDispatchDriver to hDispatchDriver

Send PrepareParams to hDispatchDriver 1

Set ComProperty of hDispatchDriver 222 OLE_VT_I4 to value

End_Procedure

By using the alias data types, Crystal made it easy to find what the possible values are and what each value represents. If you were looking at the method Set ComSortDirection, you can see it needs a data type of CRSortDirection. By doing a quick search of the class package file, you will find that CRSortDirection is an integer and the possible values are crAscendingOrder, crDescendingOrder, crOriginalOrder, or crSpecifiedOrder.

Anywhere you see an alias data type that you need know which values you can use, go to the class package and search for that alias data type. Below the alias data type there will be a list of constants that can be used.

Method Prefix

All methods with the COM prefix are from the Crystal RDC Object model. COM methods are part of the standard RDC interface. In addition to the help found here and the class reference, further help can be found in your Crystal RDC documentation. Please note that the COM prefix will not exist in any documentation other than the help provided from Visual DataFlex.

For example, the ComPaperOrientation in the Visual DataFlex help corresponds to PaperOrientation in the Crystal RDC documentation.

Methods that don't start with COM are the ones provided by Visual DataFlex. These methods are not part of the Crystal RDC interface, but were added to make it easy to work with the RDC on a higher level. Information on these helper methods will only be found within these help files and white papers found at www.dataaccess.com/whitepapers.

Event Prefix

All events with the onCOM prefix are from the Crystal RDC Object model. OnCOM events are part of the standard RDC interface. In addition to the help found here and the class reference, further help can be found in your Crystal RDC documentation. When searching for events in the Crystal RDC help, you will need to remove the onCOM prefix as onCOM events are only documented in the Visual DataFlex help.

For example, the event OnComCloseButtonClicked in the Visual DataFlex help corresponds to the event CloseButtonClicked in the Crystal RDC help.

Events that don't start with OnCOM are the ones provided by Visual DataFlex. These events are not part of the Crystal RDC interface, but were added to make it easy to work with the RDC on a higher level. Information on these helper methods will only be found within these help files and white papers found at www.dataaccess.com/whitepapers.

Class ID

Most classes have been assigned independent class IDs. What this means to you as the developer is that your programs will use the highest version of Crystal Reports installed on the client machine. A report created in an earlier version of Crystal Reports can be opened by a newer version of Crystal Reports. This allows for backwards compatibility and easier upgrades.


Integrating your Application with Crystal Report

What You Need

First you must create your report using Crystal Reports Designer. When complete, this report should get placed in a directory that can be located within one of your workspace’s data paths.

Next you must create a report object for your report based on the cCrystal class. This object will contain properties that set the report’s name and output destination. This cCrystal object will be placed inside some other view object, most likely an object based on the ReportView class. Visual DataFlex Studio has a Crystal Reports RDC Integration Wizard that you can use to create report views.

How It All Works

A report is run within your application by sending the RunReport message to the cCrystal object. When RunReport is executed, the following happens:

  • A cCrystalReport object is created. The actual report will be opened and attached to this object.

  • The OnInitializeReport event is sent to the cCrystal object. You can customize the behavior of your report by augmenting OnInitializeReport.

  • Depending on the output device selected, the event OnPrintReport, OnExportReport or OnDisplayReport will be sent. You can customize the output behavior of your report by augmenting those methods.

  • Your report is generated and sent to the appropriate output device.

  • When the report is finished, the cCrystalReport object is destroyed. This destroys all COM objects created during the running of the report.

Within the events, you may need to access other COM objects part of the RDC object model. These COM objects will not be created until they are needed. Once created, their handle is stored so it can be retrieved the next time it is requested rather than having to create and destroy the same object multiple times. This process is usually automatic. These objects will be created as children of the cCrystalReport object and therefore they are destroyed when the cCrystalReport is destroyed.

If the report destination is to the screen, a cCrystalPreview object will be created. This view contains the COM object for displaying the report. When the view is closed, it is also destroyed.

If the report destination is to file, a cCrystalExportOptions object will be created. This object will be destroyed when the report has been exported.

The cCrystalApplication object will be created the first time a report is run and will not be destroyed until the application is closed.

For advanced programming, you should follow the example of creating objects within the cCrystalReport so they will be destroyed when the report is complete.

See Also

Crystal RDC Object Model Overview

RDC Advanced Programming


The Crystal Report Classes

  • The cCrystal Class

  • The cCrystalReport Class

  • The cCrystalPreview Class

  • The cCrystalExportOptions Class

  • Other RDC Classes


The cCrystal Class

The cCrystal class is used to create a wrapper object that will contain a Crystal report. This class is simple and has a limited number of interfaces. Primarily you will use this to set the report name (psReportName) and output destination (peOutputDestination). You run the report by sending the RunReport message.

Object oCrystalReport1 is a cCrystal

Set psReportName to "CustLst.rpt"

Set peOutputDestination to PRINT_TO_WINDOW

End_Object

Object oButton1 is a Button

Set Label to "Print"

Procedure OnClick

Send RunReport of oCrystalReport1

End_Procedure

End_Object

While this is all the code required to run a report, you will often want to alter the report set up information before actually generating a report. For example, you may wish to change record selection filters, change sort order, set export options, modify preview appearance, or adjust printer settings. The Crystal RDC interface allows you to set any of these changes and much more. This is accomplished by sending the appropriate COM messages to the corresponding COM objects.

The COM connection to the report is not made until the report is started with RunReport. This method will dynamically create an object based on the cCrystalReport (link) class. All report handing will then be done through that object.

The OnInitializeReport Event

After cCrystalReport object is created, the event OnInitializeReport is sent to the cCrystal object passing the object handle of the newly created cCrystalReport object.

OnInitializeReport, where almost all interaction with the report object should be managed, takes the object handle of the cCrystalReport object as a parameter. This handle is used as the destination for most messages sent within this event.

Object oCrystalReport1 is a cCrystal

Set psReportName to "CustLst.rpt"

Set peOutputDestination to PRINT_TO_WINDOW

Procedure OnInitializeReport Handle hoReport

Forward Send OnInitializeReport hoReport

Set ComGroupSelectionFormula of hoReport to ("{Customer.State}='FL'")

End_Procedure

End_Object

You can cancel your report within OnInitializeReport by setting the pbCanceled property to TRUE.

The Report Output Destination Events

In addition to the OnInitializeReport event, there is an event for each output destination: Printer, Export and Display. You can use these events to set properties for the various output destinations. These events do nothing by default.

  • OnPrintReport can be used to control printer settings.

  • OnExportReport can be used to set export options.

  • OnDisplayReport can be used to set preview options.

These events are provided as a programming convenience. While the OnInitializeReport event could be used to handle all code for output destinations, these specialized events make it easier for you to organize your code.

This is an example of how you can use the different output methods to further control the output.

Object oCrystalReport1 is a cCrystal

Set psReportName to "CustLst.rpt"

Procedure OnInitializeReport Handle hoReport

Forward Send OnInitializeReport hoReport

Set ComGroupSelectionFormula of hoReport to ("{Cust.State}='FL'")

End_Procedure

//Printer specific

Procedure OnPrintReport Handle hoReport

Forward Send OnPrintReport hoReport

Send ComSelectPrinter of hoReport "winspool" "Lexmark 3320" "LPT1:"

Set ComPaperOrientation of hoReport to crLandscape

Set ComPaperSize of hoReport to crPaperLetter

Set piPrinterCopies of hoReport to 2

Set pbPrinterCollate of hoReport to FALSE

End_Procedure

//Export specific

Procedure OnExportReport Handle hoReport

Handle hoExport

Forward Send OnExportReport hoReport

Set pbExportPrompt of hoReport to FALSE

Get ExportObject of hoReport to hoExport

Set ComDiskFileName of hoExport To "D:\Documents\MyReport.rtf"

Set ComFormatType of hoExport To crEFTExactRichText // Rich Text

End_Procedure

//Preview specific

Procedure OnDisplayReport Handle hoReport

Handle hoViewer

Forward Send OnDisplayReport hoReport

Get ActiveXReportViewerObject to hoViewer

Set ComEnablePrintButton of hoViewer To FALSE

Set ComEnableExportButton of hoViewer To FALSE

End_Procedure

End_Object

Object oButton1 is a Button

Set Label to "Print to Screen"

Set Location to 34 71

Procedure OnClick

Set peOutputDestination of oCrystalReport1 to PRINT_TO_WINDOW

Send RunReport of oCrystalReport1

End_Procedure

End_Object

Object oButton2 is a Button

Set Label to "Print to Printer"

Set Location to 62 71

Procedure OnClick

Set peOutputDestination of oCrystalReport1 to PRINT_TO_PRINTER

Send RunReport of oCrystalReport1

End_Procedure

End_Object

Object oButton3 is a Button

Set Label to "Print to Export"

Set Location to 90 71

Procedure OnClick

Set peOutputDestination of oCrystalReport1 to PRINT_TO_FILE

Send RunReport of oCrystalReport1

End_Procedure

End_Object

You can cancel your report within any of these events by setting the pbCanceled property to TRUE.

See Also

cCrystalReport

cCrystalExportOptions

cCrystalPreview

cCrystal (Printer Options)


The cCrystalReport Class

The cCrystalReport class is by far the most important class in the Visual DataFlex RDC object model library. It provides a COM connection to the Crystal RDC Report object.

This object only exists while the report is being run. It is created when a report is started (when RunReport is sent to the cCrystal object) and it is destroyed when the reporting process is completed.

The cCrystalReport class contains large number of COM methods, which will allow you to control the behavior of a report. Some of these COM methods directly set a property of the report (e.g. Set ComRecordSelectionFormula) while other methods allow you to access other COM objects within the object model.

In addition to these methods, a number of helper methods have been created that allow you to more easily perform the most commonly required tasks. There are two types of helper methods: one type performs a specific high level task such as setting the sort order; the other type returns a handle (or an array of handles) to an object.

The functions that return object handles are particularly important for advanced programming. These functions return the handles of other commonly used COM objects. When you use these COM objects, you don’t have to worry about creating the object, attaching it to a COM dispatch pointer and eventually destroying it. This class handles all of that for you; you just get the object and use it.

There are other COM objects that you may need to use that are not provided automatically.

Much of what you will do to the report should be done by sending messages to this object from inside of the cCrystal object reporting events (OnInitializeReport, OnPrintReport, OnDisplayReport and OnExportReport). The handle to the cCrystalReport object is passed as a parameter to these events.

Examples

The following sections demonstrate how to perform the most commonly required reporting tasks within a cCrystalReport object.

  • Setting a record selection formula

  • Setting a group selection formula

  • Setting a formula field

  • Changing a sort order

  • Changing and using parameters

  • Subreports

  • Setting printer options

  • Setting file locations

  • Locating the Crystal Report

  • Using dynamic data structures (CDO)

See Also

RDC Advanced Programming


Setting a Record Selection Formula

This example uses the standard COM interface to select records to print:

Object oForm1 is a Form

Set Label to "State"

Set Size to 13 85

Set Location to 12 159

End_Object // oForm1

Object oCrystalReport1 is a cCrystal

Set psReportName to "CustLst.rpt"

// hoReport is the COM object cCrystalReport

Procedure OnInitializeReport Handle hoReport

String sState sCRState

Forward Send OnInitializeReport hoReport

Get Value of oForm1 to sState

Get CrystalString sState to sCRState

Set ComRecordSelectionFormula of hoReport to ;

("{Customer.State}=" * sCRState)

End_Procedure

End_Object

Note that this sample uses the Crystal RDC method ComRecordSelectionFormula to set the formula. Because this method is prefaced with “Com” you know that this is one of the standard methods provided by the RDC interface. If you wanted to learn more about this method, you could refer to the standard Crystal Reports documentation. You would look for RecordSelectionFormula in the ReportObject class (i.e., you drop the Com prefix for the method and the cCrystal prefix for the object).

Go back to the list of Examples

See Also

CrystalString

CrystalNumber

CrystalDate

CrystalInteger

ComGroupSelectionFormula


Setting a Group Selection Formula

This example uses the standard COM interface to select records to print:

Object oForm1 is a Form

Set Label to "State"

Set Size to 13 85

Set Location to 12 159

End_Object // oForm1

Object oCrystalReport1 is a cCrystal

Set psReportName to "CustLst.rpt"

// hoReport is the COM object cCrystalReport

Procedure OnInitializeReport Handle hoReport

String sState sCRState

Forward Send OnInitializeReport hoReport

Get Value of oForm1 to sState

Get CrystalString sState to sCRState

Set ComGroupSelectionFormula of hoReport to ;

("{Customer.State}=" * sCRState)

End_Procedure

End_Object

Note that this sample uses the Crystal RDC method ComGroupSelectionFormula to set the formula. Because this method is prefaced with “Com” you know that this is one of the standard methods provided by the RDC interface. If you wanted to learn more about this method, you could refer to the standard Crystal Reports documentation. You would look for GroupSelectionFormula in the ReportObject class (i.e., you drop the Com prefix for the method and the cCrystal prefix for the object).

Go back to the list of Examples

See Also

ComRecordSelectionFormula


Setting a Formula Field

This is an example of how to set a formula field. Formula fields can be helpful in printing calculated or combined values, or information collected at the time the report is run. For example: you may want to display a range of dates the user selected as part of the record selection formula or you may want to print your customer’s company name at the top of a report.

Object oCrystalReport1 is a cCrystal

Set psReportName to "CustLst.rpt"

Procedure OnInitializeReport Handle hoReport

String sCRCName

Forward Send OnInitializeReport hoReport

Move (Trim(Customer.Name)) to sCRCName

Get CrystalString sCRCName to sCRCName

//CompanyName is the name of the formula field in the report, i.e. @CompanyName

Send AssignFormula of hoReport "CompanyName" sCRCName

End_Procedure

End_Object

AssignFormula is a helper method. This has been added to the cCrystalReport class to make it easier to set a formula field. Although a formula can be set using the standard RDC COM methods, it is complicated.

Without this helper, you would have to create the formula field collection object, loop through this collection and create a formula field for each formula in the collection, check to see if it is the formula you are looking for and then assign its value. After all that, you would have to make sure to destroy all the objects you created. AssignFormula does all of this for you.

Standard RDC methods (methods that are part of the standard Crystal interface) always contain a Com prefix. Helper methods (methods that were added to a class to make it easier to perform commonly needed tasks) never have a Com prefix in their name. This naming convention makes it easy for a developer to distinguish between these two types of methods.

Go back to the list of Examples

See Also

Crystal RDC Imported Classes


Changing a Sort Order

Three methods are provided to make it easy to change report sort orders: DeleteSortOrder, AppendSortField, and DeleteNthSortField.

This example shows how to create a new sort order by removing the current sort fields and adding new sort fields:

Object oCrystalReport1 is a cCrystal

Set psReportName to "CustLst.rpt"

Procedure OnInitializeReport Handle hoReport

Forward Send OnInitializeReport hoReport

Send DeleteSortOrder of hoReport

Send AppendSortField of hoReport "Customer" "State" crAscendingOrder

Send AppendSortField of hoReport "Customer" "Zip" crAscendingOrder

Send AppendSortField of hoReport "Customer" "Name" crDescendingOrder

End_Procedure

End_Object

If you need to perform additional sort field processing, you will need to create a SortFields object (cCrystalSortFields) and use that object’s COM Interface.

Go back to the list of Examples


Changing and Using Parameters

This example demonstrates how you would set the text and list of options of a parameter field for a report with a parameter field called CustomerList. In this example, if the report contains the CustomerList parameter field, the text and the list of options are set. The option is a list of customers from a table.

This sample has a mix of COM methods and helper methods. By calling GetParamObjectByName, the helper method takes care of creating the object and attaching it and then returning the object handle. Once you have the object handle, you can use the COM methods for that class to set the text and options.

Object oCrystalReport1 is a cCrystal

Set psReportName to "CustLst.rpt"

Procedure OnInitializeReport Handle hoReport

Integer iItem

Handle hoParam

Forward Send OnInitializeReport hoReport

Set ComEnableParameterPrompting of hoReport to TRUE

Get GetParamObjectByName of hoReport "CustomerList" to hoParam

If (hoParam) Begin

Set ComPrompt of hoParam to "Select customers to print:"

Set ComDisallowEditing of hoParam to TRUE

Set ComEnableMultipleValues of hoParam to TRUE

Clear Customer

Find gt Customer.Name

While (Found)

Increment iItem

// Create a list of default values. Sort can be on value or description.

Send ComSetNthDefaultValue of hoParam iItem (Trim(Customer.Name))

Set ComNthValueDescription of hoParam iItem to Customer.State

Find gt Customer.Name

End

End

End_Procedure

End_Object

Go back to the list of Examples


Subreports

Subreports are based on the cCrystalReport class. You will need to get the handle of the subreport in order to set things like formulas and sort order. You can get the handle of a subreport by calling GetSubReportObjectbyName.

Setting a Subreport Formula Field

This example shows how you set the value of an existing formula field within a subreport. First you get the handle of the subreport. The rest of it is just like using the hoReport variable, but you are using the handle of the subreport.

Object oCrystalReport1 is a cCrystal

Set psReportName to "CustLst.rpt"

Procedure OnInitializeReport Handle hoReport

Handle hoCustRpt

String sCRCName

Forward Send OnInitializeReport hoReport

Move (Trim(Customer.Name)) to sCRCName

Get CrystalString sCRCName to sCRCName

Get GetSubReportObjectbyName of hoReport "CustRpt" to ho hoCustRpt

If (hoCustRpt) Begin

Send AssignFormula of hoCustRpt "CompanyName" sCRCName

End

End_Procedure

End_Object

Changing a Subreport Sort Order

This example displays how you delete and add a new sort order in a subreport. First you get the handle of the subreport. The rest of it is just like using the hoReport variable, but you are using the handle of the subreport.

Object oCrystalReport1 is a cCrystal

Set psReportName to "CustLst.rpt"

Procedure OnInitializeReport Handle hoReport

Handle hoCustRpt

Forward Send OnInitializeReport hoReport

Get GetSubReportObjectbyName of hoReport "CustRpt" to hoCustRpt

If (hoCustRpt) Begin

Send DeleteSortOrder of hoCustRpt

Send AppendSortField of hoCustRpt "Customer" "State" crAscendingOrder

Send AppendSortField of hoCustRpt "Customer" "Zip" crAscendingOrder

Send AppendSortField of hoCustRpt "Customer" "Name" crDescendingOrder

End

End_Procedure

End_Object

Go back to the list of Examples


Setting Printer Options

The Printer Options setting is part of the cCrystalReport interface. Most of the print related properties are built directly into the COM class interface. The most commonly used methods will be:

  • ComDriverName

  • ComPaperOrientation

  • ComPaperSize

  • ComPaperSource

  • ComPortName

  • ComPrinterDuplex

  • ComPrinterName

  • ComPrinterSetup

  • ComPrinterSetupEx

  • ComPrintOut

  • ComPrintOutEx

  • ComSelectPrinter

  • ComSetUserPaper

Additional properties have been added to the cCrystalReport class:

  • piPrinterStartPage

  • piPrinterEndPage

  • pbPrinterCollate

  • piPrinterCopies

  • pbPrinterPrompt

The event OnPrintReport can be used when setting the properties.

This example demonstrates how to set the printer properties.

Object oCrystalReport1 is a cCrystal

Set psReportName to "CustLst.rpt"

Set peOutputDestination of oCrystalReport1 to PRINT_TO_PRINTER

Procedure OnPrintReport Handle hoReport

Forward Send OnPrintReport hoReport

Send ComSelectPrinter of hoReport "winspool" "Lexmark 3320" "LPT1:"

Set ComPaperOrientation of hoReport to crLandscape

Set ComPaperSize of hoReport to crPaperLetter

Set piPrinterCopies of hoReport to 2

Set pbPrinterCollate of hoReport to FALSE

End_Procedure

End_Object

For more information on printer settings, please see the Crystal RDC documentation.

Go back to the list of Examples


Setting File Locations

Locating Embedded Tables within the Workspace Data Path

The cCrystalReport class contains an advanced feature, which will automatically locate tables when using the embedded database or INT files. Database table paths will be set using the workspace data path in the same manner in which your Visual DataFlex program opens them. It will open the table in the first data path that contains that table. You do not need to specify "Same As Report" in the Crystal Report Designer.

This auto-locate feature is controlled by the pbAutoLocateDFFiles property. By default this is set to TRUE. Setting it to FALSE will disable this feature.

If pbAutoLocateDFFiles is set to FALSE, you can manually locate all embedded tables within the data path by sending the LocateDFFiles message to the report object. (It is, of course, simpler to just set pbAutoLocateDFFiles to TRUE.) If you want to locate just a single embedded table within the data path, you will use the LocateDFFile message. This message has one argument: a handle of the table object. If the table cannot be found, an error is generated.

This example locates and sets a single table within the data path. It uses GetTableObjectByName to find the object handle for the table and LocateDFFile to set the table’s location:

Object oCrystalReport1 is a cCrystal

Set psReportName to "CustLst.rpt"

Set pbAutoLocateDFFiles to False // don’t automatically locate tables

Procedure OnInitializeReport Handle hoReport

Handle hoTable

Boolean bOk

Forward Send OnInitializeReport hoReport

// locate just this one table within the workspace data path

Get GetTableObjectByName of hoReport "Customer" to hoTable

If (hoTable) Begin

Get LocateDFFile of hoReport hoTable to bOk

End

End_Procedure

End_Object

Note: The above applies only to reports that use the embedded database or INT files.

Setting File-Based Tables to a Custom Path

File based tables (such as the embedded database) can be located in any directory and given any name you wish. This is accomplished using the standard COM method “Set ComLocation”. This must be sent to the table object and passed the full path and name of the table.

This example sets the location of a table to a specific location. It uses GetTableObjectByName to find the object handle for the table and ComLocation to set the table’s location

Object oCrystalReport1 is a cCrystal

Set psReportName to "CustLst.rpt"

Procedure OnInitializeReport Handle hoReport

Handle hoTable

Forward Send OnInitializeReport hoReport

// find the table object and return its handle

Get GetTableObjectByName of hoReport "Customer" to hoTable

If (hoTable) Begin

Set ComLocation of hoTable to "F:\Backup\Data\Customer.dat"

End

End_Procedure

End_Object

Go back to the list of Examples


Locating the Crystal Report

A great deal of flexibility is provided in locating a report to be run. If you include the full path in a report (e.g., Set psReportName to "F:\FreightLine\Reports\CustLst.rpt"), that path will be used to locate the report. If you provide just the name of the report (e.g., Set psReportName to "CustLst.rpt"), the report will be searched for within the workspace’s data path. If you data path contains multiple directories, the report may be located in any of those directories. This gives you the ability to separate your reports and data into multiple directories.

Setting a Report Name to be used with the Full Path

The report will be opened in the path supplied.

Object oCrystalReport1 is a cCrystal

Set psReportName to "F:\FreightLine\Reports\CustLst.rpt"

End_Object

This report must be located in the directory named in order to get successfully opened.

Setting a Report Name to be used

The report will be located within the workspace data path.

Object oCrystalReport1 is a cCrystal

Set psReportName to "CustLst.rpt"

End_Object

This report can be run as long as it is found within any of the directories listed below.

Placing Data and Reports According to Workspace Setting

Simple Workspace

Data and reports must share the same directory:

Data path= .\Data

Modified Workspace

Data and reports have a different directory within the main workspace:

Data path= .\Data;.\Reports

Semi-Complicated Workspace

Data in Data folder and in a subfolder of the Data folder; reports have a different directory within the main workspace:

Data path= .\Data;.\Data\Payroll;.\Reports

Complicated Workspace

Two workspaces sharing some files and some reports; data and reports have a different directory within the main workspace:

Workspace1

Data path= .\Data\Company1;.\Data\Common;.\Reports\Company_1;.\Reports\Common

Workspace2

Data path= .\Data\Company2;.\Data\Common;.\Reports\Company_2;.\Reports\Common

Go back to the list of Examples


Using Dynamic Data Sources (CDO)

Crystal Reports provides a method for reporting based upon dynamic data sources. Dynamic data source allows you to define and populate in-memory tables. Once properly defined, these “tables” can be used in your report as if they were actual database tables. This is accomplished by using Crystal Data Objects (CDO).

CDO technology allows you to report on data generated within your application, as opposed to data from a database table.

Two helper methods are provided to make it easier to work with CDO objects: CreateCDO and AppendCDOData.

Note

This section assumes you are familiar with using CDOs in Crystal Reports and you have Crystal Data Objects installed on your machine (this component is not installed by default).

CreateCDO is used to create your data-source object. Using this method the data types for your CDO object is automatically set up. When calling this method, you pass the name of the definition file – the TTX file. The TTX file, the file you can create inside Crystal Report Designer using Database Expert (Create New Connection | Field Definitions Only), defines the fields and data types for your dynamic table. CustomerList.ttx (below) shows an example of the content of a typical TTX file:

Customer_Number Number 1

Name String 30 Data Access Corporation

Phone_Number String 20 305-238-0012

Fax_Number String 20 305-238-0017

CreateCDO will find this file and use it to initialize the CDO object. This ensures that all fields are present and they are of the correct type.

Your dynamic data is created by defining and filling a two dimensional array of type Variant. The first dimension of this array is dynamic and represents a “row” of data. The second dimension of your array represents a “column” of data and must be explicitly declared. For example: a CDO array called vData with five columns would be defined as “Variant[][5] vData”. This array must be of type Variant, it must contain the number of columns defined in the TTX file and the data type of each column must match the data type defined in the TTX file.

AppendCDOData is used to add the CDO data to your report. After creating a variant array and filling it with data, you must attach it to the CDO using AppendCDOData. This method first makes sure that all of the data in the array is cast to the correct data type – Crystal Reports requires the data to be cast properly. It then adds the data to the CDO object.

Note that there is no need to set the CDO data source for subreports; using the CreateCDO method will delegate the data to all subreports.

This example demonstrates how you would pass data to a CDO in a report. By calling the CreateCDO, the helper method takes care of creating the object and attaching it and then returning the object handle. Next you must create and fill a variant array – this array is your virtual table. Finally, you can assign this data to your CDO object by using the AppendCDOData helper method.

Object oCrystalReport1 is a cCrystal

Set psReportName To " CustLst.rpt"

Procedure OnInitializeReport Handle hoReport

Handle hoCDO

Integer iItem iChannel

Variant[][4] vData

Forward Send OnInitializeReport hoReport

Get CreateCDO of hoReport "CustomerList.ttx" to hoCDO

If (hoCDO) begin

Get Seq_New_Channel to iChannel

Direct_input channel iChannel "c:\CustomerData.txt"

Move 0 to iItem

While (not(SeqEof))

Readln channel iChannel vData[iItem][0] vData[iItem][1];

vData[iItem][2] vData[iItem][3]

Increment iItem

End

Close_Input channel iChannel

Send Seq_Release_Channel iChannel

// Add rows to CDO object

Send AppendCDOData of hoReport hoCDO vData

End

End_Procedure

End_Object

Using CDO with a Validation Table

This example shows how you would pass data from a validation table to a CDO in a report. This uses a function in the ValidationTable (link) object named TableData to return a variant array which will be used as your virtual table. The table will have two columns – code and description.

Use Customer.DD

Object oCrystalReport1 is a cCrystal

Set psReportName To "CustLst.rpt"

Procedure OnInitializeReport Handle hoReport

Handle hoCDO

Variant[][2] vData

Forward Send OnInitializeReport hoReport

Get CreateCDO of hoReport "States.ttx" to hoCDO

// Customer_State_VT found in Customer.DD

Get TableData of Customer_State_VT to vData

// Add rows to CDO object

Send AppendCDOData of hoReport hoCDO vData

End_Procedure

End_Object

This example shows how to pass data to CDOs in a report that uses two different TTX files.

Object oCrystalReport1 is a cCrystal

// the report uses the description of the state instead of the code

Set psReportName To "CustLst.rpt"

Procedure OnInitializeReport Handle hoReport

Handle hoCDO

Integer iItem iChannel

Variant[][4] vCust

Variant[][2] vStates

Forward Send OnInitializeReport hoReport

Get CreateCDO of hoReport "CustomerList.ttx" to hoCDO

If (hoCDO) begin

Get Seq_New_Channel to iChannel

Direct_input channel iChannel "C:\CustomerData.csv"

Move 0 to iItem

While (not(SeqEof))

Readln channel iChannel vCust [iItem][0] vCust [iItem][1] ;

vCust [iItem][2] vCust [iItem][3]

Increment iItem

End

Close_Input channel iChannel

Send Seq_Release_Channel iChannel

// Add rows to CDO object

Send AppendCDOData of hoReport hoCDO vCust

Get CreateCDO of hoReport "ListOfStates.ttx" to hoCDO

Get TableData of Customer_State_VT to vStates

Send AppendCDOData of hoReport hoCDO vStates

End

End_Procedure

End_Object

Go back to the list of Examples

See Also

cCrystalCrystalComObject


The cCrystalPreview Class

The cCrystalPreview class is a subclass of the view that contains the cCrystalActiveXReportViewer object. This class controls the print preview for the report. Each time a report is output to the screen, a new preview object is created and displayed as a view within your MDI application. When the preview is closed, the view is destroyed. The size of the view is dynamic based on the user's screen resolution.

The handle to the preview object is obtained by calling the PreviewObject function within the cCrystalReport object. This provides a handle to your view object – this is not a COM object but a standard view object. The COM viewer is a child of this class.

This ActiveX control that actually displays the report is based on the cCrystalActiveXReportViewer class. Properties in this class control how your report is viewed. When customizing your preview, this will be the object you will want to access. A handle to this object is obtained by calling the ActiveXReportViewerObject function in the cCrystalReport class.

The event OnDisplayReport can be used when setting the COM properties of the previewer.

This example shows how to set the standard COM properties of the cCrystalActiveXReportViewer object:

Object oCrystalReport1 is a cCrystal

Set psReportName To "CustLst.rpt"

Procedure OnDisplayReport Handle hoReport

Handle hoViewer

Forward Send OnDisplayReport hoReport

Get ActiveXReportViewerObject of hoReport to hoViewer

Set ComDisplayToolbar of hoViewer To TRUE

Set ComDisplayBorder of hoViewer To FALSE

Set ComDisplayTabs of hoViewer To FALSE

Set ComDisplayBackgroundEdge of hoViewer To TRUE

Set ComDisplayGroupTree of hoViewer To FALSE

Set ComEnableGroupTree of hoViewer To FALSE

Set ComEnableStopButton of hoViewer To FALSE

Set ComEnablePrintButton of hoViewer To TRUE

Set ComEnableCloseButton of hoViewer To FALSE

Set ComEnableRefreshButton of hoViewer To FALSE

Set ComEnableExportButton of hoViewer To TRUE

Set ComEnableSearchExpertButton of hoViewer To TRUE

Set ComEnableHelpButton of hoViewer To FALSE

Set ComEnableZoomControl of hoViewer To TRUE

Set ComEnableProgressControl of hoViewer To TRUE

Set ComEnableSearchControl of hoViewer To TRUE

Set ComEnableNavigationControls of hoViewer To TRUE

Set ComEnableDrillDown of hoViewer To FALSE

Set ComEnableAnimationCtrl of hoViewer To TRUE

Set ComEnableSelectExpertButton of hoViewer To FALSE

Set ComEnablePopupMenu of hoViewer To FALSE

Set ComLaunchHTTPHyperlinksInNewBrowser of hoViewer To TRUE

End_Procedure

End_Object

You can assign your own preview object with the message AssignPreviewObject. This temporarily assigns another object as the destination of the report. The object will need to contain:

  • The property phoActiveXReportViewer containing the handle of the cCrystalActiveXReportViewer object

  • The message DisplayReport

  • A cCrystalActiveXReportViewer object

This example demonstrates how to set the preview object to a dialog:

Object oCrystalReport1 is a cCrystal

Set psReportName To "CustLst.rpt"

Procedure OnDisplayReport Handle hoReport

Forward Send OnDisplayReport hoReport

Send AssignPreviewObject of hoReport (SomeDialog_dg(Self))

End_Procedure

End_Object

See Also

Subclassing the Crystal Classes


The cCrystalExportOptions Class

The cCrystalExportOptions class is a class for export options. This is the class that will be used when you want to export your report to a file, application, MAPI, or other destinations. A handle to your export options object is obtained by calling the ExportObject function in the cCrystalReport class. Once you have this handle, you have access to a very rich set of options for exporting a report.

The event OnExportReport can be used when setting the COM properties of the export options.

This example demonstrates how to set the standard COM export options to create a rich text document:

Object oCrystalReport1 is a cCrystal

Set psReportName To "CustLst.rpt"

Procedure OnExportReport Handle hoReport

Handle hoExport

Forward Send OnExportReport hoReport

Set pbExportPrompt of hoReport to FALSE

Get ExportObject of hoReport to hoExport

Set ComDiskFileName of hoExport To "D:\Documents\MyReport.rtf"

Set ComFormatType of hoExport To crEFTExactRichText // Rich Text

End_Procedure

End_Object

See Also

pbExportPrompt

ComExport


Other RDC Classes

All of the COM classes in the RDC object model are available for use within your application. Many of these classes will never be needed. The most commonly used classes are presented here and some of them can have their objects accessed using helper functions. Those functions return an attached instance of that object, saving the work of having to manage (create, attach, use, destroy) the objects yourself – you just use them.

The objects that cannot be accessed using helper functions must be managed by you. This is not a difficult process and is described in the RDC Advanced Programming section. The advanced section also discusses the use of these objects in greater detail.

cCrystalDatabase

The cCrystalDatabase class contains properties for logging onto SQL data sources. Setting the log on properties should automatically delegate to all tables of the database.

The helper function DatabaseObject can be used to access this object.

cCrystalDatabaseTables

The cCrystalDatabaseTables class is a collection for tables within the database.

You should not need to create an object based on this class. The helper functions TableObjects and GetTableObjectByName provide an easier way to access all tables within a database.

cCrystalDatabaseTable

The cCrystalDatabaseTable class is the class for all database tables. You can set the SQL connection properties at this level, set the table location, and more. For a complete list of functions, please see the Crystal RDC documentation.

Two helper functions allow you access Database table objects: TableObjects, which returns an array of object handles, and GetTableObjectByName, which returns a handle for a named table.

This example demonstrates how you work with a database table by getting its handle:

Object oCrystalReport1 is a cCrystal

Set psReportName To "CustLst.rpt"

Procedure OnInitializeReport Handle hoReport

Handle hoTable

Forward Send OnInitializeReport hoReport

Get GetTableObjectByName of hoReport "Customer" to hoTable

If (hoTable) Begin

// do something with hoTable table object

End

End_Procedure

End_Object

This example demonstrates how you loop through all the tables that are part of your report using an array of handles:

Object oCrystalReport1 is a cCrystal

Set psReportName To "CustLst.rpt"

Procedure OnInitializeReport Handle hoReport

Handle[] hoTables

Integer iTable iNumTables

Forward Send OnInitializeReport hoReport

Get TableObjects of hoReport to hoTables

Move (SizeOfArray(hoTables)) to iNumTables

For iTable from 0 to (iNumTables – 1)

// do something with hoTables[iTable] table object

Loop

End_Procedure

End_Object

cCrystalFormulaFieldDefinitions

The cCrystalFormulaFieldDefinitions class is a collection for formula fields.

You can usually use the helper procedure AssignFormula to set your formula values. In more advanced cases, you may need to create an object based on this class.

cCrystalFormulaFieldDefinition

The cCrystalFormulaFieldDefinition class is the class for all formula fields. The main use of this class is to set the value of a formula field.

In an attempt to remove the complexity of assigning formulas, a helper method has been provided via the cCrystalReport class through the message AssignFormula.

This is an example of how to set a formula field using the helper method:

Object oCrystalReport1 is a cCrystal

Set psReportName To "CustLst.rpt"

Procedure OnInitializeReport Handle hoReport

String sText

Move "Overdue Invoices" to sText

Get CrystalString sText to sText

Forward Send OnInitializeReport hoReport

Send AssignFormula of hoReport "SubTitle" sText

End_Procedure

End_Object

cCrystalSortFields

The cCrystalSortFields class is a collection for Sort Fields. You will use this class to add/remove sort orders within the report.

In an attempt to remove the complexity of changing the sort order, a helper interface has been provided via the cCrystalReport class. The messages are DeleteSortOrder, DeleteNthSortField, and AppendSortField.

This example displays how to remove a sort order and add a new sort order:

Object oCrystalReport1 is a cCrystal

Set psReportName To "CustLst.rpt"

Procedure OnInitializeReport Handle hoReport

Forward Send OnInitializeReport hoReport

Send DeleteSortOrder of hoReport

Send AppendSortField of hoReport "Customer" "State" crAscendingOrder

Send AppendSortField of hoReport "Customer" "Zip" crAscendingOrder

Send AppendSortField of hoReport "Customer" "Name" crDescendingOrder

End_Procedure

End_Object

cCrystalParameterFieldDefinitions

The cCrystalParameterFieldDefninitions class is a collection for Parameter Fields. The main use of this class will be to set parameter options.

In an attempt to remove the complexity of setting parameters, helper functions have been provided via the cCrystalReport class. Those messages are ParamObjects, which returns an array of all cCrystalParameterFieldDefinition objects, and GetParamObjectByName which returns a cCrystalParameterFieldDefinition object by name.

This example shows how you would set the text and a list of options of a parameter field. In this case, the option is a list of customers:

Object oCrystalReport1 is a cCrystal

Set psReportName to "CustLst.rpt"

Procedure OnInitializeReport Handle hoReport

Integer iItem

Handle hoParam

Forward Send OnInitializeReport hoReport

Set ComEnableParameterPrompting of hoReport to TRUE

Get GetParamObjectByName of hoReport "CustomerList" to hoParam

If (hoParam) Begin

Set ComPrompt of hoParam to "Select customers to print:"

Set ComDisallowEditing of hoParam to TRUE

Set ComEnableMultipleValues of hoParam to TRUE

Clear Customer

Find gt Customer.Name

While (Found)

Increment iItem

// Create a list of default values. Sort can be on value or description.

Send ComSetNthDefaultValue of hoParam iItem (Trim(Customer.Name))

Set ComNthValueDescription of hoParam iItem to Customer.State

Find gt Customer.Name

End

End

End_Procedure

End_Object

cCrystalCrystalComObject (CDO)

The cCrystalCrystalComObject class is a class for the Crystal Data Object (CDO). The CDO is an ActiveX data source that allows you to define fields and records at runtime based on data that exists only at runtime. The CDO uses a variant Array to store and pass data to the report. CDOs are a data source for the report and its subreports.

You should rarely need to access this class. Helper methods in the cCrystalReport class (CreateCDO and AppendCDOData) will handle most of the complexity of using CDOs in your reports. See Using Dynamic Data Sources (link) for more information.

cCrystalApplication

The cCrystalApplication (link) class is your connection to the Crystal Reports application. This connection is required in order to use the RDC. One cCrystalApplication is required for a compiled application. All the RDC will use this one connection to the Crystal Reports Automation Server.

This connection is not created until it has been requested and, after the first request, it will remain active until the application is terminated. For this reason, the first time you run a report after starting your application you might notice a delay while this connection is being established.

The creation of the application object is handled automatically. A handle to your application object is obtained using the ApplicationObject. If ApplicationObject returns zero, you cannot run a crystal report. This might indicate that the Crystal RDC components are not properly installed on your computer.

You can access the object with the global variable ghoCrystalReportServer. If the connection has not been made, you can call the ApplicationObject function to create the connection.

In this example we demonstrate how, instead of placing your log on parameters inside the database or database table, you can place your log on parameters within the application object and have all reports use these settings to connect to your SQL database.

Object oCrystalReport1 is a cCrystal

Set psReportName to "CustLst.rpt"

Procedure SetLogonSettings

Handle hoRDCServer

Get ApplicationObject to hoRDCServer

If (hoRDCServer) Begin

Send ComLogOnServer of hoRDCServer "DllName" "ServerName" "DatabaseName" "UserID" "Password"

End

End_Procedure

End_Object

See Also

Using RDC COM Objects


Formatting Considerations

Crystal Reports expects strings, numbers, dates, and integers to be in a specific format. Functions have been provided to return those formats.

  • CrystalString – Formats a string with single quotes, double quotes, or a combination of single and double quotes. Crystal Reports expects a string to be within quotes. If your string contains single quotes, double quotes, or both this function will format it correctly before it is passed to the report.

  • CrystalNumber – Formats a number with a decimal point.

  • CrystalDate – Formats a date as (Date(yyyy,mm,dd))

  • CrystalInteger – Formats a numeric value as an integer.


Error Handling

  • Canceling a Report

  • The Crystal Report Error Handler


Canceling a Report

You can cancel a report by setting pbCanceled property to FALSE. This can be done within any of the report events (OnInitializeReport, OnDisplayReport, OnPrintreport, OnExportReport, OnError).

When a report starts (by sending RunReport or, possibly, OpenReport), the pbCanceled property is set to FALSE. If this property is set TRUE, the report will not be run. This provides the developer a way to halt the report if something goes wrong during report initialization.

For example, while building dynamic data to be used by a CDO object you may discover that the report should not be run if you don’t have any data. Setting pbCanceled to false would stop the report from being run.

Function LoadTheData Returns Variant[][]

Variant[][3] vMyData

// save data to vMyData

Function_Return vMyData

End_Function

Object oCrystalReport1 is a cCrystal

Set psReportName To "CustLst.rpt"

Procedure OnInitializeReport Handle hoReport

Handle hoCDO

Integer iRows

Variant[][3] vData

Forward Send OnInitializeReport hoReport

Get CreateCDO of hoReport "Stuff.ttx" to hoCDO

If hoCDO begin

Get LoadTheData to vData // go get my data

Move (SizeOfArray(vData)) to iRows

If (iRows=0) begin

// if no rows, let’s not bother running the report

Set pbCanceled to True

Procedure_Return

End

// Add rows to CDO object

Send AppendCDOData of hoReport hoCDO vData

End

End_Procedure

End_Object

Notice that we did not set pbCanceled to TRUE when the CreateCDO message was unable to return a CDO object. If there is no CDO object, we certainly do not want to run the report. When an error is generated and the property pbCancelIfError is TRUE (the default), pbCanceled gets set to TRUE. This cancels the report.


The Crystal Report Error Handler

Special error handling is built into the cCrystal class which:

  1. Allows you to determine if errors should be displayed in the windows message box or handled in a non-interactive manner.

  2. Sends an event to the cCrystal object for every error.

  3. Allows you to determine if any errors occurred during the running of a report.

  4. Allows you to determine if an error should cancel the report.

Suppressing Error Reporting

The Display_Error_State property determines if the errors should be displayed. The default behavior is to display all errors. This behavior may not be desired if you are running batch reports. Setting this property to false, suppresses the display of an error message box. Display_error_state only suppresses the display of the Error message box, the other error behaviors – such as the sending of OnError event and the optional canceling of a report – still occur.

Object oCrystalReport1 is a cCrystal

Set psReportName To "CustLst.rpt"

Set Display_Error_State to False // suppress interactive error message box

End_Object

The OnError Event

All errors will trigger the OnError event. This event, which receives the error number, error line and error text, does nothing by default and is intended for augmentation.

Procedure OnError integer ErrNum integer iErrLine string ErrMsg

String sReport

Get psReportName to sReport

Append_Output "F:\Crystal.log"

Writeln sReport " " ErrNum " " iErrLine " " ErrMsg

Close_Output

End_procedure

Determining if a Report had Errors

The pbHasErrors property can be used to determine if an error was encountered while running a report. It is set to false when a report is started and will be set to true if any error is encountered during the running of the report. You can query the pbHasErrors property to check if any errors have occurred.

boolean bErrors

Send RunReport of oCrystalReport1

Get pbHasErrors of oCrystalReport1 to bErrors

If (bErrors) Begin

// deal with errors

End

This example demonstrates how you can suppress the standard error message box and log errors to a file:

Procedure DoSomething

//do something

End_Procedure

Object oCrystalReport1 is a cCrystal

Set psReportName To "CustLst.rpt"

Set Display_Error_State to False // suppress interactive error message box

Procedure OnInitializeReport Handle hoReport

Boolean bOldCancelIfError

String sState

Forward Send OnInitializeReport hoReport

Get pbCancelIfError to bOldCancelIfError

Set pbCancelIfError to False // don’t cancel report if error occurs

Send DoSomething // this might generate errors

Set pbCancelIfError to bOldCancelIfError // reset property

End_Procedure

Procedure OnError integer ErrNum integer iErrLine string ErrMsg

String sReport

Get psReportName to sReport

Append_Output "C:\Crystal.log"

Writeln sReport " " ErrNum " " iErrLine " " ErrMsg

Close_Output

End_procedure

End_Object

This special error handling behavior is only used when you run a report using the RunReport method. If you do not use the RunReport method to run a report, no special error handling will be used (we assume you are performing an advanced task and that you will control error handling yourself).

Canceling a Report after an Error

If an error occurs while a report is running, you will usually want to cancel the report. If an error occurs, you usually don’t want to attempt to run the report. The default behavior of the cCrystal report class is to cancel a report as soon as an error is encountered. When an error is encountered the error handler sets the pbCanceled property to true.

In some circumstances, you may wish to run a report even if an error has occurred. The pbCancelIfError property allows you to do this. If you set pbCancelIfError to false, errors will not cause the report to stop. This may be useful when executing code within the report events, such as OnInitializeReport, where errors are expected and being handled within your code. If you do set this property to false, you are encouraged to set it back to its default state as soon as possible.

In this sample, we want to run the report even if the CDO object cannot be loaded. In addition, we will disable the error message box and display the error using the showln command. This kind of augmentation might be useful while you are debugging your report view.

Function LoadTheData Returns Variant

Variant[][3] vMyData

// save data to vMyData

Function_Return vMyData

End_Function

Object oCrystalReport1 is a cCrystal

Set psReportName To "CustLst.rpt"

Procedure OnInitializeReport Handle hoReport

Handle hoCDO

Boolean bOldCancel bOldDisplay

Variant[][3] vData

Forward Send OnInitializeReport hoReport

// remember the old settings

Get Display_Error_State to bOldDisplay

Get pbCancelIfError to bOldCancel

// If errors occur while CDO is being built, keep running the report.

Set Display_Error_State to False

Set pbCancelIfError to False

Get CreateCDO of hoReport "Stuff.ttx" to hoCDO

If hoCDO begin

Get LoadTheData to vData // go get my data

Send AppendCDOData of hoReport hoCDO vData

End

// restore the old settings

Set Display_Error_State to bOldDisplay

Set pbCancelIfError to bOldCancel

End_Procedure

Procedure OnError integer ErrNum integer iErrLine string ErrMsg

String sReport

Get psReportName to sReport

Showln sReport " " ErrNum " " iErrLine " " ErrMsg

End_procedure

End_Object

Note: This special “cancel on error” behavior is only used when you run a report using the RunReport method. If you do not use the RunReport method to run a report, no special error handling will be used (we assume you are performing an advanced task and that you will control error handling yourself).


RDC Advanced Programming

There is a great deal of functionality added to the Crystal RDC COM classes to make them easier to use. Many objects and collections are available through a property or function. We do not expect to have covered every possible scenario, which will solve everyone’s needs. However, there are plenty of examples on how to use the COM automation provided through the RDC.

  • Using RDC COM objects

  • General Guidelines

  • Working with the Export Options

  • Working with the Database

  • Working with Database Tables

  • Working with Formulas

  • Working with Parameter Fields

  • Working with Sort Fields

  • Working with Subreport Objects

  • Working with the Structure of a Report

  • Customizing the Report Cycle

  • Opening a Report

  • Outputting a Report

  • Closing a Report

  • Subclassing the Crystal Classes


Using RDC COM Objects

COM automation objects must be properly created and attached before you can use them. When you are done with the object you need to dispose of it. For the most commonly used tasks, this process is hidden. The objects are created when needed, handles are provided as needed, and the objects are destroyed when no longer needed.

For more advanced needs, you may have to handle these objects yourself. This is not a difficult process. In general, you will be using two types of COM objects: regular objects and collections objects. The use of both of these types of objects is described below.

Using COM Objects

To use a single COM object you must:

  • Create the Visual DataFlex object

  • Get the appropriate pointer (Dispatch ID) to the com object

  • Attach the Dispatch ID to DataFlex object

  • Use the object. You can now send any COM message to the object

  • Dispose of the object.

This is an example of how to create a Visual DataFlex Object and attach it to the COM object:

Handle hoDatabase

Variant vDataBase

Get Create U_cCrystalDatabase To hoDatabase

Get ComDatabase To vDatabase

Set pvComObject Of hoDatabase To vDatabase

:

Send Destroy of hoDatabase

Using COM Collection Objects

A COM collection is an object that contains an array of pointers to other objects. When you work with collections you will need to create and attach the collection object and the objects inside of the collection. Normally, you do this with two objects as follows:

  • Create two Visual DataFlex objects – the collection object and the item in the collection.

  • Get the appropriate pointer (Dispatch ID) to the COM collection object.

  • Attach the collection Dispatch ID to the DataFlex object.

  • For All items in the Collection Object.

  • Get the appropriate pointer (Dispatch ID) to the COM item.

  • Attach the item’s Dispatch ID to the DataFlex object.

  • Use the object – you can now send any COM method to the object

  • Loop

  • Dispose of the two objects created.

This is an example of working with a COM collection. Note that:

  1. ComCount is used to get the number of items in a Collection

  2. ComItem is used to get the Dispatch ID of an item in the collection

  3. ComItem is one based – it starts counting at one.

  4. The same object can be used to attach to multiple items.

Handle hoFormulaFields hoFormulaField

Variant vFormulaFields vField

Integer iItem iCount

Get Create U_cCrystalFormulaFieldDefinitions To hoFormulaFields

Get Create U_cCrystalFormulaFieldDefinition to hoFormulaField

Get ComFormulaFields of hoReport To vFormulaFields

Set pvComObject Of hoFormulaFields To vFormulaFields

Get ComCount of hoFormulaFields to iCount

// note the COM collections start their count at 1

For iItem from 1 to iCount

Get ComItem of hoFormulaFields iItem to vField

Set pvComObject of hoFormulaField to vField

// you can now send COM messages to hoFormulaField

Loop

Send Destroy of hoFormulaField

Send Destroy of hoFormulaFields


General Guidelines

Guidelines for Objects Created by the Developer

  1. If you create an object, you are responsible for it.

  • Before working with the object, you must get a COM Dispatch ID and assign it to the object.

  • When you are done working with the object, you must destroy it.

  2. Objects are created using the Create function.

  3. Objects are destroyed using the Destroy procedure.

Guidelines for Objects obtained via Helper Functions

  1. If a helper method returns an object handle, it is responsible for the object and will do all of the work for you.

  2. An object handle returned by a helper method is already attached to the appropriate COM object.

  3. You should never destroy an object acquired via a helper method.

  4. The helper functions that end with the word “object” return a single object. Those methods are:

  • ExportObject

  • DatabaseObject

  5. The helper functions that end with the word “objects” return an array of objects. Those methods are:

  • TableObjects

  • ParamObjects

  • SubReportObjects

See Also

Working with the Export Options

Working with the Database

Working with Database Tables

Working with Formulas

Working with Parameter Fields

Working with Sort Fields

Working with Subreport Objects

Working with the Structure of a Report

Customizing the Report Cycle

Opening a Report

Outputting a Report

Closing a Report


Working with the Export Options

Just about anytime you need to export a report you will need to set properties within the ExportOptions object. This object, based on cCrystalExportOptions class, contains all of the COM methods required to define your export requirements.

The ExportOptions object is obtained by using the ExportObject helper function. This returns a handle to an export object that is attached to its COM object. The event OnExportReport is most often used as the event where you set all of your export options although you could also access this object within OnInitializeReport.

This example demonstrates how to set the standard COM export options to create a rich text document:

Object oCrystalReport1 is a cCrystal

Set psReportName To "CustLst.rpt"

Procedure OnExportReport Handle hoReport

Handle hoExport

Forward Send OnExportReport hoReport

Set pbExportPrompt of hoReport to FALSE

Get ExportObject of hoReport to hoExport

Set ComDiskFileName of hoExport To "D:\Documents\MyReport.rtf"

Set ComFormatType of hoExport To crEFTExactRichText // Rich Text

End_Procedure

End_Object

The RDC ExportOptions object supports many options. For more information on cCrystalExportOptions, please see the Crystal RDC documentation.

See Also

General Guidelines

Working with the Database

Working with Database Tables

Working with Formulas

Working with Parameter Fields

Working with Sort Fields

Working with Subreport Objects

Working with the Structure of a Report

Customizing the Report Cycle

Opening a Report

Outputting a Report

Closing a Report


Working with the Database

The DatabaseObject function returns the handle to a database object (cCrystalDatabase). This object is properly attached to its object.

Object oCrystalReport1 is a cCrystal

Set psReportName To "CustLst.rpt"

Procedure OnInitializeReport Handle hoReport

Handle hoDatabase

Forward Send OnInitializeReport hoReport

Get DatabaseObject of hoReport to hoDatabase

If (hoDatabase) Begin

Send ComLogOnServer of hoDatabase "DllName" "ServerName" "DatabaseName" "UserID" "Password"

End

End_Procedure

End_Object

If the report contains subreports, you will need to get the DatabaseObject of each subreport.

See Also

General Guidelines

Working with the Export Options

Working with Database Tables

Working with Formulas

Working with Parameter Fields

Working with Sort Fields

Working with Subreport Objects

Working with the Structure of a Report

Customizing the Report Cycle

Opening a Report

Outputting a Report

Closing a Report


Working with Database Tables

The cCrystalDatabaseTable objects allow you to work with each table within a report. Two helper methods are provided to make it easy to work with these objects:

GetTableObjectByName – This searches for a table by its name and returns its handle if found. The object returned is based on the cCrystalDatabaseTable class and is attached to its COM object.

TableObjects – This returns an array of table object handles. Each object is based on the cCrystalDatabaseTable class and is attached to its COM object.

These methods only apply to a single report. If the report contains subreports, you will need to send these methods to the subreport objects.

This sample shows how you could search for a single table name and change its location:

Object oCrystalReport1 is a cCrystal

Set psReportName To "CustomerList.rpt"

Procedure OnInitializeReport Handle hoReport

Handle hoDatabaseTable

Forward Send OnInitializeReport hoReport

Get GetTableObjectByName of hoReport "customer" to hoDatabaseTable

If (hoDatabaseTable) Begin

Set ComLocation of hoDatabaseTable to "C:\AltLocation\Customer.dat"

End

End_Procedure

End_Object

This sample shows how you can change the location of all tables in a report:

Object oCrystalReport1 is a cCrystal

Set psReportName To "CustomerList.rpt"

Procedure OnInitializeReport Handle hoReport

String sTable sFilePath sNewPath

Integer iTableItem iTableCount

Handle hoDatabaseTable

Handle[] hoTables

Boolean bSuccess

Forward Send OnInitializeReport hoReport

Move "c:\AltLocation\" To sNewPath

Get TableObjects of hoReport to hoTables

Move (SizeOfArray(hoTables)) to iTableCount

// Loop through all tables

For iTableItem From 0 To (iTableCount-1)

Move hoTables[iTableItem] To hoDatabaseTable

If (hoDatabaseTable) Begin

Get ComLocation of hoDatabaseTable to sTable

Move sTable To sFilePath

Move (PathRemoveFileSpec (AddressOf (sFilePath))) To bSuccess

Move (CString (sFilePath)) To sFilePath

If (sFilePath <> "") Begin

Move (Replace (sFilePath, sTable, sNewPath)) To sTable

End

Else Begin

Move (sNewPath + sTable) To sTable

End

Set ComLocation of hoDatabaseTable to sTable

End

Loop

End_Procedure

End_Object

See Also

General Guidelines

Working with the Export Options

Working with the Database

Working with Formulas

Working with Parameter Fields

Working with Sort Fields

Working with Subreport Objects

Working with the Structure of a Report

Customizing the Report Cycle

Opening a Report

Outputting a Report

Closing a Report


Working with Formulas

The helper method AssignFormula allows you to assign a formula by name. Most of the time, this will be all you need to work with formulas within a report:

Object oCrystalReport1 is a cCrystal

Set psReportName To "CustLst.rpt"

Procedure OnInitializeReport Handle hoReport

String sName

Forward Send OnInitializeReport hoReport

Get CrystalString sName to sName

Send AssignFormula of hoReport "CompanyName" to sName

End_Procedure

End_Object

If you need to work with formulas at a lower level, you will need to create your own objects to do this. You will do this by creating and attaching a FormulaFieldDefinitions object (cCrystalFormulaFieldDefinitions). This is a collection object. You would use this object to find a Dispatch ID to a single FormulaFieldDefinition object and attach it to a DataFlex object (cCrystalFormulaFieldDefinition).

The first sample shows how you could search for a formula field and to change its value only if it is blank. This demonstrates the basics of working with COM automation objects. You will:

  1. Create all objects needed for the task (cCrystalFormulaFieldDefinitions and cCrystalFormulaFieldDefinition).

  2. Get the Dispatch ID for FormulaFieldDefinitions from the report object. Assign this to the field definitions collection object.

  3. Search for Formula by name with the COM method ComGetItemByName. This returns a Dispatch ID.

  4. If the Dispatch ID is not null, attach this ID to the FormulaFieldDefinition object.

  5. Process the FormulaFieldDefinition object as needed

  6. When finished, destroy all of the objects you created.

Object oCrystalReport1 is a cCrystal

Set psReportName To "CustLst.rpt"

Procedure OnInitializeReport Handle hoReport

Handle hoFormulaFieldDefinitions hoFormulaFieldDefinition

Variant vFormulaFields vField

String sText

Forward Send OnInitializeReport hoReport

// create all of the objects needed

Get Create U_cCrystalFormulaFieldDefinitions To hoFormulaFieldDefinitions

Get Create U_cCrystalFormulaFieldDefinition to hoFormulaFieldDefinition

// Get Dispatch ID of FormulaFieldDefinitions collection and attach.

Get ComFormulaFields of hoReport To vFormulaFields

Set pvComObject Of hoFormulaFieldDefinitions To vFormulaFields

// Search for the formula by name.

Get ComGetItemByName of hoFormulaFieldDefinitions "CompanyName" to vField

// If found, attach ID to the FormulaFieldDefinition object

If (Not(IsNullComObject(vField))) Begin

Set pvComObject of hoFormulaFieldDefinition to vField

// get the Text, if empty set it.

Get ComText of hoFormulaFieldDefinition to sText

If (trim(sText)= "") Begin

Set ComText of hoFormulaFieldDefinition to "My Test Company"

End

End

// Destroy all objects created

Send Destroy of hoFormulaFieldDefinition

Send Destroy of hoFormulaFieldDefinitions

End_Procedure

End_Object

This sample shows how you could loop through all formula fields and display each field’s name and value:

Object oCrystalReport1 is a cCrystal

Set psReportName To "CustLst.rpt"

Procedure OnInitializeReport Handle hoReport

Handle hoFormulaFieldDefinitions hoFormulaFieldDefinition

Variant vFormulaFields vField

Integer iItem iCount

String sText sFormulaName

Forward Send OnInitializeReport hoReport

// create all of the objects needed

Get Create U_cCrystalFormulaFieldDefinitions To hoFormulaFieldDefinitions

Get Create U_cCrystalFormulaFieldDefinition to hoFormulaFieldDefinition

// Get Dispatch ID of FormulaFieldDefinitions collection and attach.

Get ComFormulaFields of hoReport To vFormulaFields

Set pvComObject Of hoFormulaFieldDefinitions To vFormulaFields

Get ComCount of hoFormulaFieldDefinitions to iCount

// note the COM collections start their count at 1

For iItem from 1 to iCount

// get the Dispatch ID from the collection and attach to the field object

Get ComItem of hoFormulaFieldDefinitions iItem to vField

Set pvComObject of hoFormulaFieldDefinition to vField

Get ComFormulaFieldName of hoFormulaFieldDefinition to sFormulaName

Get ComFormulaFieldName of hoFormulaFieldDefinition to sText

// for debugging we just display to a shown console

Showln iItem ":" sFormulaName " - " sFormulaName

Loop

// Destroy all objects created

Send Destroy of hoFormulaFieldDefinition

Send Destroy of hoFormulaFieldDefinitions

End_Procedure

End_object

As a general rule, any object that you create within a procedure (using the Create method) needs to be destroyed. Any object you access via one of the helper functions is managed by the class and should not be destroyed.

See Also

General Guidelines

Working with the Export Options

Working with the Database

Working with Database Tables

Working with Parameter Fields

Working with Sort Fields

Working with Subreport Objects

Working with the Structure of a Report

Customizing the Report Cycle

Opening a Report

Outputting a Report

Closing a Report


Working with Parameter Fields

Parameter Fields are accessed by using the ParameterFieldDefinition object (cCrystalParameterFieldDefinition). Two helper methods are provided to allow you to easily access a parameter field definition object:

GetParamObjectByName – This searches for a parameter by its name and returns its handle if found. The object returned is based on the cCrystalParameterFieldDefinition and is attached to the COM object.

ParamObjects – This returns an array of parameter field object handles. Each object is based on the cCrystalParameterFieldDefinition and is attached to the COM object.

This sample shows how to search for a parameter by name and to add a list of all customer names and states as parameter selection options:

Object oCrystalReport1 is a cCrystal

Set psReportName To "CustLst.rpt"

Procedure OnInitializeReport Handle hoReport

Integer iItem

Handle hoParam

Forward Send OnInitializeReport hoReport

Set ComEnableParameterPrompting of hoReport to TRUE

Get GetParamObjectByName of hoReport "CustomerList" to hoParam

If (hoParam) Begin

Set ComPrompt of hoParam to "Select customers to print:"

Set ComDisallowEditing of hoParam to TRUE

Set ComEnableMultipleValues of hoParam to TRUE

//

Clear Customer

Find gt Customer.Name

While (Found)

Increment iItem

// Creates a list of default values.

// Sort can be on value or description.

Send ComSetNthDefaultValue of hoParam iItem (Trim(Customer.Name))

Set ComNthValueDescription of hoParam iItem to Customer.State

Find gt Customer.Name

End

End

End_Procedure

End_Object

This sample demonstrates how to list all parameter names and their prompt message:

Object oCrystalReport1 is a cCrystal

Set psReportName To "CustLst.rpt"

Procedure OnInitializeReport Handle hoReport

Handle[] hoParamObjects

Integer iItem iCount

String sParamName sPrompt

Forward Send OnInitializeReport hoReport

Get ParamObjects to hoParamObjects

Move (SizeOfArray(hoParamObjects)) to iCount

If (iCount > 0) Begin

For iItem from 0 to (iCount-1)

Get ComParameterFieldName of hoParamObjects[iItem] to sParamName

Get ComPrompt of hoParamObjects[iItem] to sPrompt

// for debug purposes we just use showln

Showln iItem ":" sParamName " - " sPrompt

Loop

End

End_Procedure

End_Object

One could also use the ParameterFieldDefinitions collection object to do perform the same tasks but the GetParamObjectByName and ParamObejcts methods are easier to use. You don’t have to worry about creating, attaching or destroying objects.

This sample shows how you would perform the parameter search by name without using any of the helper functions. Please note that there is no good reason to work at this level – this sample if provided so you can compare the differences between using the helper methods and the built in COM methods.

Object oCrystalReport1 is a cCrystal

Set psReportName To "CustLst.rpt"

Procedure OnInitializeReport Handle hoReport

Integer iItem

Handle hoParam hoParameterFieldDefinitions

Variant vParam vParamFields

Forward Send OnInitializeReport hoReport

Set ComEnableParameterPrompting of hoReport to TRUE

// create the objects you will need

Get Create U_cCrystalParameterFieldDefinitions to hoParameterFieldDefinitions

Get Create U_cCrystalParameterFieldDefinition to hoParam

// attach the COM pointer for ParameterFieldDefinitions

Get ComParameterFields of hoReport To vParamFields

Set pvComObject Of hoParameterFieldDefinitions To vParamFields

// use the COM method ComGetItemByName to find pointer to Param field

Get ComGetItemByName of hoParameterFieldDefinitions "CustomerList" to vParam

// if pointer is not null, attach it to the parameter field object

If (Not(IsNullComObject(vParam))) Begin

Set pvComObject Of hoParam To vParam

Set ComPrompt of hoParam to "Select customers to print:"

Set ComDisallowEditing of hoParam to TRUE

Set ComEnableMultipleValues of hoParam to TRUE

//

Clear Customer

Find gt Customer.Name

While (Found)

Increment iItem

// Creates a list of default values.

// Sort can be on value or description.

Send ComSetNthDefaultValue of hoParam iItem (Trim(Customer.Name))

Set ComNthValueDescription of hoParam iItem to Customer.State

Find gt Customer.Name

End

End

// destroy the objects you created

Send Destroy of hoParam

Send Destroy of hoParameterFieldDefinitions

End_Procedure

End_Object

See Also

General Guidelines

Working with the Export Options

Working with the Database

Working with Database Tables

Working with Formulas

Working with Sort Fields

Working with Subreport Objects

Working with the Structure of a Report

Customizing the Report Cycle

Opening a Report

Outputting a Report

Closing a Report


Working with Sort Fields

Helper methods have been added to the cCrystalReport to allow you to manage changes in sort orders. Those methods are:

  • DeleteSortOrder – This removes all sort fields

  • AppendSortField – This appends a sort field to sort order

  • DeleteNthSortField – This deletes the Nth field from the sort order

Most of the time you will delete the entire sort order and build a new one as follows:

Object oCrystalReport1 is a cCrystal

Set psReportName to "CustLst.rpt"

Procedure OnInitializeReport Handle hoReport

Forward Send OnInitializeReport hoReport

Send DeleteSortOrder of hoReport

Send AppendSortField of hoReport "Customer" "State" crAscendingOrder

Send AppendSortField of hoReport "Customer" "Zip" crAscendingOrder

Send AppendSortField of hoReport "Customer" "Name" crDescendingOrder

End_Procedure

End_Object

In some cases, you may wish to remove a field from the sort order. In this example we remove item 3 from the sort order (assume it is the last item) and replace it with something else:

Object oCrystalReport1 is a cCrystal

Set psReportName to "CustLst.rpt"

Procedure OnInitializeReport Handle hoReport

Forward Send OnInitializeReport hoReport

// assume item three is the last sort field

Send DeleteNthSortField of hoReport 3

Send AppendSortField of hoReport "Customer" "State" crAscendingOrder

End_Procedure

End_Object

If you need to perform more complex sort handling, you will need to create a SortFields objects (cCrystalSortFields). This is a COM collection (items start at one and the values are Dispatch IDs that must be attached to a DataFlex object before they can be used). Each item contains a SortField COM object (cCrystalSortField).

This sample shows how you can use this object to make sure you are actually deleting the last field in the sort order:

Object oCrystalReport1 is a cCrystal

Set psReportName to "CustLst.rpt"

Procedure OnInitializeReport Handle hoReport

Handle hoSortFields

Variant vSortFields

Integer iCount

Forward Send OnInitializeReport hoReport

// create and attach sort fields collection object

Get Create U_cCrystalSortFields To hoSortFields

Get ComRecordSortFields of hoReport To vSortFields

Set pvComObject Of hoSortFields To vSortFields

Get ComCount of hoSortFields to iCount

Send DeleteNthSortField of hoReport iCount

Send AppendSortField of hoReport "Customer" "State" crAscendingOrder

Send Destroy of hoSortFields

End_Procedure

End_Object

This sample searches through the sort fields looking for a particular table and field name. If found, this field is deleted from the sort list. To do this we must:

  1. Loop through all items in the SortFields COM collection.

  2. Attach each item to SortField object.

  3. Get the TableFieldDefinition pointer from the SortField object and attach it to a TableFieldDefintion object.

  4. Check properties in the TableFieldDefinition object for table name and field name.

In this sample, all of the objects needed are created at the start. They are attached to COM IDs as needed. When complete all of the objects are destroyed. The techniques used here can be applied to other COM objects.

Object oCrystalReport1 is a cCrystal

Set psReportName to "CustLst.rpt"

Procedure OnInitializeReport Handle hoReport

Handle hoSortFields hoSortField hoTableField

Integer iItem iCount

Variant vSortFields vSortField vTableField

Variant vDispatch

String sTable sField

Forward Send OnInitializeReport hoReport

// create all objects needed for this task

Get Create U_cCrystalSortFields to hoSortFields

Get Create U_cCrystalSortField to hoSortField

Get Create U_cCrystalDatabaseFieldDefinition to hoTableField

// attach COM SortFields to hoSortFields

Get ComRecordSortFields of hoReport To vSortFields

Set pvComObject Of hoSortFields To vSortFields

Get ComCount of hoSortFields to iCount

// note that COM collections start at 1

For iItem From 1 To iCount

// get sort field item (dispatch) and attach it to the sort field object

Get ComItem of hoSortFields iItem to vSortField

Set pvComObject of hoSortField to vSortField

// get the table item (dispatch) and attach it to the table field object

Get ComField of hoSortField to vTableField

Set pvComObject of hoTableField to vTableField

Get ComTableAliasName of hoTableField to sTable

Get ComDatabaseFieldName of hoTableField to sField

If (lowercase(sTable)="customer" and ;

lowercase(sField)="name") Begin

Send ComDelete Of hoSortFields iItem

Move iCount to iItem // break out of loop

End

Loop

// destroy all objects we created

Send Destroy of hoTableField

Send Destroy of hoSortField

Send Destroy of hoSortFields

End_Procedure

End_Object

See Also

General Guidelines

Working with the Export Options

Working with the Database

Working with Database Tables

Working with Formulas

Working with Parameter Fields

Working with Subreport Objects

Working with the Structure of a Report

Customizing the Report Cycle

Opening a Report

Outputting a Report

Closing a Report


Working with Subreport Objects

Before using a subreport object you must get a handle to the subreport object. Subreport objects are represented as Report objects (cCrystalReport). Once you have a handle to the report you can send many of the same types of messages you would send to a main report allowing you to assign formulas, change sort order, etc.

Two helper methods are provided to allow you to easily access a subreport handle:

GetSubReportObjectbyName – This searches for a subreport by its name and returns its handle if found. The object returned is based on the cCrystalReport and is attached to the COM object.

SubReportObjects – This returns an array of subreport object handles. Each object is based on the cCrystalReport and is attached to the COM object.

This example shows how you could assign a formula to a named sub-report:

Object oCrystalReport1 is a cCrystal

Set psReportName to "CustLst.rpt"

Procedure OnInitializeReport Handle hoReport

Handle hoTimeDetail

String sCustomerName

Forward Send OnInitializeReport hoReport

Get GetSubReportObjectbyName of hoReport "TimeDetail" to hoTimeDetail

If (hoTimeDetail) Begin

Get CrystalString Customer.Name to sCustomerName

Send AssignFormula of hoTimeDetail "CompanyName" sCustomerName

End

End_Procedure

End_Object

This sample shows how you could assign the same formula to all subreports:

Object oCrystalReport1 is a cCrystal

Set psReportName to "CustLst.rpt"

Procedure OnInitializeReport Handle hoReport

Integer iReportItem iReportCount

Handle hoSubReport

Handle[] SubReportArray

String sCustomerName

Forward Send OnInitializeReport hoReport

Get SubReportObjects to SubReportArray

Move (SizeOfArray(SubReportArray)) to iReportCount

For iReportItem From 0 To (iReportCount-1)

Move SubReportArray[iReportItem] to hoSubReport

If (hoSubReport) Begin

Get CrystalString Customer.Name to sCustomerName

Send AssignFormula of hoSubReport "CompanyName" sCustomerName

End

Loop

End_Procedure

End_Object

See Also

General Guidelines

Working with the Export Options

Working with the Database

Working with Database Tables

Working with Formulas

Working with Parameter Fields

Working with Sort Fields

Working with the Structure of a Report

Customizing the Report Cycle

Opening a Report

Outputting a Report

Closing a Report


Working with the Structure of a Report

If you review the diagram of RDC classes, you will see that there are series of objects defined on the left side and a series of objects defined on the right side. We stated that most of the time you will be working with the “right side objects”. Those are the objects used to set sort orders, define formulas, define tables, etc. The left side objects can be used to query the actual structure of a report. While most of the time you will not need to work with the report structure, you can do so as demonstrated by this sample.

In this sample, we have created a procedure that traverses the entire structure of a report searching for sections of a specific type. If those sections are found, they will be suppressed. This could be used to suppress the detail section of any report.

// Mark all section of passed type as hidden

Procedure MakeSectionsInvisible integer eSectionType

Handle hoReport

Handle hoAreas hoArea

Variant vAreas vArea

Integer iAreas iArea iKind

Get ReportObject to hoReport

Get Create of hoReport U_cCrystalAreas to hoAreas // areas collection

Get Create of hoReport U_cCrystalArea to hoArea // area object

Get ComAreas of hoReport to vAreas // areas dispatch

Set pvComObject Of hoAreas To vAreas // attach dispatch to object

Get ComCount Of hoAreas To iAreas

For iArea from 1 to iAreas

Get ComItem of hoAreas iArea to vArea

Set pvComObject Of hoArea To vArea // attach area dispatch to object

Get ComKind of hoArea to iKind // is it the right kind, if so, suppress

If (iKind=eSectionType) Begin

Set ComSuppress of hoArea to True

End

Loop

Send Destroy of hoArea //destroy objects used

Send Destroy of hoAreas

End_Procedure // MakeSectionsInvisible

This procedure could be called inside of OnInitializeReport as follows:

Object oCrystalReport1 is a cCrystal

Set psReportName to "CustLst.rpt"

Procedure OnInitializeReport handle hoReport

:

// Set the details section to visible/invisible.

Send MakeSectionsInvisible crDetail

:

End_Procedure

End_Object

This sample demonstrates the flexibility of the RDC object model.

See Also

General Guidelines

Working with the Export Options

Working with the Database

Working with Database Tables

Working with Formulas

Working with Parameter Fields

Working with Sort Fields

Working with Subreport Objects

Customizing the Report Cycle

Opening a Report

Outputting a Report

Closing a Report


Customizing the Report Cycle

Normally, a report is run by sending the RunReport method to a cCrystal object. The Runreport method looks something like this:

Procedure RunReport

Boolean bOK

Set pbHasErrors to false

// direct to local error handler

Set Old_Error_Object_id to Error_Object_id

Move self to Error_Object_id

Get OpenReport to bOK

If (bOK) Begin

Send OutputReport

Send CloseReport

End

// restore error handler

Get Old_Error_Object_id to Error_Object_id

End_Procedure // RunReport

RunReport manages the entire report cycle. It opens the report, outputs the report and closes the report. In advanced situations you may wish to manage these three steps yourself by sending the OpenReport, OutputReport and CloseReport methods. If you do this, it becomes your responsibility to properly manage the report. For example, if you open the report, it is your responsibility to close it.

See Also

General Guidelines

Working with the Export Options

Working with the Database

Working with Database Tables

Working with Formulas

Working with Parameter Fields

Working with Sort Fields

Working with Subreport Objects

Working with the Structure of a Report

Opening a Report

Outputting a Report

Closing a Report


Opening a Report

The OpenReport function is used to open a report. It creates a COM report object (cCrystalReport) and attempts to open a report defined by the psReportName property. If the report was successfully opened, it calls the OnInitializeReport event. If all steps are successful and the report was not canceled, the function returns True. At this point, your report is opened and initialized. You can obtain the handle of the report object with the ReportObject function.

You can use this function to open a report to make a change to it or query it without outputting it.

If OpenReport returns false, no report object has been created. If it returns true, a report object exists and must be closed by sending CloseReport.

// This code should be placed inside the cCrystal object...

Procedure ScanReport String sTable

Handle hoReport hoTable

Boolean bOK

String sTable

Set psReportName to "CustList.rpt"

Get OpenReport to bOK

If (bOK) Begin

Get ReportObject to hoReport

If (hoReport) Begin

Get GetTableObjectByName of hoReport sTable to hoTable

If (hoTable) Begin

// Table exists. Do something.

End

End

// manually close the report when done

Send CloseReport

End

End_Procedure

See Also

General Guidelines

Working with the Export Options

Working with the Database

Working with Database Tables

Working with Formulas

Working with Parameter Fields

Working with Sort Fields

Working with Subreport Objects

Working with the Structure of a Report

Customizing the Report Cycle

Outputting a Report

Closing a Report


Outputting a Report

The OutPutReport procedure calls various output messages depending if your report is output to a printer, display or file. The peOutputDestination property determines where the report will be handled as follows:

peOutputDestination value | Outputs report by Calling | Event Called
PRINT_TO_WINDOW | DisplayReport | OnDisplayReport
PRINT_TO_PRINTER | PrintReport | OnPrintReport
PRINT_TO_FILE | ExportReport | OnExportReport

A report must be opened using the OpenReport message in order to use this method.

See Also

General Guidelines

Working with the Export Options

Working with the Database

Working with Database Tables

Working with Formulas

Working with Parameter Fields

Working with Sort Fields

Working with Subreport Objects

Working with the Structure of a Report

Customizing the Report Cycle

Opening a Report

Closing a Report


Closing a Report

The CloseReport procedure closes your report. It destroys the cCrystalReport object, which removes all the other COM objects that have been created using the helper interfaces. If you have successfully opened a report using OpenReport, you must close it yourself.

Note that this does not close the preview window. The report that has been generated to your preview view can still be viewed in this view.

See Also

General Guidelines

Working with the Export Options

Working with the Database

Working with Database Tables

Working with Formulas

Working with Parameter Fields

Working with Sort Fields

Working with Subreport Objects

Working with the Structure of a Report

Customizing the Report Cycle

Opening a Report

Outputting a Report


Subclassing the Crystal Classes

The cCrystal class has 3 properties which can be changed to use another class which make subclassing easy.

  • phcReportObject – Class to be used for report and subreport objects.

  • phcPreviewObject – Class to be used for preview object.

  • phcExportObject – Class to be used for export object.

This example demonstrates how to subclass the Export object and the cCrystal classes:

//cMyCrystalReport.pkg

Use cCrystal.pkg

Class cMyCrystalExportOptions is a cCrystalExportOptions

Procedure Construct_Object

Forward Send Construct_Object

Set ComDiskFileName To "D:\Documents\MyReport.rtf"

Set ComFormatType To crEFTExactRichText // Rich Text

End_Procedure

End_Class

Class cMyCrystal is a cCrystal

Procedure Construct_Object

Forward Send Construct_Object

Set phcExportObject to U_cMyCrystalExportOptions // custom export object

Set pbAutoLocateDFFiles to FALSE // Do not automatically locate DF Files

End_Procedure

End_Class

Note: When subclassing the phcPreviewObject, your subclass must contain the following:

  • The property phoActiveXReportViewer containing the handle of the cCrystalActiveXReportViewer object.

  • The message OnDisplayReport to attach the report to the cCrystalActiveXReportViewer object.

  • The property Label.


Converting from CrystalReport to cCrystal

The complexity of converting your reports to use the cCrystal class relates directly to the amount of complexity you use in your CrystalReport. You may find that in using the cCrystal much of the code you were using will no longer be required. Setting formulas, sort order, and selection formulas do not use the same names as the previous class did.

CrystalReport is still part of Visual DataFlex packages and can still be used when using older reports. You are not required to upgrade all of your reports to the new class.

If you do upgrade to cCrystal, the way you deploy your reports also needs to change. Please refer to the Crystal RDC documentation for information on distributing DLLs.

As for your Crystal Report, you do not need to change anything to use it with the cCrystal class.

Different Properties and Methods

Some of the properties and methods from the old CrystalReport class were renamed or the parameters they expect/return have changed or they were completely replaced by new properties and methods.

This is a list of some of the most used properties and methods that you will need to change when converting a CrystalReport object into a cCrystal object.

Formula and SelectionFormula

If you used Set Formula and Set SelectionFormula, then changing to the new class will require a few changes.

Old Method

Object oCrystalReport1 is a CrystalReport

Set Report_Name To "CustLst.rpt"

Procedure OnInitializeReport

Forward Send OnInitializeReport

Set Formula "Company" to ('"'+Trim(System.Name)+'"')

Set SelectionFormula to ("{OrderHea.Order_No} = OrderHea.Order_No)

End_Procedure

End_Object

New Method

Object oCrystalReport1 is a cCrystal

Set psReportName To "CustLst.rpt"

Procedure OnInitializeReport Handle hoReport

String sName

Forward Send OnInitializeReport hoReport

Get CrystalString Customer.Name to sName

Send AssignFormula of hoReport "Company" sName

Set ComRecordSelectionFormula of hoReport to ("{Customer.Customer_number} =" + string(Customer.Customer_number))

End_Procedure

End_Object

NSortField and AddSortField

If you used NSortField and AddSortField, you will need to adjust your code when changing to the new class.

Old Method

Object oCrystalReport1 is a CrystalReport

Set Report_Name To "CustLst.rpt"

Procedure OnStartPrinting

Integer iNumSortFields iCurSortField

//*** Delete old sort order

Get NSortFields To iNumSortFields

For iCurSortField From 0 To (iNumSortFields-1)

Send DeleteNthSortField 0

Loop

//*** Setup new sort order

Send AddSortField "{Customer.Name}" PE_SF_ASCENDING

End_Procedure // OnStartPrinting

End_Object

New Method

Object oCrystalReport1 is a cCrystal

Set psReportName To "CustLst.rpt"

Procedure OnInitializeReport Handle hoReport

Integer iNumSorts

Forward Send OnInitializeReport hoReport

Send DeleteSortOrder of hoReport

Send AppendSortField of hoReport "Customer" "State" crAscendingOrder

Send AppendSortField of hoReport "Customer" "Zip" crAscendingOrder

Send AppendSortField of hoReport "Customer" "NameX" crDescendingOrder

End_Procedure

End_Object

Note that now it is not necessary to get the number of sort fields. However, if you need to do that, use ComCount as shown in the example below:

Object oCrystalReport1 is a cCrystal

Set psReportName To "CustLst.rpt"

Procedure OnInitializeReport handle hoReport

Handle hoRecordSort

Integer iSortFields iCounter

Forward Send OnInitializeReport hoReport

Get RecordSortObject of hoReport to hoRecordSort

Get ComCount of hoRecordSort To iSortFields

// Only add sort fields if none is defined

If (iSortFields = 0) Begin

Send AppendSortField of hoReport "Customer" "State" crAscendingOrder

Send AppendSortField of hoReport "Customer" "Zip" crAscendingOrder

Send AppendSortField of hoReport "Customer" "NameX" crDescendingOrder

End

End_Procedure // OnInitializeReport

End_Object // oMyReport

Report_Data_Path

Report_Data_Path should be replaced by the combination of the property pbAutoLocateDFFiles and the function LocateDFFiles when changing to the new class. By using this new method, the new class can automatically find where the data files are.

Old Method

Object oCustCrystalReport Is A CrystalReport

Set Report_Name To "OrdrLst.rpt"

Set Report_Data_Path To "C:\MyData"

End_Object // oCrystalOrdrRpt1

New Method

Object oCrystalReport1 is a cCrystal

Set psReportName to "CustLst.rpt"

Set pbAutoLocateDFFiles to False

Procedure OnInitializeReport Handle hoReport

Handle hoTable

Boolean bOk

Forward Send OnInitializeReport hoReport

Get LocateDFFiles of hoReport to bOk

If (not(bOk)) Begin

// deal with it

End

End_Procedure

End_Object

Report_Name and Output_Destination

Report_Name and Output_Destination should be replaced by the properties psReportName and peOutputDestination when changing to the new class.

With the new class Report_Name and Output_Destination are supported as backwards compatible methods. While we suggest that you change your code, this is not a requirement. This means that the simplest Crystal Reports require no changes at all, except for changing the class name to cCrystal.

Old Method

Object oCustCrystalReport Is A CrystalReport

Set Report_Name To "OrdrLst.rpt"

Set Output_Destination To CRW_PRINT_TO_WINDOW

End_Object // oCrystalOrdrRpt1

New Method

Object oCustCrystalReport Is A cCrystal

Set psReportName To "OrdrLst.rpt"

Set peOutputDestination To PRINT_TO_WINDOW

End_Object // oCrystalOrdrRpt1

Notice that the values taken by peOutputDestination have also changed:

peDesintation value | Output_Destination value
PRINT_TO_WINDOW | CRW_PRINT_TO_WINDOW or WQL_PRINT_TO_WINDOW
PRINT_TO_PRINTER | CRW_PRINT_TO_PRINTER or WQL_PRINT_TO_PRINTER
PRINT_TO_FILE | CRW_PRINT_TO_FILE or WQL_PRINT_TO_FILE

Run_Report

Run_Report should be replaced by RunReport when changing to the new class.

With the new class Run_Report is supported as backwards compatible method. While we suggest that you change your code, this is not a requirement. This means that the simplest Crystal Reports require no changes at all, except for changing the class name to cCrystal.

Old Method

Send Run_Report of oCrystalReport1

New Method

Send RunReport of oCrystalReport1


WinPrint 2

Using WinPrint Reports in Applications

  • Building WinPrint 2 Reports

  • Using the Report Viewer

  • Comparing WinPrint 1 with WinPrint 2

  • Converting a WinPrint 1 Report

  • Using WinPrint 2 without the cWinReport2 Class


Building WinPrint 2 Reports

Use the Visual DataFlex Studio to build and test your WinPrint 2 reports. To build a report, activate the Studio’s “Create New” dialog and select “WinPrint2 Report Wizard” from the “Report” tab. Click OK then follow the wizard’s instructions. A WinPrint 2 report view will be created and added to your application.

WinPrint 2 reports use the cWinReport2 class to wrap all of your report definition code into logical report sections with an event driven interface.

For more information refer to: WinPrint 2 Report Wizard and cWinReport2.


Using the Report Viewer

To allow end users to preview how your WinPrint 2 report will appear on the printed page, your application should use the Report Viewer. The report viewer renders a report in WYSIWYG (what you see is what you get) fashion to the screen.

Your application can specify that a report is sent to the viewer by setting the Output_Device_Mode property in your cWinReport2 object to PRINT_TO_WINDOW. For example…

Set Output_device_mode to PRINT_TO_WINDOW

The report viewer will display your report one page at a time in a special preview window. This window is free floating and can be re-sized and re-positioned on your screen as desired. The report viewer supports…

  • Zooming and scrolling: You can zoom in and out to change the report view’s magnification and scroll up, down, left and right to see parts of the page that do not fit in the window.

  • Page forward, page backward, go to first, go to last, and jump to page. Use these features to navigate the pages of a multi-page report.

  • Print hard copy. The Report Viewer supports sending the previewed report to the printer. You can select the desired page range, number of copies, or print to file.

  • Copy to clipboard. Use this feature to copy part of the current page to the clipboard as a graphic. You can use the mouse to click and drag to select a region of the page to be copied.

The report viewer is active as soon as the first page has been sent to it by your application. Users can operate the viewer normally and perform every operation while your report is being sent to the viewer. The only operation not available until the entire report has been sent to the viewer is the Print operation.

See Also

The Report Viewer Toolbar

Report Viewer Hot Keys

Printing from the Report Viewer


The Report Viewer Toolbar

The following is a description of each item (left to right) on the viewer’s toolbar.

Go to First Page

Click this button to display page 1 of your report

Go to Previous Page

Click this button to go back one page, e.g. from page 2 to page 1.

Go to Next Page

Click this button to go forward one page, e.g. from page 2 to page 3.

Go to Last Page

Click this button to go to the final page of your report. Note, if your application is still generating the report, then the last page is only the last page that has been generated so far. This value will increase as the application continues to generate pages.

Cancel Reading Records

If you do not want to wait until the entire report has been generated then click the cancel button. This instructs the application to stop generating more report pages. You can still continue to preview all of the pages generated so far.

Print

Click this button to send the report you are previewing to the printer. See Printing from the Report Viewer for more information.

Zoom

Use the zoom combo to change the report’s magnification. You can select one of the magnification percentages, or “full page”.

When you select a percentage size then the report is sized relative to the estimated size of a printed page. If the page does not fit in the viewer window, then it is clipped. You can use the scrollbars to bring different parts of a clipped page into view.

When you select “full page” the report is resized to the maximum height & width available in the viewer’s window. Resizing the viewer’s window will then automatically resize the report pages.

Jump to Page

To jump to a specific page number of your report, enter the desired page number in this box, then click the “Jump to Page Number” button situated on the right of the entered page number.

Copy Image to Clipboard

Click this button to copy all or part of the current report page to the clipboard. When you click this button, the mouse cursor changes into the cross-hair shape. Position the mouse on the top left corner of the page section you want to copy, then click and drag the mouse to the bottom right corner.

The selected region will be copied to the clipboard as a bitmap image. This can be pasted into any document which supports this format.


Report Viewer Hot Keys

The viewer supports the following keyboard shortcuts…

Key Combination | Action
Ctrl+Home | Page - First
PgUp | Page - Previous
PgDn | Page - Next
Crtl+End | Page - Last
Up Arrow | Scroll - Up
Down Arrow | Scroll - Down
Left Arrow | Scroll - Left
Right Arrow | Scroll - Right


Printing from the Report Viewer

Printing from the Viewer

The Report Viewer supports sending the previewed report to the printer. When you click the Print button on the viewer’s toolbar the Print Options dialog is displayed. The text below discusses each section of the Print Options dialog…

Current Printer

This text box displays the name of the printer that the report is sent to. The destination printer will have already been selected before the report was previewed and cannot be changed from the report viewer.

The characteristics of the selected printer determine the layout of each page of your report. To print a report to a different printer, the report viewer must be closed and the report regenerated.

Print to File

Select this checkbox to send the output to a file on disk instead of a printer. You will be prompted to enter a filename and folder location. The format of the file is the standard windows print to file format.

Print Range

Use page range to select the portion of the report that you want to print. You can select print all pages, a range of pages, or only the current page.

Copies

Use this box to enter the number of copies to be printed.

Collate

The Collate check box indicates whether the you want to collate the pages if multiple copies are being printed.

OK

Prints the report using the options you have specified in this dialog.

Cancel

Closes the print options dialog without printing.


Comparing WinPrint 1 with WinPrint 2

  • Report Viewer Changes

  • Report Progress

  • Image Support

  • Graphic File Locations

  • Differences in Rendering between WinPrint 1 & 2

Report Viewer Changes

The most obvious change introduced with WinPrint 2 is the viewer that is used to preview a report on screen. WinPrint 2's viewer is far superior to the old WinPrint 1 viewer. Below is a list of the major differences…

  • Faster previewing: WinPrint 2 now launches the viewer as soon as the first page has been generated. This gives users almost instant response. With WinPrint 1 the viewer is not launched until the entire report is ready.

  • Background Previewing: The WinPrint 2 viewer can be used to preview a report even as the report is being generated. For long reports, you will see the total page count incrementing in the back ground as you browse the pages that have already been generated.

  • Faster Paging: WinPrint 2's viewer allows you to browse from one page to another much faster than in WinPrint 1.

  • Modern Interface: The viewer's interface has been modernized.

  • Consistent Interface: The viewer's interface has been changed to match the Crystal Reports viewer. This means that if your application uses a combination of WinPrint2 and Crystal Reports, the viewer interfaces will be more consistent.

  • More Intuitive: Operation of WinPrint 2's viewer is more intuitive that with WinPrint 1.

  • Jump to Page: The viewer now supports toolbar buttons to jump to the first page or last page of your report. It also supports jumping to an arbitrary page number.

  • Your report's Report_Title property is now displayed in the viewer's title bar. e.g. the following line in your report object would cause the string to be displayed in the viewer's title bar…

Set Report_Title to "Customer Names Report"

For more information see Using the Report Viewer.

Report Progress

Because WinPrint 2 reports begin output as soon as the first page is ready the viewer displays the report status in the viewer's status bar. In WinPrint 1, the report's status was displayed in a popup dialog.

To stop a report before it has been completely generated, you click the stop button in the Viewer's toolbar.

Image Support

The DFWriteBmp command now supports printing JPG files. e.g.

DFWriteBMP "logo.jpg" 14 7 12 9 DFGR_NOPOS

In addition to JPG, WinPrint 2 also supports (.rle) graphics, icons (.ico) Windows meta files (.wmf) and advanced Windows meta files (.emf).

Graphic File Locations

WinPrint 1 used a single registry setting to specify the location for all graphic files (bitmaps, jpeg's, etc). WinPrint2 uses the Workspace to find bitmaps used in your reports. This gives much better control.

Users should be aware that this change can have the side-affect that converted reports now start showing a different bitmap due to the difference in pathing now applied to a report.


Differences in Rendering between WinPrint 1 & 2

Margins

WinPrint 1 miscalculated left/right page margins. This has been corrected in WinPrint2. This may affect where word-wrapping occurs in your reports.

The total print margin is calculated by adding the printer's non-printable region plus the user-defined report margins (as specified by the DFSetMargins command).

WinPrint1 did not add the printer's non-printable left and right margin to the user-defined left and right margin.

This can be illustrated by defining a report where the user-defined margins are set to 0 or a very small value using the DFSetMargins message. e.g.

Send DFSetMargins of WinPrintId 0.5 0.5 0.5 0.5

The following graphics illustrate the difference between WinPrint1 and WinPrint2 for the same report. The yellow area represents the printer's non-printable region (the region of a page that the printer cannot reach).

WinPrint 1

WinPrint 2

In WinPrint 1, the incorrect rendering is only observed when the report is rendered in the viewer. When the report is sent to printer, it is properly scaled and positioned to account for the printer's non-printable region.

Clipping to Margin

WinPrint 1 would print text outside the user-defined right margin. This bug has been corrected in WinPrint2 so that text which is specified to print beyond the right margin is now clipped.

Headers/Footers

WinPrint 1 miscalculated the non-printable header and footer regions of a report page. This resulted in headers being written to the wrong location and footers sometimes not being printed at all.

The non-printable header & footer is the region at the top and bottom of a page where a specific printer in unable to print. Each printer will have a different sized non-printable regions of a page.

Because of the way WinPrint1 miscalculated these regions, it is possible that you have made positional adjustments in your report to ensure that footers are rendered on a page. If this is the case, then when your report is rendered in WinPrint2 the footer location will be over-adjusted.

Bitmaps not Transparent

WinPrint 1 applies an transparency formula to bitmaps. This is not supported in WinPrint 2.

Line Spacing

Calculation of the line height for successive DFWriteln commands was slightly too small in WinPrint 1. This has been corrected in WinPrint 2.

Maximum Length in DFWritePos

The maximum length parameter of the DFWritePos command was not being calculated correctly in WinPrint 1. This is especially evident when bold text is printed. This has been corrected in WinPrint 2. As a result, you may notice some bolded text is clipped when you convert your reports to WinPrint 2. You will need to adjust the maximum width parameter of these commands accordingly.

Line Drawing

The line thickness calculation in the DFWriteXYLine command is more accurate in WinPrint 2.

Rectangle/Ellipse Drawing

In WinPrint 1, rectangles (and ellipses) were drawn so that the center of the line was drawn at the coordinates that were given. The diagram below illustrates how a rectangle was designed to be 6 CM wide at a position of 4 CM from the margin with a thickness of 1CM, was actually drawn 7 CM wide and from a position of 3.5 CM from the margin.

In WinPrint II, this has been corrected, so that the rectangle is drawn within the area that you supply. That is, it will now be 6 CM wide and start from 4 CM from the margin. Because of this change, rectangles and ellipses will be smaller than they were in WinPrint I, and you may need to adjust your code. The formula for this is quite simple: the width and height should be the incremented by the width of the line; the position should be decremented by half the width of the line. So, in this example, the width would need to be set to 7 (calculated as 6 + 1) and the position to 3.5 (calculated as 4- (1/2)).

If you used a small line width, then you probably won’t notice this change.

Header Size Adjustment With Graphics

When a graphic item, such as a rectangle of bitmap, is drawn inside a header section, then the section's height is adjusted if necessary to accommodate the graphic. In WinPrint 1, this adjustment did not take into consideration the vertical offset of the graphic from the top of the header. This would cause the graphic to overflow out of the bottom of the header. WinPrint 2 correctly considers the vertical offset of the graphic when calculating the height of the header.

Charting

WinPrint 2 does not support charting. Reports that use charting should continue to use WinPrint 1.

Floating Text vs Fixed Text

WinPrint 2 introduces a strong distinction between text that is written using DFWritePos and text written using DFWrite.

Text written using DFWritePos is considered to be 'fixed' text. i.e. its horizontal position is explicitly fixed.

Text written using DFWrite is considered 'floating' text. i.e. its horizontal position is variable depending on what other floating text has come before it.

In WinPrint 2, the horizontal position of floating text is not affected by fixed text. It is still affected by other floating text on the same line.

In WinPrint 1 the following code:

DFWritePos "Customer: " 0.0

DFWrite Customer.Name

Would be output as...

"Customer: John Smith"

In WinPrint 2, the position of the floating text (customer.name) would not be affected by the fixed text. This is different from WinPrint 1.

The position of floating text can be affected by the current header justification and various other attributes such as DFGR_SETPOS. This is the same as WinPrint 1.

The position of fixed text is not affected by the current header justification and various other attributes such as DFGR_SETPOS. This is different from WinPrint 1.

The result of these changes means that with WinPrint 2 you can always predict the horizontal position of fixed text. This was not always the case with WinPrint 1.


Converting a WinPrint Report

Converting a WinPrint 1 Report

This section discusses the changes required to modify a report that uses Visual DataFlex's old WinPrint 1 technology into a report using WinPrint 2.

The easiest WinPrint reports to convert are the ones which use the WinReport class. To convert these to WinPrint 2 reports requires changes to only a few lines of code.

Step 1 – Use Package

WinPrint2 is encapsulated in a new report class called cWinReport2. The first change you need to make is to include the package file which contains cWinPrint2's class definition.

Use cWinReport2.pkg

Put this use statement in the top section of your report view's Outer-Component code.

Step 2 – Report Object Declaration

Next you need to use the cWinReport2 class in your report object declaration. For example…

Object oCustomer_WinPrint is a cWinReport2 // WinReport

…

End_Object

Visual DataFlex supports applications that use both WinPrint 1 and WinPrint 2 reports.

Improved Header Command Protection

When you build a WinPrint report the various header commands, DFHeaderFrame, DFHeaderLineCheck, DFHeaderMargin, DFHeaderPos and DFHeaderWrap must be defined inside a DFBeginHeader – DFEndHeader block.

WinPrint 1 did not report an error when these commands were defined outside a DFBeginHeader – DFEndHeader block, however the commands would not perform correctly. In WinPrint 2 an error is raised when a header command is defined outside a DFBeginHeader – DFEndHeader block.


Using WinPrint 2 without the cWinReport2 Class

Using WinPrint 2 Outside the cWinReport2 Class

The easiest way to use WinPrint is to use the class wrappers provided by the Visual DataFlex class library. WinPrint 1 and WinPrint 2 are specifically designed to work together in the same application. This design works best when you are using the provided report classes for each reporting engine. When you do this, your application should automatically figure out which WinPrint engine to use for which report. For WinPrint 2 you would create objects based on the cWinReport2 class. For WinPrint 1 you would create objects based on the WinReport class.

See Building WinPrint 2 Reports for more information.

If you are using WinPrint 2 without using the cWinReport2 class wrapper, then there are additional things you need to know about WinPrint 2 in order to make this work.

Use Package

To utilize WinPrint 2 manually, you need to use the Visual DataFlex package WinPrint2.pkg in your application. e.g.

Use WinPrint2.pkg

This package declares the necessary global object that wraps all calls to the WinPrint2 API.

Global WinPrint Object

WinPrint 2 uses a global object to wrap API calls to WinPrint 2 dll. A handle to the WinPrint 2 global object is stored in the global variable ghoWinPrint2. If you are not using the cWinReport2 class, then you will need send messages to this object. e.g.

Send DFNewDoc of ghoWinPrint2 True

Document Control

WinPrint 2 requires strict document control. If you are not using the cWinReport2 class, then each report that you create must begin by sending DFNewDoc and finish by sending DFEndDocument to the global WinPrint 2 object. This was not necessary with WinPrint 1.

The DFPreview and DFPrint messages must be sent after DFEndDocument. This was not necessary with WinPrint 1.


Using Basic Reports in Applications

The BasicReport class allows the developer to create code based reports. While it is an extremely powerful report-processing tool, its output capabilities are limited. It is used when you wish to create non-proportional, single-font reports. You might think of it as a "line-printer-mode" reporting tool. It is also used as the basis for the cWinReport2 class. cWinReport2, which is a sub-class of BasicReport, adds a much more flexible output capability to BasicReport’s already considerable report processing capabilities.

The BasicReport and the cWinReport2 sub-class both have the following capabilities.

  • A report is broken down into sections. Sections correspond to the various parts of a report. The Header, Footer, Sub-header, sub-total, page totals, report totals, body sections are all examples of report sections. Each section is defined by a special procedure named procedure_section.

  • You may choose to nest reports. This lets you perform "outer joins," in which a child file's records are ordered by a parent file's index. This lets you print reports in a more-sensible order. In addition, parent records with no relating child records may be printed or not printed (zero suppression).

  • Child-nested reports are automatically constrained by the parent report. A nested report (the report inside the main report) may itself contain child reports, or the report may have sibling reports (all constrained by the same parent).

  • Up to nine levels of breakpoints with subheaders and subtotals are supported. BasicReport supports nine levels at each level of report nesting. cWinReport2 supports a total of nine-levels within a report (including nested child reports).

  • Page breaks with the proper display of all headers, sub-headers, and page totals are automatically supported.

  • Breakpoints may be based on just about anything, including field values, variables, window values, and functions (break (left(vendor.name, 1)).

  • Records may be found through data-dictionary objects or directly through the files. In addition, custom finding and relating of records is also fully supported.

  • They allow complete control over the finding of records. You can find up or down an index. Custom relating of records is supported.

  • They support the page-formatting options such as page_top, report_header, page_header, page_title, page_footer, report_footer, page_bottom.

  • Printing of variable-length memo fields is fully supported.

  • They automatically support a status panel. A status panel is a panel that appears while the report is processing. This panel is used to show report progress and to provide a method for canceling a report.

  • Errors are directed to a single point in the report object making it easy to control errors.

You would choose BasicReport for the following reasons:

  • Your report only requires a single, non-proportional font.

  • It is fast.

  • It is a good choice for creating large, high-volume reports.

  • It provides an excellent path for migrating character-mode-report-object (and report-macro) reports to Visual DataFlex.

  • It can also be used as the report engine for other types of reports. Output could be sent to word processors, spreadsheets, etc.

Here is an example of a simple object based on the BasicReport class. Note that all of this code could be placed in the bottom code area of your Report View object.

/CustHdr Resident

Customer Name Listing Page:___.

Cst Id Name

----------------------------------------------------------------------------

/CustBody Resident

_____. ______________________________

/Footer Resident

Basic Report Sample: Printed on: __/__/__

/*

Object Cust_Report is a BasicReport

Report_Main_File Customer

Report_Index by Customer.Name

Set Report_Title to "Printing Customer Names" // status panel title

Function Starting_Main_Report Returns Integer

Integer RetVal

Forward Get Starting_Main_Report to Retval

SysDate Footer.1 // set date for report

End_Function

Procedure_Section Page_Top as CustHdr

Print (Page_Count(self)) // show current page #

OutPut_PageCheck

End_Procedure

Procedure_Section Body as CustBody

Send Update_Status Customer.Name // update the status panel

Print Customer.Number

Print Customer.Name

OutPut_PageCheck

End_Procedure

Procedure_Section Page_Bottom as Footer

OutPut_PageCheck

End_Procedure

End_Object

Within the report view the report would be started by creating the following procedure:

Procedure StartReport

Integer ToScreen

String FileNme

Get Print_to_Screen_State to ToScreen

If ToScreen Begin

Make_Temp_File "TXT" FileNme

Move (".\" -FileNme) to FileNme

Set Output_Device_Name to FileNme

End

Else ;

Set Output_Device_Name to "WinLst:"

Send run_report

If ToScreen Begin

RunProgram background "NotePad" FileNme

EraseFile FileNme

End

End_Procedure

Some things to note about this sample:

  • This report should be placed inside a ReportView or ReportPanel. This source may by directly pasted into the container or it may be placed in its own source file and included in the program (with the #INCLUDE command).

  • Output lines are created by creating embedded named "images" (e.g., CustBody). Each image contains display fields (e.g., ____). These fields are filled with the print command and then displayed with the Output_PageCheck command.

  • It is assumed that the report will be generated to a file or a print file. It is up to you to define this file by setting the report view’s Output_Device_Name property. It is also up to you to display or print the file.

  • If a report view contains a single report, the report may be run simply by sending the message Run_Report to the view.

  • Each part of a report is defined by the Procedure_Section command. These sections are procedures that are called during at the appropriate time of the report process.

  • In the body section, the message Update_Status is used to display progress to a status panel. The status panel will appear when the report starts, show progress, allow the user to cancel the report, and will disappear when the report is complete.

  • If the status panel’s cancel button is selected, a prompt will appear asking the user if the report should be canceled.

  • If an error occurs during the report, an error message will be displayed. Then, another prompt will appear asking the user if the report should be stopped.


Reporting

  • Using Crystal Reports in Applications

  • Using WinPrint Reports in Applications

  • Using Basic Reports in Applications