Foxite.COM Community Weblog

Foxite.COM Community Weblog - free weblog service for the Visual FoxPro Community.
Welcome to Foxite.COM Community Weblog Sign in | Join | Help
in
Home Blogs Forum Photos Forum Archives

tushar



  • Excel Report Listener Update

    Updated the Excel Listener
    Updates

    1. Continuous excel sheet.
    Make Continuous property .T. This will print the page header only once. Page footers will be ignored, so you will get a continuous report

    2. Formula in group footers
    Right click on any object in group footer, or select multiple objects in group footer and right click. In the dialog box, choose Total. This will then create formula in the excel worksheet instead of just the value. So the excel cell will have =SUM(.....). This will only happen for calculated object in group footer and summary band. The reset at should not be on page footer. The detail band should be of a single row.

    3. Create with Automation
    Instead of creating XML workbook, you can now create workbooks with automation. For this you have to create a class with the following methods
    PageBreak(Row) - Create a page break at this row
    SaveFile(FileName) - Save file. FileName will contain extension of XML. You may want to remove the extension or FORCEEXT it to something else
    SetRowHeight(Row,RowHeight) - Set the height of a row
    PutInCell(row,column,Contents,formula,VAlign,HAlign,topborder,bottomborder , leftborder , rightborder,ExcelInterior,FontColour)
    mergecells(Row1,Column1,Row2,Column2) - merge cells in range given by Row1,Column1,Row2,Column2
    SetColumnWidth(column,width) - Change column width

    These methods will be called by the listener. An example excelobj.prg is enclosed. It ignores a lot of the commands. You will need to complete the class to get excel automation. You should also be able to create a class for open office automation.

    To get report using automation give command
    loReportlistener = NewObject('xllistener','xllistener.vcx')
    loReportListener.automationclassname='excelobj'
    loReportListener.automationclasssource='excelobj.prg'
    report form YourReportName object loReportlistener
  • Excel Report Listener

    This listener follows a different approach in the sense the that developer had to make the report ready for being transferred to excel, unlike the others that take a frx directly and try to convert to excel by making intelligent guesses as to which report control belongs to which excel cell.

    To do that, you run the EXCELREPORT.SCX form and choose the FRX file to be converted. This gives you a screen similar to the report designer.


    To create columns, click on the top container where you want to column to begin

    Similarly to create rows in the bands, clicking on the left container.

    You can drag / drop the lines that mark the columns / rows to change the size of the rows/ columns. Dropping them into the trashcan will delete the line and merge the affected cells.

    You can drag/drop the report controls to change their position in the band.

    You can select cells by clicking on them and then merge them / set their borders by right clicking a selected cell. Only horizontal cell merging is possible currently.

    Once you have created all the required cells and placed all the controls, you have to click on "Snap to Grid" to move all the objects to their correct location. This is when it will be decided which report control goes into which cell and what its alignment in that cell will be.

    Finally you have to save the report. It will write a xml in each object in the user column of the FRX.

    There will be details of number of rows and the height of each row in the record of the band (objtype=9). There will also be details of the borders and merging of cells.

    There will be details of row number and column number in the user column of the report object (objtype 5 and 8)

    In the first record (objtype=0) , there will be details of all column width

    These details will be used by the listener to create the excel file.

    The report listener that uses this information to create excel file is xllistener of xllistener.vcx. Currently the only additional property of the listener that the developer can use is the XLFileName property which will store the name of the excel file. The file is saved in the xml spreadsheet 2003 format.

    I would like to thank Alejandro Sosa for giving me permission to use any code of his excellistener class and this listener uses his code to a large extent.



    Current Wish List of additional features
    Support for Images
    Continuous excel sheet without page break (as in original)
    Font Colours
    Formula in Totals
    Dynamic Formatting
    Update can be found at http://weblogs.foxite.com/tushar/archive/2010/07/18/12859.aspx
  • Alternatives to Public Variables

    Alternatives to Public Variables

    Use of public variable is not considered a good idea. For reasons you can visit Everybody tells me that PUBLIC variables are not good practice - Why?
    I am trying to document why public variables are used by developers and what are the safer alternatives.

    For globally available variables.

    Create a custom class to hold the global variables as properties. Use the Assign Event to check if the current procedure is allowed to update the value
    Code example

    Making values available to forms

    Pass them as parameters
    DO FORM Form2 WITH lnParameter1,lnParameter2
    For each of the parameter passed, create a property in the called form and save the value of the parameters in the property. Then you can get the value of the passed parameter in any method of the form with thisform.Parameter1, thisform.Parameter2 …

    Alternatively
    Do the form without showing it but by giving it some name.
    DO FORM Form2 NAME loSomeName
    Update properties in the form and then show the form.
    loSomeName.Parameter1=….
    loSomeName.Parameter2=….
    loSomeName.Show

    To allow forms to return values

    Use the TO clause in the DO FORM command
    DO FORM Form2 TO lnSomeVariable
    And return the value in the UnLoad event

    Alternatively, give the called form a name
    DO FORM Form2 NAME loSomeName
    Then do not release the form. Just hide it. In the calling method, use the loSomeName to get a handle to the form and get hold of any of its property. Then release the form with loSomeName.Release

    To allow forms to return multiple values

    Again use the TO clause in the DO FORM command
    DO FORM Form2 TO lnSomeVariable
    In UnLoad event, create an object with all the values to be returned as properties. Then return this object

    You can also use the above alternative

    Have values available in multiple methods of a form

    Use form properties instead

    To allow functions to update variables before returning

    Pass parameters by reference
  • Shadow Tables

    In a client/server environment we sometimes have to bring down large data over the network, especially if we are displaying lookup tables. Instead, if we could have a local copy of the same table and keep it in sync with the server table, we could decrease the network load.

    Server Database Setup

    To do so, we first need to keep track of the changed records on the online database. This is done by having an update serial number for each record.

    To track the deleted records, create a table called deleted with the fields TableName C(25), PK Int, UpdateSR Int.

    TableNameVarchar(25)Name of the table of the deleted record
    PKInt4Value of primary key of the deleted record. Could also be of type GUID depending on what you use as a primary key
    UpdateSRInt4Update Serial number of the DELETE

    This will have details of all deleted records of all tables.

    To track added/changed records, add an integer column called UpdateSr in all the tables that we want to keep synced.

    In the INSERT and UPDATE trigger, we update the value of UpdateSr to the next serial.

    In the DELETE trigger, we insert a record in the ‘deleted’ table.

    If the backend is PostgreSQL then the setup will be

    • Create a sequence for each table
    CREATE SEQUENCE clientupdate INCREMENT 1 MINVALUE 1 MAXVALUE 9223372036854775807 START 11 CACHE 1;

    • Create a Trigger function
    CREATE OR REPLACE FUNCTION updateclient()
    RETURNS "trigger" AS
    $BODY$
    declare nextsr int;
    begin
    nextsr=nextval('clientupdate');
    IF tg_op="DELETE" THEN
    INSERT INTO deleted (tablename,pk,updatesr) values ('client',old.id,nextsr);
    RETURN old;
    END IF;
    new.updatesr=nextsr;
    RETURN new;
    End
    $BODY$
    LANGUAGE 'plpgsql' VOLATILE;

    • Create a Trigger
    CREATE TRIGGER client
    BEFORE INSERT OR UPDATE OR DELETE
    ON client
    FOR EACH ROW
    EXECUTE PROCEDURE updateclient();

    It is better to check for changes in value of only the columns that we are interested in to mark the record as changed instead of marking it as changed when there are changes in any column of the record.

    Client Database Setup

    Create a VFP database on the client side. Create a table called UPDATESR in it. This table will contain a list of all the tables that have to be kept synchronized. Structure of the table will be
    TableNameVarchar(25)Name of the table of the deleted record
    PKFLDNAMEVarchar(15)Name of the Primary Key Field
    UpdateSRInt4Serial number upto which the table is synchronized

    When entering the tablename, add the word ‘Shadow’ to the server table name. So the local table name for the client table will be clientshadow. The record in UPDATESR will look like
    TableNamePKFLDNAMEUpdateSr
    ClientShadowID0

    Create the shadow table (clientshadow.dbf) with the same structure as that of the online client table. You do not need to have all the fields there. The primary key is necessary. Create only those fields that you want synchronized. Do not use AutoInc for the primary key otherwise it will become readonly and we want to insert records along with the primary key. Create index tags etc as are necessary.

    Now we turn on event in the database

    DBSETPROP(YourDatabaseName,"DATABASE","DBCEvents",.t.)

    And add a procedure in dbc_AfterOpenTable

    PROCEDURE dbc_AfterOpenTable
    LPARAMETERS lcTableName
    DO sync WITH lcTableName

    Basically every time a table is opened, we are calling sync.prg. Sync.prg will query the online database and update the table accordingly. Sync.PRG is attached.

    So all you have to do is open the shadow table and the local data will be up to date.

    To requery a table you can create a PRG class called shadowupdate

    DEFINE CLASS shadowupdate as Session
    PROCEDURE init
    LPARAMETERS lcTableName
    USE (lcTableName)
    USE
    RETURN .f.
    ENDPROC
    ENDDEFINE

    And refresh a table with
    loShadowUpdate=NEWOBJECT('ShadowUpdate','shadowupdate.prg','', 'clientshadow')

    Be aware that a local synchronized table can be easily opened or queried for changes. The security offered by a back end will be lost in a synced table.

This Blog

Post Calendar

<July 2010>
SuMoTuWeThFrSa
27282930123
45678910
11121314151617
18192021222324
25262728293031
1234567

Syndication