PFC Programmer's Reference Manual

Exclusive Excerpt from PFC Programmer's Reference Manual, by Richard Brooks

Appendices by Sharon Weinstrom Buntz, Jason Cohen, Glenn G. D’mello, Ken Howe, Greg Schultz and Brian Suter; List: $37.95, Published by Manning, ISBN # 1-88-477755-4

 

Appendix J - pfc_w_master - pfc_w_master’s Key Events

by Sharon Weinstrom Buntz

What is pfc_w_master?
Why understand
pfc_w_master?
Which of these
six window type descendants of
pfc_w_master do I inherit from?
Would I ever customize my
pfc_w_master?
Would I ever customize my
w_master?
Two Big Pictures of
pfc_w_master

What does pfc_w_master contain?
Tell me more about everything in
pfc_w_master…
pfc_w_master’s Instance Variables
pfc_w_master’s Functions
pfc_w_master’s Key Events ... Note: This section was too big to be included in the main web page (You are here)

Tell me a little about pfc_w_master’s PFC descendants…
Other
pfc_w_master-Related Objects

And what about the changes to pfc_w_master in the PFC 6.0 Release ?
Summary
About the Author


 

pfc_w_master’s Key Events

Events

Close ( ) returns long
CloseQuery ( ) returns long
Pre-5.0.03
Move ( integer xpos, integer ypos ) returns long
Open ( ) returns long

pfc_AcceptText ( powerobject apo_control [ ], boolean ab_focusonerror ) returns integer Pre-5.0.02
pfc_BeginTran ( ) returns integer
pfc_Close ( )
pfc_ControlGotFocus ( dragobject adrg_control )
pfc_DBError ( ) returns integer5.0.02
pfc_Descendant ( ) returns boolean
pfc_EndTran ( integer ai_update_results ) returns integer
pfc_Help ( ) returns integer
pfc_MessageRouter ( string as_message ) returns integer
pfc_MicroHelp ( string as_microhelp )
pfc_New ( )
pfc_Open ( )
pfc_PageSetup ( ) returns integer
pfc_PostOpen ( )
pfc_PostUpdate ( powerobject apo_control [ ] ) returns integer
pfc_PreClose ( ) returns integer
pfc_PreOpen ( )
pfc_PreUpdate ( ) returns integer
pfc_Print ( ) returns integer
pfc_PrintImmediate ( ) returns integer
pfc_Save ( ) returns integer
pfc_SaveAs ( )
pfc_Update ( powerobject apo_control [ ] ) returns integer
pfc_UpdatesPending ( powerobject apo_control [ ] ) returns integer
Pre-5.0.03
pfc_Validation ( powerobject apo_control [ ] ) returns integer
Resize ( ) returns long

Event

PFC CodePFCExtYou add code
PBNative Public,
Name
(arguments), return type

What is it?

Arguments

Return Values

When does it execute?

What does it do?

How should I either extend or add to it?

Close ( ) returns long Window Close

Returns long:
not used

Of course this Close event is the native PowerBuilder event that executes when
  • The user exits the application and the frame is told to close, thereby closing all open windows and sheets first
  • The user double-clicking the control menu, the user clicking the X close box
  • The user selecting File/Close (which sends a message to the pfc_close event, which in turn issues the script Close(this)
  • The user clicking a default or cancel button that eventually may do a Close( this ) or CloseWithReturn ( this, <return info> )
Note This Close event is not executed if a HALT or HALT CLOSE is done. That means that some services may not be properly destroyed. And while NT is better at "cleaning up" after such things, care should be taken to avoid doing this as it is not a good programming practice.

In this Close event, PFC

  • Stores the window size preferences and
  • Destroys any existing window service objects, such as the Basic Window, Resize and Preference service instances
CloseQuery ( ) returns long Window About to Close - Query if user wants to Save, Close, etc.

Returns long:

0 = Allow window to close

1 = Prevent window from closing

Note
CloseQuery is a KEY event in the PFC!

And, of course this CloseQuery event is the native PowerBuilder event that executes when a window is told to close, just before the Close event.

Here’s what the PFC’s CloseQuery script does:

  1. Checks the ib_DisableCloseQuery flag.

If it is TRUE, the entire PFC CloseQuery logic will be bypassed. So in order to skip all of the CloseQuery script, simply set ib_DisableCloseQuery to TRUE elsewhere in your program.

  1. Triggers the empty pfc_PreClose event.

If there are cases where you need to stop a window from closing, place that code in the pfc_PreClose event. The pfc_PreClose event allows you to stop the window from closing without actually having to override the CloseQuery event’s script.

  1. Sets the ib_CloseStatus flag to TRUE to indicate that a close is in progress. This is because in the PFC, individual validation errors are suppressed if the window is closing. If the window is not allowed to close during this event, it is automatically reset back to FALSE for you.

So during the time that you are in the CloseQuery script, ib_CloseStatus is TRUE. You may wish to know whether or not the window is in the process of closing in other scripts to determine how you handle things like validation errors. Outside of this window, you may check this flag if you need to know if the user is in the process of closing via the window’s of_GetCloseStatus( ) function.

  1. Checks all updateable DataWindows for changes or validation errors by calling the of_UpdateChecks( ) function.
  • If no pending updates are found, the window is allowed to close.
  • If a validation error occurs, the user is asked if they would like to close anyway (and lose their changes), or if they would like to cancel so that they may correct their error (and then they may save).
  • If unsaved changes are detected (and there are no validation errors), the user is asked if they would like to save their changes. If they choose to save, the pfc_Save event is triggered. Furthermore, if the save fails, the window is prevented from closing. If the save is successful, the window is then allowed to close.
Note Summary of what’s done in the PFC CloseQuery
  • Checks ib_DisableCloseQuery, if TRUE closes window
  • Triggers pfc_PreClose, if rc <> 1 stops window from closing
  • Sets ib_CloseStatus to TRUE to suppress validation errors
    (will auto reset back to FALSE if window is prevented from closing hereafter)
  • Calls of_UpdateChecks( ) function
  • Rc=0 - If no pending updates are found, the window is allowed to close
  • Rc<0 - If validation errors were detected, user is given choice to close (and lose their changes) or cancel the close (to correct & save)
  • Rc> 0 - If pending changes were detected and there were no validation errors, asks if user would like to save..

Triggers pfc_Save

If save is unsuccessful, window will not close.

Bug Alert! - Pre-PowerBuilder 5.0.03
Prior to PowerBuilder 5.0.03, there was a bug, not in the PFC, but in PowerBuilder itself (low-level). This PB bug sometimes makes the ‘No’ and ‘Cancel’ buttons on the Error Service Special Message Window (w_message) not actually stop the window from closing.

Scenario:

The user makes some changes (and does not save).
Then the user goes to close the window by using the "close box" or "control menu" (versus menuoptions File/Close or File/Exit, or the toolbar Exit button ~ which work!).

Next, the PFC w_message window opens, and…

  • The user is told that they have validation errors, and the user is asked if they would like to close anyway. They say ‘No’, but the window closes anyway.
  • The user is asked if they would like to save their changes. The user presses ‘Cancel’, but the window closes anyway.

Users find this particularly annoying because the window just closes and they lose their changes. And actually, they were trying to stop the window from closing so that they may correct their input and then save.

The problem surfaces because the Error Service uses the PowerBuilder OpenWithParm( ) function, passing a PowerObject type parameter, in order to open the w_message window. And the bug in PB centers around that scenario. Somehow when PB does that, it interferes with the CloseQuery event’s return code - even though the Return <rc> happens much later. Go figure. I am pointing this out because there are other implications where the OpenWithParm( ) may interfere with other event’s return codes as well.

Note that the problem is fixed in PowerBuilder 5.0.03, as opposed to PFC 5.0.03.

If you are not yet at PowerBuilder 5.0.03, and you are using the Error Service PFC Message Window style, here are some temporary workarounds…

  1. Users like the Error Service’s PFC Message Window, so the preferred temporary workaround is to change the Error Service to not use OpenWithParm( ), but simply Open( ) instead (See code below under A - D)
  2. One workaround is to use the Error Service’s Standard MessageBox style, rather than the PFC Message Window style. But this does not allow the user to add their comments and/or print error messages.
  3. Another workaround is to not use the Error Service at all. But this does not give you any of the advantages of error logging and reporting.
  4. Oddly enough, this problem only happens when the user closes the window by using the "close box" or "control menu" (versus menuoptions File/Close or File/Exit, or the toolbar Exit button). So one "workaround" (although lame) is to suggest that your users use the File/Close menuoption for a while - Or ask your users to please make it a practice to always save first before closing windows.
  1. Add comments to each object that you change (as appropriate):

//////////////////////////////////////////////////////////////////////////////
//
// NOTE: Temporary fix until PowerBuilder 5.0.03
// NOTE: Ancestor is overridden!!
//
//////////////////////////////////////////////////////////////////////////////

  1. Add an instance variable to your Application Manager:

n_cst_errorattrib inv_errorpass

  1. Override & Copy n_cst_error's of_processmessage() function to make this one small change to the existing code:

li_testrc = OpenWithParm(w_message, inv_errorpass)
gnv_app.inv_errorpass = this.inv_errorpass
li_testrc = Open(w_message)

  1. Override & Copy the ancestor script in w_message’s pfc_PreOpen event to make this one small change to the existing code:

//Get the PowerObjectParm.
inv_errorattrib = Message.powerobjectparm
//Populate inv_errorattrib from gnv_app.inv_errorpass
this.inv_errorattrib = gnv_app.inv_errorpass

Note By the way, the Powersoft PFC newsgroup (news://forums.powersoft.com/powersoft.public.powerbuilder.pfc ) plus the pfcsig & pfc-users mail lists are invaluable resources for PFC information sharing… such as finding out about these kinds of workarounds as problems are found. Read all about them at the PFC Cheat Sheet (http://www.pfccheatsheet.com/).
Move ( integer xpos, integer ypos ) returns long Window has moved - Store the window position and size

Returns long:
not used

This event is actually a PFC user-defined event which is simply mapped to the native Windows pbm_move event. Since it is not strictly a user-defined PFC event, it is not prefaced with pfc_.

This event executes when the user moves a window.

This Move event will post an event to the Preference service to take note of the window’s current position and size if it is in its Normal state. This is so that the service will know the normal size of the window (which should be saved when the window closes) even when the window is closed as maximized/minimized.

Open ( ) returns long Window is Opening

Very first event of window to execute by PowerBuilder

Triggers pfc_PreOpen at very top, so note that pfc_PreOpen actually executes before PFC’s script in this Open event

Window does not show until Open event completes (so as little as possible should be done here)

Returns long:
not used

Of course, this is the native PowerBuilder event that executes whenever a window is opened.

In this Open event, the PFC

  • First thing, triggers the pfc_PreOpen empty event - to execute any script that needs to happen very first, such as turning on the Window Resize and Preference services.
  • Posts the pfc_PostOpen empty event so that it will execute after this Open event completes
  • Restores the window preference settings if the Window Preference service is turned on. This is why it is important to turn on your Window Preference and Resize service in the pfc_PreOpen event if desired. Because first of all, this event needs to know if the Preference service is turned on, and secondly, when the Preference service resizes your window, you want your Resize service to kick in and resize and reposition any controls on the window as well.
Note Summary of what’s done in the PFC’s Open
  • Triggers pfc_PreOpen

(put your Preference and Resize initialization in this event)

  • Posts pfc_PostOpen

(save anything that can wait until here so window appears faster when opened. Put your database retrievals here)

  • // If no window title, sets it to the Application’s title
  • // Check User Preferences, and resizes/moves window/menu as appropriate
Note It is best to add application-specific code to either the pfc_PreOpen or the pfc_PostOpen event, rather than overriding or extending this actual Open event. And of course, if it can wait until the pfc_PostOpen event, that is best so that the window’s Open event completes quickly, allowing the window to appear to open just as quickly as possible.
pfc_AcceptText ( powerobject apo_control [ ], boolean ab_focusonerror ) returns integer Perform AcceptText’s on all DataWindows on current window

Argument apo_control [ ]:
an array of powerobjects containing the control array for the current window when PFC triggers this event from of_UpdateChecks( )

Note: For optional uses, you may pass an array of any objects which you would like to have this event perform AcceptText’s on any DWs within those objects

Argument ab_focusonerror:
boolean to specify whether or not to set focus on a DataWindow column with errors

Returns integer:
1 = Success
-1 = Error

This event is triggered by of_UpdateChecks( ) from the CloseQuery or the pfc_save event.

It issues a DataWindow AcceptText( ) function to all DataWindow's on the current window.

Note: This event includes support for the Linkage Service and for non-PFC DataWindows as well.

Note: This event features recursive processing for tab controls and user objects. In other words, this event calls itself in order to drill down through tab controls, tab pages and user objects, looking for DataWindows within them.

Note: This script can be "extended" to check for other objects (besides DataWindows) as well. I say "extended" because…
Note If you do have a need to "extend" the script for this event, you should first check the ancestor’s return code. And to do so (at least pre-PB 6.0), you must override the ancestor script. You must check the ancestor’s return code because there is already script in the PFC’s ancestor code, and it may fail.

Note: In thePFC 6.0 Release, rather than having to extend this event to recognize such controls as the ListView and TreeView, you may take advantage of the new Self-Updating Object (SUO) architecture. To do so, you may either switch to using the new SUO objects. Or you may add the SUO API to your current objects. For more details, please see the section at the end of this appendix which covers the PFC 6.0 Release.

Bug Alert! - Pre-PFC 5.0.02
Prior to PFC 5.0.02, there was a bug in this event. The problem was that AcceptText()’s were being performed on DataWindows in Tab pages which were not yet created. Here is the code that needs to be fixed as shown:
Case Else
// This is a NonLink PFC DW or a Non PFC DataWindow
// Perform AcceptText, check rc
la_rc = ldw_dw.AcceptText()
If la_rc < 0 Then 
   If ab_FocusOnError Then ldw_dw.SetFocus()
   Return -1
End If
// 5.0.02 Do not perform AcceptText on Datawidows found on uncreated tab pages.
Case Else
// This is a NonLink PFC DW or a Non PFC DataWindow
// Do not perform AcceptText on Datawidows found on uncreated
// tab pages.
If ldw_dw.RowCount() + ldw_dw.FilteredCount() + &
   ldw_dw.ModifiedCount() + ldw_dw.DeletedCount() > 0 Then 
   // Perform AcceptText, check rc
   la_rc = ldw_dw.AcceptText()
   If la_rc < 0 Then 
      If ab_FocusOnError Then ldw_dw.SetFocus()
      Return -1
   End If
End If

* Datawidows= PFC code Easter egg found by Judy Cote <s>

Rather than overriding the ancestor and copying the code to make the change… In this case, you could change the PFC code directly since it is a known problem with a known, existing, official fix in a particular version. This is not a bad idea in these kinds of cases. There are very few cases like this where it is kosher to change the PFC directly. But, still, don’t forget to document it!
pfc_BeginTran ( ) returns integer Begin a Transaction

Returns integer:
1 = Success
-1 = Error

Note
pfc_BeginTran is a KEY event in the PFC!
Note The pfc_BeginTrans&pfc_EndTrans events control your database transactions. Therefore, they are the KEY to the success of your client/server application!

PFC triggers this event from the pfc_Save event in order to begin a transaction.

Note: You should add code here to begin a transaction.

Note The way that you will approach this depends on the database which you are using, and how your company wishes to handle their transactions. Consult your DataBase Administrator for details. This is very important.

Note Be careful to make sure that if code is in this event, that the corresponding code is also in the matching pfc_EndTran event. In all truth, code should be required in both of them.
Ex: In your pfc_BeginTran event for your particular window:
// Begin a transaction
if SQLCA.of_begin( ) <> 0 then 
   SQLCA.of_DisplayError( "pfc_begintran event after"       + "~r~n" + &
                                      "of_begin in"                            + "~r~n" + &
                                      "window titled ~r~n'"  + this.Title  + "'~r~n"+ &
                                      "(WindowObject is "  + this.ClassName() + &
                                      ")" )
   Return -1 //error
end if
Return 1 //success
pfc_Close ( ) Close a PFC Window The PFC menu calls this event whenever the File/Close menu option is selected.

Closes the window. The PFC script simply contains Close(this).

Post or trigger this event in order to close a window.

pfc_ControlGotFocus ( dragobject adrg_control ) A Control just Got Focus

Keeps track of last active DataWindow

Argument adrg_control:
control (of type dragobject) which just got focus

PFC triggers this event whenever any PFC visual control gets focus.

This event keeps track of the current control.

Specifically it keeps track of the last active DataWindow. (Needed by several things, including the "Message Router"). If the control getting focus is a DataWindow inherited from u_dw, this event sets the idw_Active instance variable.

If you have some special need, you could use this event to add application-specific functionality to be performed when a visual control gets focus.

pfc_DBError ( ) returns integer

5.0.02

Display any stored DBError message

Returns integer:
1 = Success
-1 = Error

PFC triggers this event from the pfc_Save event in order to display any saved DBError message.

This event is only triggered if there was an error, and it does so only after the transaction has ended (after pfc_EndTran).

pfc_Descendant ( ) returns boolean Is this Window a PFC Descendant?

Returns boolean:

TRUE = Window is a descendant of w_master

FALSE = Window is not a descendant of w_master

PFC events and functions call this event to determine if the window is inherited from w_master.

This event simply has the script to Return True… Here’s an interesting, indepth look into this odd little trick here, compliments of Jon Credit…

Whenever the PFC code uses this event, the PFC code does a TriggerEvent( ) as follows:
If adrg_control.TriggerEvent ("pfc_descendant") = 1 Then…
The TriggerEvent( ) function will return either a 1 or -1…
1 if the event exists AND has code in it;
-1 if the event does not exist OR the event exists but does not have any code.
So the PFC guys and gal had to put something in the event in order for TriggerEvent to return 1. Interesting tidbit there for you.

Internal use only (so usually you would not need to call this)

pfc_EndTran ( integer ai_update_results ) returns integer End a Transaction

Argument ai_update_results:
integer that contains the results of the last update
1 = Success
-1 = Error
NULL= Some argument NULL

Returns integer:
1 = Success
-1 = Error

Note
pfc_EndTran is a KEY event in the PFC!
Note The pfc_BeginTrans&pfc_EndTrans events control your database transactions. Therefore, they are the KEY to the success of your client/server application!

PFC triggers this event from the pfc_Save event in order to end a transaction.

Note: You should add code here to end a transaction.

Note The way that you will approach this depends on the database which you are using, and how your company wishes to handle their transactions. Consult your DataBase Administrator for details. This is very important.

Note: You would normally call n_tr’s of_end( ) function here, and then encapsulate of_commit( ) and of_rollback( ) calls within n_tr itself.

Note: The ai_update_results argument will indicate the success (when = 1) or failure of the previous Update functions. You will need to pass this to your of_end( ) function so that it knows whether or not to ROLLBACK or COMMIT.

Note Be careful to make sure that if code is in this event, that the corresponding code is also in the matching pfc_BeginTran event. In all truth, code should be required in both of them.
Ex: In your pfc_EndTran event for your particular window:
// End the transaction
if SQLCA.of_end( ai_update_results ) <> 0 then
   SQLCA.of_DisplayError( "pfc_endtran event after"         + "~r~n" + &
                                      "of_end in"                              + "~r~n" + &
                                      "window titled ~r~n'"  + this.Title  + "'~r~n"+ &
                                      "(WindowObject is "  + this.ClassName() + &
                                      ")" )
   Return -1 //error
end if
Return 1 //success
pfc_Help ( ) returns integer Show Help

Returns integer:
1 = Success
-1 = Error

The PFC menu calls this event whenever the Help/Help Topics menu option is selected.

You can also trigger it directly (from a Help CommandButton, for example).

It calls the ShowHelp function by index, topic or keyword, based on the ia_HelpTypeID instance variable.

Note: To specify the application's Help filename, call the n_cst_AppManager’s of_SetHelpfile( ) function.

pfc_MessageRouter ( string as_message ) returns integer Message Router Key Event

Route a message to the current window, current active control, or to the last active DataWindow control… last resort to frame if MDI

Argument as_message:

string of event name to route

Returns integer:
1 = Success
0 = No objects contain the user event
-1 = Error

Note
pfc_MessageRouter is a KEY event in the PFC!
Note This event is known as the "Message Router" event. Its purpose is to "route" the message passed in argument as_message to the appropriate object.

PFC triggers this event most anytime that a menu option is selected. At that time, the menu uses its of_SendMessage( ) function to trigger this event to communicate to windows.

Message = User Event… The "Message" that the menu passes is actually the passed as_message argument containing a user event name that needs to be triggered on the window or one of its controls.

Route = Trigger Event… By "Routing", we mean that the menu needs to tell some window or window control to do something. So the menu tries to trigger the given event on various window/window controls until some control understands the message.

There is a particular defined order of objects to which the Message Router attempts to send a message…

First, it will attempt to trigger the event on the current window.
If not found there, then on the current control.
If not found there, then on the last active DataWindow control.
Finally, if still not found (and if this is an MDI frame), the event is triggered on the frame.

Note "Message Router" tries to trigger the given event on these objects, in this order…
è Current Window

è Current Active Control

è Last Active DataWindow

è MDI Frame

until it finds an object that can process the message

Note: All PFC visual controls, including the windows and the u_dw, include events that work with the Message Router.

Note: Although this window event usually bridges a menu and a window/its controls, it could also be used to communicate between a window/its control and any other kind of object.

pfc_MicroHelp ( string as_microhelp ) Display MicroHelp

Argument as_microhelp:
string for MicroHelp text to display

This event is called by u_dw’s ItemFocusChanged( ) event.

It is extended in pfc_w_sheet and pfc_w_frame to simply display the string in as_microhelp in the MicroHelp area of the frame.

Pfc_w_sheet simply routes to pfc_w_frame, which in turn simply displays the MicroHelp text if the AppManager’s Display MicroHelp attribute is TRUE.

pfc_New ( ) Add New Entity to Window

(For example File/New menu selected, so you would Add a New Employee by inserting a blank row to be entered by user)

The PFC menu calls this event whenever the File/New menu option is selected.

Note: If needed, you should add code here to perform processing to add a new entity to the current window, window control, or DataWindow.

Ex: In your pfc_New event for your particular window:
// Add a New Employee
dw_Employee.InsertRow(0)
or // Add a New Row to the Active DW
idw_Active.InsertRow(0)
pfc_Open ( ) Open sheets from the frame

Or open a file from this Window

Open another Window from this Window

(For example File/Open from a selection list window of all Employees, so you would maybe open a detail window for that selected Employee)

Note
pfc_Open is a KEY event in the PFC!

The PFC menu calls this event whenever the File/Open menu option is selected.

Note This pfc_Open event is in a different category from the Open, pfc_PreOpen and pfc_PostOpen events… This pfc_Open event opens another window from the current window. Whereas the other events are involved with the opening of this current window. They are easy to get mixed up sometimes, so make a mental note of the distinction.

Note: You will need to add code here to open your sheets from your frame.

Note: If needed, you should add code here to open another window or a file from the current window.

Open Another Window - Ex: In your pfc_Open event for your particular selection window:
SetPointer(Hourglass!)
long ll_row
string ls_student_id
ll_row = dw_student.GetRow()
if ll_row < 1 then
   messagebox("Student Selection Error", "No student selected")
else
   ls_student_id = dw_student.object.state_stu_id[ll_row]
   message.inv_open_window.is_window_stringparm[1] = ls_student_id
   message.inv_open_window.is_window_name = "w_stu_pmz210_maint_id_demo"
   gnv_app.of_GetFrame().Event pfc_open()
end if
Open A File - PS** Ex: In your pfc_Open event for your particular window:
// Display Open dialog box when the user selects File/Open menu option
// Is_fullname and is_filename are instance variables:
Integer li_fileid 
SetPointer (HourGlass!) 
IF GetFileOpenName("Open", is_fullname, is_filename &
      "txt", "Text Files (*.txt),*.txt) < 1 THEN
   Return 
END IF 
// Open the new file and put results into the MLE
li_fileid = FileOpen(is_fullname, StreamMode!) 
FileRead (li_fileid, mle_notepad.text) 
FileClose (li_fileid)
** from hereonè "PS Ex:" denotes an example from the Powersoft Manual
pfc_PageSetup ( ) returns integer Display a Page Setup Dialog Box

Returns integer:
1 = Success
-1 = Error

The PFC menu calls this event whenever the File/Page Setup menu option is selected.

Note: If needed, you should add code here to display the Page Setup dialog box for a particular DataWindow. If there are multiple DataWindows to be printed, you may also want to then sync their print properties accordingly.

Note The PFC has a couple of "PFC DLLs" that it accesses for print-related external function calls [via pfc_n_cst_platformwin## (16/32)]. For example, u_dw’s pfc_PageSetupDlg event needs these DLLs to open the print dialog window. So make sure that you include these DLLs when deploying. These PFC DLLs are PFCCOMM.DLL (16-bit) and PFCCOM32.DLL (32-bit). By the way, the other PFC DLL to be aware of is PFCFLSRV.DLL, and it is used for file services [via pfc_n_cst_filesrvwin##].

Ex: In your pfc_PageSetup for your particular window:
Integer li_rc 
s_pagesetupattrib lstr_pagesetup
// Open the Page Setup Dialog Box for the first DataWindow
li_rc = dw_1.event pfc_PageSetupDlg(lstr_pagesetup) 
// Sync the paper source for the other two DataWindows
IF li_rc = 1 THEN
   dw_2.object.datawindow.print.paper.source = &
      lstr_pagesetup.i_papersource
   dw_3.object.datawindow.print.paper.source = &
      lstr_pagesetup.i_papersource 
   RETURN 1 //Success
ELSE
   RETURN -1 //Error
END IF
pfc_PostOpen ( ) Post-Open Processing
Note
pfc_PostOpen is a KEY event in the PFC!

PFC posts this event at the top of the window’s Open event, right after it triggers thepfc_PreOpen event.

This event is posted, not triggered. Therefore, it is simply queued, and will execute whenever it gets a chance. It would normally end up executing just after a window opens (and displays). This is the classic use of a posted event in PowerBuilder.

Note I was surprised to find that this event was not posted at the end of the PFC’s Open code. This is because if any process in the window’s Open event allows a queued event to slip in (such as a MessageBox or something), this event would execute "prematurely". So just be aware.
  • If for some reason you end up overriding the Open event, avoid anything that could allow a posted event to execute (a MessageBox being a common culprit of this)
  • In some cases, this event could end up executing before the window actually opens and displays. This is because, in fact, if you are in PFC debug mode, there are some MessageBoxes that may display in the PFC Open event if PFC encounters an error.

Add code here which you would like to execute just after a window opens. Processes which need to be done when the window opens, but not necessarily right away are best placed here, rather than in thepfc_PreOpen event, which holds up the display of the window to the user. Remember, perceived quick response time is very important when dealing with human users who need to be kept happy <s>…

So save any of your:

  • of_SetTransObject( )’s (if no tabs involved)
  • database retrievals or
  • long-running processes

for this event.

PS-modified Ex: In your pfc_PostOpen event for your particular window:
Long ll_return 
dw_employee.of_SetTransObject(SQLCA)
ll_return = dw_employee.Retrieve( ) 
IF ll_return = -1 THEN 
   MessageBox("Retrieve", "Retrieval error") 
ELSE 
   gnv_app.of_get_frame( ).SetMicroHelp( String(ll_return) + " rows retrieved") 
END IF
pfc_PostUpdate ( powerobject apo_control [ ] ) returns integer Post-Update Processing

Reset update flags on all DataWindows

Argument apo_control [ ]:
an array of powerobjects containing a control array for the current window which within PFC’s code usually holds all of the DataWindows which have pending updates

Note: For optional uses, you may pass an array of any objects which you would like to have this event perform Post-update processing upon

Returns integer:
1 = Success
-1 = Error

PFC triggers this event at the very bottom of the pfc_Save event if all of the previous save process was successful. The logic in this event executes outside of any database transaction.

The PFC code that is already in this even will reset all of the DataWindow update flags since all of the database updates were successful and the DataWindows need to know that.

Note: This script can be "extended" to check for other objects (besides DataWindows), such as ListViews or TreeViews as well. I say "extended" because…
Note If you do have a need to "extend" the script for this event, you should first check the ancestor’s return code. And to do so (at least pre-PB 6.0), you must override the ancestor script. You must check the ancestor’s return code because there is already script in the PFC’s ancestor code, and it may fail.

Note: In thePFC 6.0 Release, rather than having to extend this event to recognize such controls as the ListView and TreeView, you may take advantage of the new Self-Updating Object (SUO) architecture. To do so, you may either switch to using the new SUO objects. Or you may add the SUO API to your current objects. For more details, please see the section at the end of this appendix which covers the PFC 6.0 Release.

Note: You can also extend this event to perform additional updates that do not need to be done within the database transaction.

PS Ex: In your pfc_PostUpdate for your particular window to add functionality to reset the update flags for the ListView:
Integer li_return
Integer li_max, li_i
powerobject lpo_changed
u_lv llv_changed
// apo_control is an argument to pfc_PostUpdate.
// It contains the ipo_pendingupdates array.
li_max = UpperBound(apo_control)
FOR li_i = 1 to li_max
   lpo_changed = apo_control(li_i)
   IF lpo_changed.TypeOf () = Listview! THEN
      llv_changed = ipo_changed
      li_return = llv_changed.of_ResetUpdate()
      IF li_return = -1 THEN
         Return -1
      END IF
      llv_changed.ib_changed = FALSE
   END IF
NEXT
Return 1
pfc_PreClose ( ) returns integer Pre-Close Processing

Returns integer:
1 = Success
-1 = Prevent window from closing

PFC triggers this event from the top of the CloseQuery event in order to give you a chance to do anything that you may need to do just before the CloseQuery processing begins since the window will be closing, but there is still a chance to stop if from doing so.

Note: If needed, you should add code here that needs to be done before the CloseQuery processing begins.

PS Ex: In your pfc_PreClose for your particular window:
// Write message to a log file to report that the window is closing
IF inv_filesrv.of_FileWrite (is_logfile, "Window: " + this.title &
      + " closing at " + String(Today()) &
      + String(Now()),TRUE) = 1 THEN
   Return 1 //Success
ELSE
   Return -1 //Prevent window from closing
END IF
pfc_PreOpen ( ) Pre-Open Processing
Note
pfc_PreOpen is a KEY event in the PFC!

PFC triggers this event first thing at the top of the Open event.

Note Remember that since this event is triggered at the top of the Open event, when this code executes, the window has not yet opened so it has not yet been displayed to the user.

Also, it is not good practice to put anything in here that would try to close the window, because the window is not yet open. Save anything like that for the pfc_PostOpen event.

Also save any database retrievals or long-running processes for the pfc_PostOpen event as well.

Note: Add code here which you need to execute prior to the PFC’s Open event or anything that cannot wait until the pfc_PostOpen event. Common things to add here are:

  • Turn on any of your Window Services, using functions such as of_SetResize(TRUE), of_SetPreference(TRUE), of_SetBase(TRUE)

(Note The Resize and the Preference services need to be turned on here before the PFC’s Open event script because that is where the PFC does the preference resizes/moves)

  • Register the window with the Security Service by calling gnv_app.inv_security.of_SetSecurity(this)
  • Set your ia_HelpTypeID variable
  • Disable theCloseQuery logic (by setting ib_DisableCloseQuery = FALSE) if user does not have the proper security.
Examples: In pfc_PreOpen event for whichever level of your window that you deem appropriate (perhaps w_sheet level, perhaps application window level, etc) :
// Turn on the Window Resize Service
this.of_SetResize (TRUE)
this.inv_Resize.of_register (dw_1,"ScaletoRight&Bottom")
// Turn on the Window Preference Service
this.of_SetPreference (TRUE)
this.inv_preference.of_SetToolBars (TRUE)
this.inv_preference.of_SetWindow (TRUE)
// Turn on the Basic Window Service 
// (in your w_response to center all response windows for example)
this.of_SetBase(TRUE)
this.inv_Base.of_Center()
// Register this window with the Security Service 
IF IsValid (gnv_app.inv_security) THEN
   gnv_app.inv_security.of_SetSecurity(this) 
END IF
// Set your Help Type ID
ia_HelpTypeID = "Part#"
// To protect the user from being able to 
// save data on "read only" windows, 
// by responding "Yes" to a message that says
// that the data has changed would you like to save?
m_xxx_master   lm_menu
lm_menu = this.menuid
IF lm_menu.m_file.m_save.Enabled THEN
   ib_DisableCloseQuery =FALSE
ELSE
   ib_DisableCloseQuery =TRUE
END IF
pfc_PreUpdate ( ) returns integer Pre-Update Processing

Returns integer:
1 = Success
-1 = Error, terminate save processing

PFC triggers this event from the pfc_Save event after it has been determined that there are changes and there are no validation errors, but before the database transaction is actually started.

Note: If needed, you should add code here to perform Pre-Update processing that is needed before the updates take place, but does not need to be in the actual database transaction. One great example (contributed by Bob Fields), is the case where you would generate new primary keys for inserts, which must be outside of the normal save transaction.

Note: If you do add code with database updates in here, please realize that if you are running with AUTOCOMMIT=FALSE, you should be sure to include a COMMIT after any updates that you perform in here. Otherwise, the updates you do in here will be included with the database transaction. This would defeat the purpose of including those updates in here, versus simply extending the pfc_Update event.

PS Ex: In your pfc_PreUpdate for your particular window:
// Check for any unauthorized User ID’s
String ls_user
ls_user = gnv_app.of_GetUserID( ) 
IF ls_user = "UnauthorizedPerson" THEN
   Return -1 //Do not proceed with update - Terminate save
ELSE
   Return 1 //Okay to proceed with update
END IF
pfc_Print ( ) returns integer Print Window - first showing Print Dialog Box (or Print content of one or more DataWindows on Window)

Returns integer:
User defined, suggested:
1 = Success
-1 = Error

The PFC menu calls this event whenever the File/Print menu option is selected.
Note: The only difference between this event and the pfc_PrintImmediate event is that this event should first open the print dialog box.

Note: If needed, you should add code here to print windows or DataWindows, first displaying the Print dialog box.

Note: If you leave this event (which pertains to the whole window) empty, the Message Router will attempt to send the pfc_print message to the "last active DataWindow". And then that DataWindow will print. So it is not necessary to add code here if that is all that you would like for this event to do, especially if you only have one DataWindow on your window… However, please note that if for some reason, there was no "last active DataWindow", nothing will happen. Also, if you leave this event empty and there are multiple DataWindows on the current window, you should point out to the user the expected behavior (of how the "last active DataWindow" will print), as it is not obvious to them. That way, they will know to click on the desired DataWindow before printing.

Note: In this event you might choose to:

  1. Leave the script emtpy and allow the "last active DataWindow" to print
  2. Print multiple DataWindows
  3. Force only a particular DataWindow to always print
  4. Call the DataWindow Report Service's of_CreateComposite( ) function to create a composite report of all the window's DataWindows

Note The PFC has a couple of "PFC DLLs" that it accesses for print-related external function calls [via pfc_n_cst_platformwin## (16/32)]. For example, u_dw’s pfc_PageSetupDlg event needs these DLLs to open the print dialog window. So make sure that you include these DLLs when deploying. These PFC DLLs are PFCCOMM.DLL (16-bit) and PFCCOM32.DLL (32-bit). By the way, the other PFC DLL to be aware of is PFCFLSRV.DLL, and it is used for file services [via pfc_n_cst_filesrvwin##].

Ex: In your pfc_Print for your particular window:
// Force only the student DataWindow to print
Integer li_rc 
s_pagesetupattrib lstr_pagesetup
// Open the Print Dialog Box
li_rc = dw_student.event pfc_PageSetupDlg(lstr_pagesetup)
IF li_rc = 1 THEN
   // Print the Student DataWindow Only
   IF dw_student.Print( ) = 1 THEN 
      RETURN 1 //Success
   ELSE
      RETURN -1 //Error
   END IF
ELSE
   RETURN -1 //Error
END IF 
pfc_PrintImmediate ( ) returns integer Print Information Immediately (without displaying Print Dialog Box first)

Returns integer:
User defined, suggested:
1 = Success
-1 = Error

The PFC menu calls this event whenever the "File/Print Immediate..." menu option is selected.
Note: The difference between this event and the pfc_Print event is that this event should not first open the print dialog box. It should just print the report immediately.

Note: If needed, you should add code here to print windows or DataWindows, without first displaying the Print dialog box.

Note: Please see extra notes in the pfc_Print event directly above, since these two events are so similar.

Ex: In your pfc_PrintImmediate for your particular window:
// Print the Employee DataWindow
IF dw_employee.Event pfc_PrintImmediate( ) <> 1 THEN
   RETURN -1
END IF
// Print the Employee’s Pay DataWindow
IF dw_employee_pay.Event pfc_PrintImmediate( ) <> 1 THEN
   RETURN -1
END IF
RETURN 1
pfc_Save ( ) returns integer Save any Pending Changes

Returns integer:
1 = Success
0 = No pending changes
-1 = AcceptText error
-2 = UpdatesPending error
-3 = Validation error
-4 = Failed pfc_PreUpdate
-5 = Failed pfc_BeginTran
-6 = Failed pfc_Update
-7 = Failed pfc_EndTran
-8 = Failed pfc_PostUpdate
-9 = Failed pfc_DBError

Note
pfc_Save is a KEY event in the PFC!
Note Please note that there has been a major overhaul of the Save Process of the PFC 6.0 Release.

The PFC menu calls this event whenever the File/Save menu option is selected.

And the PFC CloseQuery event calls this user event automatically after finding pending updates and verifying that the user would like to save before closing.

Note: You can call it from your application to save changes, as needed also.

This pfc_Save event saves all changes for all DataWindows (plus any added controls) in the window. It calls:

  • Only If NOT ib_CloseStatus

(since if it is closing this function has already been called)
of_UpdateChecks( ) (exits save on error or if no updates to do)

which in turn calls these three events

  1. pfc_AcceptText
    to do an AcceptText on all DataWindows.
    If no AcceptText errors…
  2. pfc_UpdatesPending
    to look for any DataWindows with pending changes… Builds an array of all of the DataWindows which do in ipo_PendingUpdates[ ]
  3. pfc_Validation
    to check for any Validation errors in any of the DataWindows with pending updates
  • (Continues if updates pending and no validation errors)
  • pfc_PreUpdate (exits save on error)
    to perform anything special needed before updating transaction begins
Starting DB Transaction
  • pfc_BeginTran (exits save on error)
  • Set ib_SaveStatus = TRUE (suppress error msgs)
  • pfc_Update
    which updates all DataWindows in
    ipo_PendingUpdates[ ]
  • Set ib_SaveStatus = FALSE
  • pfc_EndTran (always executes even if error on pfc_Update)

Ended DB Transaction

  • pfc_DBError (if error during pfc_PreUpdate)
    which displays any DBErrors from pfc_Update
  • pfc_PostUpdate (if no errors throughout)
    which resets all update flags on all DataWindows in ipo_PendingUpdates[ ] if all updates completed successful

Note: Basically, if any of the events encounter errors, the save process is aborted and the corresponding error return code is returned. There are exceptions to this. For example, if pfc_Update were not successful, the transaction still needs to be ended and a DBError message still needs to display. Therefore, first pfc_EndTran and pfc_DBError are executed, and then it bails out.

pfc_SaveAs ( ) SaveAs all or part of a window, usually using the SaveAs Dialog Box to choose a name of the file to be saved The PFC menu calls this event whenever the File/Save As menu option is selected.
Note: The difference between this event and the pfc_Save event is that this event usually saves data to a File rather than to the database.

Note: If needed, you should add code here to save all or part of a window (usually as a File format versus updating the DataWindow).

Ex: In your pfc_SaveAs for your particular window:
IF IsValid (idw_active) THEN
   idw_active.SaveAs()
END IF 
PS Ex: In your pfc_SaveAs for your particular window:
// Display the Save As dialog box
String is_docname, is_named 
Integer li_value 
li_value = GetFileSaveName( "Save As", &
   is_docname, is_named, "TXT", &
   "Text Files (*.TXT),*.TXT," + &
   " Doc Files (*.DOC), *.DOC")
pfc_Update ( powerobject apo_control [ ] ) returns integer Update all DataWindows

Argument apo_control [ ]:
an array of powerobjects containing a control array for the current window which within PFC code usually holds all of the DataWindows which have pending updates

Note: For optional uses, you may pass an array of any objects which you would like to have this event perform update processing upon

Returns integer:
1 = Success
-1 = Error

PFC triggers this event in the middle of the pfc_Save event.

This event actually updates all of the DataWindows with modified, updateable DataWindows.

  • Linkage service
    Calls the
    n_cst_dwsrv_linkage of_Update( ) function
  • All other PFC DataWindows
    Calls the datawindow’s
    pfc_Update event
  • Non-PFC DataWindows
    Calls the PowerScript
    Update( ) function
Note For the duration of this event, the ib_SaveStatus flag is turned on, and no messages or other modal windows should be displayed. This is because we are in the middle of a database transaction, and we want to finish just as quickly as possible - to avoid causing locks. So if any DataWindow’s DBError event should fire due to a database error, the PFC will store the error message into is_DBErrorMsg for later display, after the transaction has ended (after pfc_EndTrans).

Note: This script can be "extended" to check for other objects (besides DataWindows), such as ListViews or TreeViews as well. I say "extended" because…
Note If you do have a need to "extend" the script for this event, you should first check the ancestor’s return code. And to do so (at least pre-PB 6.0), you must override the ancestor script. You must check the ancestor’s return code because there is already script in the PFC’s ancestor code, and it may fail. Please see the sample below for how to do this.

Note: In thePFC 6.0 Release, rather than having to extend this event to recognize such controls as the ListView and TreeView, you may take advantage of the new Self-Updating Object (SUO) architecture. To do so, you may either switch to using the new SUO objects. Or you may add the SUO API to your current objects. For more details, please see the section at the end of this appendix which covers the PFC 6.0 Release.

Ex: In your pfc_Update event for your particular window:
//////////////////////////////////////////////////////////////////////////////
//
// NOTE: Ancestor is overridden!!
//
//////////////////////////////////////////////////////////////////////////////
// Override ancestor’s code 
// (so that we may check the ancestor’s return code)
// Check ancestor’s return code…
// Note that with PowerBuilder 6.0 you can do this easier
// This is done so that if the PFC’s ancestor already had a failure, 
// we want do not want to continue with our updates
// and then possibly return a misleading return code 
// that says that our update was successful
li_rc = Super::EVENT pfc_update() 
IF li_rc <> 1 THEN return li_rc
pfc_UpdatesPending ( powerobject apo_control [ ] ) returns integer Check if Updates are Pending

(i.e. Check if Changes were made that were not yet saved)

Populate the ipo_PendingUpdates[ ] array

Argument apo_control [ ]:
an array of powerobjects containing the control array for the current window when PFC triggers this event from of_UpdateChecks( ) … This array is "read in", and the ipo_PendingUpdates[ ] array is then "written out"

Note: For optional uses, you may pass an array of any objects which you would like to have this event populate the ipo_PendingUpdates[ ] array

Returns integer:
1 = Pending updates found
0 = No pending updates
-1 = AcceptText failed
-2 = Other validation failed
-3 = Validation error

This event is triggered by of_UpdateChecks( ) from the CloseQuery or the pfc_save event.

It searches for DataWindows which have pending changes (unsaved data), and it builds an array of all of the DataWindows found which therefore need to be updated. These DataWindow objects are then stored in the ipo_PendingUpdates[ ] array.

In other words, it looks thru all of the DataWindows found in its argument’s control array apo_control [ ], and then it populates the instance variable ipo_PendingUpdates[ ] array with those that have unsaved data.

When CloseQuery calls this user event, validation messages are suppressed.

Note: This event includes support for the Linkage Service and for non-PFC DataWindows as well.

Note: This event features recursive processing for tab controls and user objects. In other words, this event calls itself in order to drill down through tab controls, tab pages and user objects, looking for DataWindows within them.

Note: A tip from Bob Fields… In order to improve performance, you may wish to populate the ipo_PendingUpdates[ ] array directly with all updateable DataWindows, rather than having this function call itself recursively.

Note: This script can be "extended" to check for other objects (besides DataWindows) as well. I say "extended" because…
Note If you do have a need to "extend" the script for this event, you should first check the ancestor’s return code. And to do so (at least pre-PB 6.0), you must override the ancestor script. You must check the ancestor’s return code because there is already script in the PFC’s ancestor code, and it may fail.

Note: In thePFC 6.0 Release, rather than having to extend this event to recognize such controls as the ListView and TreeView, you may take advantage of the new Self-Updating Object (SUO) architecture. To do so, you may either switch to using the new SUO objects. Or you may add the SUO API to your current objects. For more details, please see the section at the end of this appendix which covers the PFC 6.0 Release

Bug Alert! - Pre-PFC 5.0.03 - Tech Support Bug #: 40210
Prior to PFC 5.0.03, there was a bug in this event. The problem was that pfc_UpdatesPending gets triggered recursively and li_max doesn't get reset. So if you have nested objects, then your update array may not be properly loaded, and not all of your objects would get updated properly. Here is the code that needs to be fixed as shown:
If lb_updatespending Then
   li_max ++
   ipo_pendingupdates[li_max] = ldw_dw
End If
// 5.0.03 Corrected variable information which allowed the new upperbound for 
// ipo_pendingupdates to be off due to the recursive nature of the event.
If lb_updatespending Then
   // Get the new upperbound for the pending changes.
   li_newupper = UpperBound (ipo_pendingupdates) + 1
   // Store the control with updates pending.
   ipo_pendingupdates[li_newupper] = ldw_dw
End If
Rather than overriding the ancestor and copying the code to make the change… In this case, you could change the PFC code directly since it is a known problem with a known, existing, official fix in a particular version. This is not a bad idea in these kinds of cases. There are very few cases like this where it is kosher to change the PFC directly. But, still, don’t forget to document it!
pfc_Validation ( powerobject apo_control [ ] ) returns integer Validate all DataWindows with Pending Updates

Argument apo_control [ ]:
an array of powerobjects containing a control array for the current window which holds all of the DataWindows which have pending updates when PFC triggers this event from of_UpdateChecks( )

Note: For optional uses, you may pass an array of any objects which you would like to have this event perform validation processing upon

Returns integer:
1 = Success, no validation errors
-1 = Error

This event is triggered by of_UpdateChecks( ) from the CloseQuery or the pfc_save event.

Searches for validation errors in any DataWindow which has pending updates.

  • Linkage service
    Calls the
    n_cst_dwsrv_linkage of_Validation( ) function
  • All other PFC DataWindows
    Calls the datawindow’s
    pfc_Validation event