View RSS Feed

Development Team Blog

Visual Report Writer and The Web (V)

Rate this Entry
In this fifth blog about Visual Report and The Web I will talk about the next report that you can find on the Live Demo website (European Server, USA server) named OrderList. If this is the first blog you read I encourage you to read the four other blogs (1: The Solution, 2: Invoices Report, 3: The Cleanup and 4: The CustomerList). Between the fourth blog and this one we have been able to release the Alpha I version of Visual Report Writer 3.0 and the 2.1+ Library only setup. The latter one is needed to make the web reporting using the 17.1 DataFlex Web Framework easy and look like at the demo website.

The Report
The report used in this example webview is delivered with the 17.1 WebOrder example workspace. The report shows how you can make use of colors (page header and group header have a different color), how you can show the selection criteria (assuming the report "only" filters on order total) can be shown and how you can concatenate table columns with a function into a new calculated column. The report also uses two summaries placed in the group footer.

The Integration
At integration level the speciality contains two features not seen in the other blogs yet; the user can select the report order in a multi-segment way and the result (PDF) is shown in a modal dialog.

The Ordering Selection
This integration feature from a Visual Report Writer point of view is not very complex as you can set the record ordering by either expanding or replacing the current ordering. Replacing means you need to remove the current ordering which is done via:
Send RemoveAllRecordSortFields sReportId
The sReportId is a GUID returned by an OpenReport instruction. The report id (required) may also be passed to the function as an empty string (or C_USEMAINVRWREPORTID) in which case the integration library uses the psReportId property of the cVRWReport class.

Adding record filter instructions can be done by sending the AddRecordSortField message. The user selected sort fields in this example view can be retrieved by calling a function named SortColumnSet from the grid. The function returns two-dimensional string array with the sort field names and their sort direction (ascending/descending). The returned information can be enumerated and each row in the array can be used to set the record sort order. The code in this example is:
Procedure SetSortFields
    String[][2] sSortColumnData
    Integer iElements iElement

    Send RemoveAllRecordSortFields C_USEMAINVRWREPORTID
    Get SortColumnSet of oSortColumnsGrid to sSortColumnData
    Move (SizeOfArray (sSortColumnData)) to iElements
    Decrement iElements
    For iElement from 0 to iElements
        Send AddRecordSortField C_USEMAINVRWREPORTID sSortColumnData[iElement][0] sSortColumnData[iElement][1]
Let us now look at the control because that let the user select the columns for the sort ordering. First; the end-user cannot select which columns to sort on, only set the sort order amongst the columns. Which columns are available is chosen by the report integrator (you). The grid has 2 columns; one for the name of the sort column and one for selecting ascending/descending.

Object oSortColumnsGrid is a cVRWWebSortColumnsGrid
    Object oSortOnWebColumn is a cWebColumn
        Set psCaption to "Sort On"
        Set pbSortable to False
        Set piWidth to 90
        Set pbEnabled to False

    Object oSortDirectionWebColumn is a cVRWWebSortDirectionColumn
        Set psCaption to "Direction"
        Set pbServerOnChange to True

        Procedure OnChange String sNewValue String sOldValue
            Forward Send OnChange sNewValue sOldValue

            Send ProcessDataSet of oSortColumnsGrid 3

    Procedure OnSetSortColumns tWebRow[] ByRef WebSortColumnRows
        Move "{Orderdtl.Detail_Number}" to WebSortColumnRows[0].aValues[0]
        Move "Detail Nr" to WebSortColumnRows[0].aValues[1]
        Move C_VRWAscending to WebSortColumnRows[0].aValues[2]

        Move "{Orderdtl.Item_Id}" to WebSortColumnRows[1].aValues[0]
        Move "Item ID" to WebSortColumnRows[1].aValues[1]
        Move C_VRWAscending to WebSortColumnRows[1].aValues[2]

        Move "{Orderdtl.Qty_Ordered}" to WebSortColumnRows[2].aValues[0]
        Move "Qty Ordered" to WebSortColumnRows[2].aValues[1]
        Move C_VRWAscending to WebSortColumnRows[2].aValues[2]
The grid object is instantiated from the cVRWWebSortColumnsGrid class which is a sub-class of cWebGrid. The grid sub-class is not readonly and not a cWebList because the user must be able to select the sort direction of each of the columns. New grid rows cannot be added by the end-user by setting the pbAllowAppendRow, pbAllowInsertRow to false. Users cannot remove columns by setting the pbAllowDeleteRow to false. These properties are set in the construct_object event.

The sort columns are loaded in an event named OnSetSortColumns. This event is fired from OnManualLoadData (documented event of the cWebGrid class). The OnManualLoadData is marked as private for the cVRWWebSortColumnsGrid class and should not be used by you. The grid data is stored in both the grid as well as a web synchronized property that contains a comma separated value list of the grid data. This comma separated list is maintained to reduce the complexiity when getting the users' choice for the sort order. The SortColumnSet function (used from SetSortFields) converts this comma separated list into a two dimensional array of sort columns.

As you can see in the code above the grid seems to have three columns as for every grid row 3 values are put in the tWebRow[] argument. This is not true, the first value (the [0] value) is a unique row identifier. In a data-aware grid (such as an order detail grid) it contains the serialized RowId of the database table row. In this non data-aware grid (pbDataAware is false and peDbGridType is gtManual) the unique value is the table.column reference as Visual Report Writer likes to get it from you. This makes the first real grid column just a display value column and you could anything you like in there, of course the end-user must be able to make the decision based on this value.

If you like to translate the words "Ascending" and "Descending" you can set the properties psAscendingText and psDescendingText of the sort direction column (class is cVRWWebSortDirectionColumn). BTW, I noticed Google translate the values last week and misteriously the control kept working.

Changing the sort order can be done by clicking one of the buttons on the side. These controls are cWebButtons but don't look like buttons (thanks to CSS). If the end-user clicks one of the buttons a ProcessDataSet message is send to the grid. The method ProcessDataSet is augmented in the class to correct the piCurrentRowIndex if needed and then send to the client. The client on its turn sends a OnProcessDataSet to the server. Based on whether the user clicked the up or down button or changed the sort direction drop-down value the OnProcessDataSet is changing the contents of the grid's data (passed as a tWebRow[] argument). After really changing the data a grid refresh instruction is send to the client. Data is only changed if a sort column could be moved up or down in the order or the user changed the sort direction.

The good news of this rather complex grid control is that you never have to code this yourself if you are using the integration wizard. During the wizard you can select that the user should be able to select the sort order and that a multi value sort order is desired and the whole grid (plus the buttons) are generated. Important complexity if this grid is that the messages are - due to process pooling - most likely not handled by the same server process. It is all covered for you but be aware if you like to make changes.

The Modal Display
The results of the report are written to a PDF file on the server as with the customer report and a couple of other reports on this demo website. The PDF is not displayed in a IFrame or on a different window/browser nor on a second tab-page but are shown in a modal dialog. The integration library contains a web object component that can be included in your web application and used for displaying these files. The component file name is PDFModalDialog.wo. The component can be included in the report component or inside webapp.src where all other web components are included. In either case the dialog becomes a sibling of the cWebView as it should. And the really good news - I hope - is that the integration wizard can do this for you!
Use PDFModalDialog.wo
The component defines an object of the cWebModalDialog class with two panel children. One to display the results and one to organize the location of the button to close the dialog.

You use the dialog by sending the message ShowPDFModal to the object passing the object to return back to when the dialog closes, the object handle of the cVRWReport object and a caption text to be displayed in the caption bar of the modal dialog.
Object oRunReportMenuItem is a cWebMenuItem
    Set psCaption to "Run Report"
    Set psTooltip to "Run the report with selections"
    Set psCSSClass to "VRWRunReportButton"

    Procedure OnClick
        Send ShowPDFModal of oPDFModalDialog Self oReport "OrderList (PDF)"
As you can deduct from above code you are not passing the name of the PDF file but sending the instruction to create the report PDF and display it. The ShowPDFModal method sends a message RunReport which in its turn send a GenerateReport message to the cVRWReport object. So the cVRWReport object (inside the cWebView component) has the logic to collect the selection criteria, sort orders etc and generate the PDF file and return the URL to the PDF file to the caller (the dialog).

The GenerateReport inside the cVRWReport object is the same as shown before but to make it easy for my readers, here is the code:
Function GenerateReport Returns String
    String sReportId sFile
    vrwPDFExportOptions PDFReportOptions
    Boolean bCanceled

    Get OpenReport to sReportId
    If (sReportId <> "") Begin
        Get DefaultPDFExportOptions to PDFReportOptions
        Set pPDFExportOptions to PDFReportOptions
        Get ReportCacheFileName ".pdf" to sFile
        If (sFile <> "") Begin
            Send ExportReport C_vrwPDF sFile

        // see if report ran ok, if not return empty path
        Get pbCanceled to bCanceled
        If bCanceled Begin
            Move "" to sFile

        Send CloseReport of oReport sReportId

    Function_Return sFile
This finishes this fifth blog about Visual Report Writer and the web. Keep tuned as more blogs will follow in the coming time.