Wednesday, September 07, 2016

DBI Offers New Control to Southwest Fox/Xbase++ Attendees

DBI is giving away a full license of Calendar COM (ctxCalendar) to each Southwest Fox/Southwest Xbase++ 2016 attendee. Calendar COM is a really great Outlook style scheduling control with three fully extensible views baked into one control: a multi-column Day View (a multiple day appointment scheduler for one resource or multiple resource appointment scheduler for one day at a time), a Week View Appointment Scheduler, and a Month Calendar / date picker. It comes in both 32-bit and 64-bit versions.

DBI has been a long-time supporter of Southwest Fox and we thank them for their generous support once again this year!

Tuesday, August 23, 2016

Rendering a Report Image in the Foreground

A feature I’d forgotten about in the VFP 9 reporting enhancements is the ability to absolutely position objects. As Cathy Pountney discusses in What's New in the Visual FoxPro 9.0 Report Writer, you can use this feature to add a watermark to a report. One problem, though, is that regardless of how you arrange objects using Sent to Back or Bring to Front, the image always appears in the background of the report. What if you want it to appear in the foreground (that is, on top of the detail band objects)?

A little-used reporting enhancement to the rescue. FX and GFX objects extend a report listener by hooking into the various events, allowing you to change the behavior of the report run. These objects can be of any base class, as long as they have an ApplyFX method that accepts certain parameters, but it’s easier to subclass FXAbstract in _ReportListeners.vcx in the FFC folder. The difference between an FX and a GFX object isn’t in the class definition; it’s just that the report listener pays attention to the return value of a GFX object’s ApplyFX method, whereas it doesn’t for an FX object. To add an FX or GFX object to a listener, use:

loListener.AddCollectionMember(Class, Library, APP or EXE, .T. for singleton, .T. for GFX or .F. for FX)

I created a subclass of FXAbstract that does the following:

  • When the report run starts (tcMethodToken is “BEFOREREPORT”), create an array of Empty objects, one for each image in the FRX file that has “FOREGROUND” in its Comment property (the record number of the image in the FRX is used as the index for the object in the array).
  • When such an image is rendered (tcMethodToken is “RENDER”), save its rendering information (left, top, height, width, etc.) to the corresponding Empty object in the array and tell the listener to not render the image by returning 2.
  • When the page footer band is entered (tcMethodToken is “BEFOREBAND” and the first of the variable parameters is 7), tell the listener to render each of the images. Because they’re being drawn after the detail band text, they’ll appear on top of the text.

Here’s the code for the class:

define class SFFXRenderInForeground as FXAbstract of home() + ;

     'FFC\_ReportListener.vcx'
    dimension aForeground[1]
        && an array of rendering information
    lSelfCall = .F.
        && .T. if we're being called from ourselves (we call Render which calls
        && us)
   
    function ApplyFX(toListener, ;
        tcMethodToken, ;
        tP1, ;
        tP2, ;
        tP3, ;
        tP4, ;
        tP5, ;
        tP6, ;
        tP7, ;
        tP8, ;
        tP9, ;
        tP10, ;
        tP11, ;
        tP12)
        local lnReturn, ;
            lnSession, ;
            lnSelect, ;
            lnCurrRecno, ;
            lnRecno, ;
            lnI, ;
            loForeground
        lnReturn = 0 && OUTPUTFX_BASERENDER_AFTERRESTORE
        do case

* Before the report runs, create an array of information about objects we'll
* process.

             case tcMethodToken = 'BEFOREREPORT'
                lnSession = set('DATASESSION')
                if toListener.FRXDataSession > -1
                    set datasession to toListener.FRXDataSession
                endif toListener.FRXDataSession > -1
                lnSelect = select()
                select FRX
                lnCurrRecno = recno()
                scan for 'FOREGROUND' $ upper(COMMENT)
                    lnRecno = recno()
                    dimension This.aForeground[lnRecno]
                    This.aForeground[lnRecno] = createobject('Empty')
                endscan for 'FOREGROUND' $ upper(COMMENT)
                if between(lnCurrRecno, 1, reccount())
                    go lnCurrRecno
                endif between(lnCurrRecno, 1, reccount())
                select (lnSelect)
                set datasession to lnSession

* If we're rendering an object that's supposed to be in the foreground, save
* the rendering information.

            case not This.lSelfCall and tcMethodToken = 'RENDER' and ;
                vartype(This.aForeground[tP1]) = 'O'
                loForeground = This.aForeground[tP1]
                addproperty(loForeground, 'FRXRecno', tP1)
                addproperty(loForeground, 'Left',     tP2)
                addproperty(loForeground, 'Top',      tP3)
                addproperty(loForeground, 'Width',    tP4)
                addproperty(loForeground, 'Height',   tP5)
                addproperty(loForeground, 'ContType', tP6)
                addproperty(loForeground, 'Contents', tP7)
                addproperty(loForeground, 'Image',    tP8)
                lnReturn = 2 && OUTPUTFX_BASERENDER_NORENDER

* If we're about to do the page footer band, render all foreground objects.

             case tcMethodToken = 'BEFOREBAND' and tP1 = 7
                 && FRX_OBJCOD_PAGEFOOTER
                for lnI = 1 to alen(This.aForeground)
                    loForeground = This.aForeground[lnI]
                    if vartype(loForeground) = 'O'
                        This.lSelfCall = .T.
                        toListener.Render(loForeground.FRXRecno, loForeground.Left, ;
                            loForeground.Top, loForeground.Width, loForeground.Height, ;
                            loForeground.ContType, loForeground.Contents, ;
                            loForeground.Image)
                        This.lSelfCall = .F.
                    endif vartype(loForeground) = 'O'
                next lnI
        endcase
        return lnReturn
    endfunc
enddefine

The following code tests the GFX object by running a report with and without it.

open database _samples + 'Data\TestData'

loListener = newobject('FXListener', home() + 'FFC\_ReportListener.vcx')
loListener.ListenerType = 1 && LISTENER_TYPE_PRV
report form Foreground object loListener
loListener.AddCollectionMember('SFFXRenderInForeground', ;
    'SFFXRenderInForeground.prg', '', .T., .T.)
report form Foreground object loListener

Here’s what the report looks like without the GFX object:

Background

You can see the image is drawn under the text. Here’s the report when the GFX object is used:

Foreground

Notice the image now appears on top of the text.

You can download the source and sample files for this from the Technical Papers page of my personal web site, http://doughennig.com/papers/default.html (look for “Rendering a Report Image in the Foreground”).

Monday, August 22, 2016

The FoxShow #81 is now available

Listen to Andrew MacNeill interview Tamar Granor and me about Southwest Fox and Southwest Xbase++ 2016 on the FoxShow #81. There's a special offer for FoxShow listeners until September 2. http://www.thefoxshow.com/

Wednesday, July 27, 2016

Southwest Fox 2016: Early-bird deadline approaches

Simple quick reminder: save $50 by registering before the Early-bird registration deadline this Sunday, July 31st.

If you still need to register, please head over to the registration Web site today: http://geekgatherings.com/Registration

Every registered attendee gets admission and white papers to all regular conference sessions. Don't miss this chance to learn from the best and mix with your peers.

It’s still only July and we already have nearly $15,000 in raffle prizes, with more yet to come.

See you in September!

Friday, July 01, 2016

Southwest Fox/Southwest Xbase++ 2016 are a Go!

We have some good news to share today. Thanks to the support of both the Visual FoxPro and Xbase++ communities, Southwest Fox and Southwest Xbase++ 2016 will proceed as planned September 22-25, 2016 in Gilbert, AZ.

We really appreciate all the support people have shown for the conference with early registrations! That said, the decision comes with a calculated risk because we are still many registrations short of the breakeven point. Please continue to spread the word to your colleagues, clients, user group and anywhere else Visual FoxPro or Xbase++ is spoken.

If you haven't yet registered, there's still room for you. Register before August 1 for a $50 early-bird discount.

Contact Us:

Only 83 days until we gather in Gilbert.

Monday, June 27, 2016

Southwest Fox 2016 Super-Saver Deadline Approaches

The countdown clock is ticking! The Super-Saver deadline for Southwest Fox and Southwest Xbase++ 2016 is June 30th, just a few days away. We still need people to register to make the conferences happen.

The conferences take place September 22-25 in Gilbert, Arizona and we really hope you can be there. We would hate to see you miss out on the $125 discount, the FREE pre-conference session, and an opportunity to win a scholarship or a license of Stonefield Query SDK (a $6,000 value).

After checking out our list of amazing speakers and digging into our session tracks for VFP and Xbase++, head over to the registration Web site today: http://geekgatherings.com/Registration.

Wednesday, June 01, 2016

Registration for Southwest Fox 2016 now available

Registration for Southwest Fox and Southwest Xbase++ (http://www.swfox.net/register.aspx) is now available. See http://www.swfox.net/speakers.aspx for a list of speakers and http://www.swfox.net/sessionsswfox.aspx for a list of sessions. Super-Saver Registration, which saves you $125, is available only through June 30th, so don't wait.

Putting on a conference is a risky endeavor. Conference centers require a guaranteed minimum income to block the dates of a conference; for a conference like Southwest Fox, that minimum is in the tens of thousands of dollars. We have to commit to the conference center by July 2nd and need your support by July 1st to make that commitment. We will not send out any "We need your help" appeals so please do not wait; register by June 30th! We know most of you like to wait until the last minute to avoid the credit card bill arriving too soon. We will not charge any attendee credit cards or cash any checks until we have committed to going forward (sometime after July 2), so this is not is a reason to wait.

Rick, Tamar, and I look forward to seeing you in September!