How to: Replace the Report Builder's Expression Builder Dialog Box

The Report Builder architecture defines a framework into which you can plug your own classes to handle specific builder actions, such as responding to report designer events. Additionally, the report builder also allows you to replace the Expression Builder dialog box with your own implementation, by registering a class in the report builder's event handler registry table.

This topic describes the process of plugging in your own class to replace the expression builder dialog box, and also gives a more comprehensive example class that demonstrates how to create a Data Environment-aware expression builder.

Note

If you have not already done so, create an editable copy of the report builder's internal event handler registry table, as described in How to: Add Your Own Handler to the Report Builder's Registry.

Note

This topic assumes that the _REPORTBUILDER system variable is set to the default Report Builder application. If it is empty, or has been set to a third-party report builder, then the procedures described below probably will not apply. For more information, see _REPORTBUILDER System Variable.

To create a GetExpression wrapper class

  1. Create or edit a programmatic class library:

    MODIFY COMMAND c:\temp\mylibrary.prg

  2. Define a class that implements the GetExpression wrapper API, as documented in Report Builder Event Handler Registry Table. You can use the simple example shown here as a template. This example replaces the label caption editor with a simple input box.

  3. Save your changes.

DEFINE CLASS MyGetExpressionWrapper AS Custom
   PROCEDURE GetExpression( cDefaultExpr, cDataType, ;
                            cCalledFrom,  oEvent )
      LOCAL cNewValue
      * Switch to the designer datasession, where tables
      * may be open:
      SET DATASESSION TO (oEvent.DefaultSessionID)
      DO CASE
      CASE m.cCalledFrom = "LabelCaption"
         * Specify special handling for label captions:
         cNewValue = INPUTBOX("Enter the label caption")
      OTHERWISE
         * All other expression builders will use the default:
         GETEXPR "Enter the label caption" TO cNewValue ;
            TYPE cDataType DEFAULT cDefaultExpr
      ENDCASE
      * Restore to the private builder datasession:
      SET DATASESSION TO (oEvent.FrxSessionID)
      RETURN cNewValue
   ENDPROC
ENDDEFINE

To register your class as in the handler registry table

  1. Open the Report Builder Options dialog box:

    DO (_REPORTBUILDER)

  2. Click Explore Registry.

  3. Locate the record with Type = "G".

  4. Edit the record so that Class = "MyGetExpressionWrapper" and Library= "c:\temp\mylibrary.prg"

  5. Click OK to save your changes.

To test your changes

  1. Open a report or label layout in the designer.

  2. Double-click on a label control to display the Properties dialog box.

  3. Click on the ellipsis (…) next to the label caption text box to invoke the custom expression builder provided by your class.

Example

By design and by default, the Expression Builder dialog box invoked by ReportBuilder.App does not show tables that are not open in the designer's data session. Tables and cursors included in the report layout's data environment are not available. This is a change from how the designer's Expression Builder behaved in earlier versions (and still does when _REPORTBUILDER = "").

This example shows how you can define an alternative wrapper class that inspects the data environment, opens the tables in a private data session and then invokes the standard GETEXPR dialog box, to duplicate the original behavior of the report designer's Expression Builder.

You can save this class to a procedure file, and register it for use in the report builder, as described above.

DEFINE CLASS GetExpressionWithDE AS Session

  DataSession = 2   && private
  
  PROCEDURE GetExpression
     LPARAMETERS lcDefaultExpr, lcDataType, lcCalledFrom, loEvent 

     LOCAL lCurSel, lcNewExpr, liPrivateSession
     LOCAL liLines, i, iTableCount, lcAlias, lcSource

     *----------------------------------------------
     * Save this before it changes:
     *----------------------------------------------
     liPrivateSession = THIS.DataSessionId 

     *----------------------------------------------
     * Scan the data environment and open tables:
     *----------------------------------------------
     SET DATASESSION TO (loEvent.FrxSessionId)
     lCurSel     = SELECT()
     iTableCount = 0
     SELECT expr FROM frx WHERE objtype = 26 ;
        INTO CURSOR environCursors
     SELECT environCursors
     SCAN
        liLines = ALINES(laValuePairs, environCursors.expr )
        FOR i = 1 TO liLines
           DO CASE
           CASE LEFT(UPPER(laValuePairs[i]),5) == "ALIAS"
              lcAlias = THIS.GetValue( laValuePairs[i])
           CASE LEFT(UPPER(laValuePairs[i]),7) == "CURSORS"
              lcSource = THIS.GetValue( laValuePairs[i])
           ENDCASE
        ENDFOR

        lcSource = EVL(lcSource,"")

        DO CASE
        CASE FILE(lcSource)
        CASE FILE(FORCEEXT(lcSource,"DBF"))
           lcSource = FORCEEXT(lcSource,"DBF")
        CASE FILE(JUSTFNAME(lcSource))
           lcSource = JUSTFNAME(lcSource)
        CASE FILE(FORCEEXT(JUSTFNAME(lcSource),"DBF"))
           lcSource = FORCEEXT(JUSTFNAME(lcSource),"DBF")
        ENDCASE

        IF FILE(lcSource)
           lcAlias = EVL( lcAlias, "Cursor"+TRANSFORM(i))
           SET DATASESSION TO (liPrivateSession)
           IF NOT USED(lcAlias)
              TRY
                 IF EMPTY(ALIAS())
                    USE (lcSource) ALIAS (lcAlias) ;
                       NOUPDATE SHARED
                 ELSE
                    USE (lcSource) ALIAS (lcAlias) ;
                       IN 0 NOUPDATE SHARED
                 ENDIF
                 iTableCount = iTableCount+1
              CATCH WHEN .T.
              ENDTRY
           ENDIF
           SET DATASESSION TO (loEvent.FrxSessionID)
        ENDIF
     ENDSCAN
     USE IN environCursors
     SELECT (lCurSel)
     IF iTableCount > 0
        SET DATASESSION TO (liPrivateSession)
     ELSE
        SET DATASESSION TO (loEvent.DefaultSessionID)
     ENDIF

     *-----------------------------------
     * Display the GETEXPR dialog:
     *-----------------------------------
     GETEXPR TO lcNewExpr TYPE lcDataType DEFAULT lcDefaultExpr

     *-----------------------------------
     * Clean up and exit:
     *-----------------------------------
     SET DATASESSION TO (liPrivateSession)
     CLOSE DATA
     SET DATASESSION TO (loEvent.FrxSessionID)
     RETURN lcNewExpr
  ENDPROC

  PROTECTED PROCEDURE GetValue
     LPARAMETER lcValuePair
     LOCAL liPos, lcReturn
     lcReturn = ""
     IF NOT EMPTY(lcValuePair)
        liPos = AT("=",lcValuePair)
        IF EMPTY(liPos)
           RETURN ""
        ENDIF
        lcReturn =CHRTRAN(ALLTRIM(SUBSTR(lcValuePair,liPos+1)),["'],[])
     ENDIF
     RETURN lcReturn
  ENDPROC

ENDDEFINE

See Also

Tasks

How to: Configure the Report Builder's Event Handling
How to: Specify an Alternate Report Event Handler Table

Reference

Event Handler Registry Dialog Box (Report Builder)
Report Builder Options Dialog Box (Report Builder)
Report Builder Event Handler Registry Table

Concepts

Understanding Report Builder Events