For years, people have said that PowerBuilder Foundation Classes (PFC) has a steep learning curve. Drives me crazy. Don't believe it for a minute.
While it's true that there is a lot to learn about PFC, the same can be said of cars, but few of us learn the intricacies of ignition systems before learning to drive. The fact is, there are three things you need to know before building your application with PFC; the rest can be learned later. We'll discuss the basic benefits of starting with a PFC architecture with minimal understanding later, but for those readers with attention issues, let's get started on the 15-minute PFC learning curve.
Setting Up the Application Object
First create an application directory and copy in everything from the PFC folder, but not the subdirectories. Then create an application with either
- The New Application Wizard
- Create the application object and application PBL in the directory you've created.
- The wizard finishes.
- Right-click on the new target, select Properties, and add all the PBLs you copied from the PFC folder to the Library Search Path.
- Create your own application manager by creating an empty descendant of (PfeApSrv.pbl) n_cst_AppManager in your application PBL, naming it something like n_cst_AppMgr_YourAppName.
- Export (PfcApp.pbl)Generic_Pfc_App and copy its contents to your application object, starting with the global variable, but with the following exception:
The New Application Template Wizard in PB8 or PB9, or the Wizard downloaded from the PFC - Open Source project (http://pfc.codexchange.sybase.com) in PB10
- When declaring the global variable gnv_App (an important magic key in PFC), make it of type n_cst_AppMgr_YourAppName as created above instead of type n_cst_AppManager.
- Create the application object and application PBL in the directory you've created.
- Select the PFC-based application option when prompted.
- Add to the application Library List all the PBLs you copied from the PFC folder when prompted.
- Other options: yada, yada, yada.
- The wizard finishes.
- Create your own application manager, by creating an empty descendant of (PfeApSrv.pbl) n_cst_AppManager in your application PBL, naming it something like n_cst_AppMgr_YourAppName.
- Change the generated global variable declaration for gnv_app from n_cst_AppManager to n_cst_AppMgr_YourAppName.
After creating the application object with either of these methods, proceed with the following steps:
- In the application object, go into Additional Properties, Variable Types. The message object should already be set to n_Msg and the transaction object to n_Tr. Change the error object to n_Err, the dynamic description area (SQLDA) to n_Dda, and the dynamic staging area (SQLSA) to n_Dsa.
- Remove PfcApp.pbl from your library list and application directory (it was only there for the application model used in the New Application Wizard technique).
- When you think you want to add code to the application object, instead add it to the corresponding event in your application manager. For example, one of the first things you'll want to do is add an Open(<window>) line to the pfc_Open event of your application manager, not the Open event of your application.
Inherit Everything from Objects in the PFE Layer
- Whenever you create a new object, find a PFE object (Pfe*.pbl) to inherit it from.
Whenever you add a new control or embed a new object in a class, find the PFE standard object to use (usually in the PfeMain.pbl).
- Windows: Inherit from windows in PfeMain.pbl (w_Main, w_Sheet, w_Frame, w_Response, w_Child, w_Popup), but never from w_Master.
- Custom Classes: If you don't know of a closer class, inherit from n_Base in PfeMain.pbl.
- Custom Visual: If you don't know of a closer class, inherit from u_Base in PfeMain.pbl.
- Standard Class/Standard Visual: Inherit from the appropriate class in PfeMain.pbl (the comments identify the class of each object if you can't discern this from the naming convention).
- Structures can't be inherited, but consider a descendant of n_BaseAttrib with instance variables instead of structure elements. These can be used instead of structures pretty much everywhere structures can be used except external function calls, and hold the opportunity for extension through their object-oriented characteristics.
- Global functions can't be inherited, but consider grouping (encapsulating) functions of a similar type in an autoinstantiate custom class. Again, it gives you opportunities such as function overloading or overriding if the functional group is ever brought to another application.
Whenever you declare a variable of a standard class data type (e.g., DataStore), use its PFE equivalent instead (e.g. n_Ds).
You should never have to directly use or inherit from something in the PFC layer (Pfc*.pbl).
- Eventually you'll want to customize your painter toolbars to have many of those PFE objects available directly from the toolbar.
Logical Unit of Work Basics
- There is one primary functionality that is forced on you unless you do something to stop it: the Logical Unit of Work (LUW) service's Save functionality
- The LUW will burrow through any window and find any objects that it considers updateable, including DataWindows, TreeViews, and ListViews. If you know enough about PowerBuilder, these last two shouldn't make sense to you yet, since the native versions of these controls aren't updateable, but that's okay for now. It will bundle all these objects together and treat them as one logical unit of work. In very basic terms, this means that they will all be saved at the same time, and if any of the updates on these objects fail, none of the updates will be saved.
- When you want to save, call the pfc_Save event on the window.
- When the user tries to close the window without saving, the LUW will prompt to save changes if outstanding changes exist. A failed save will cancel the window closing, giving the user an opportunity to fix the data and have it saved.
- If you want to customize the save process, put code in one of the following events, in the most appropriate object (e.g., window, DataWindow, tabpage).
- pfc_AcceptText: Performs AcceptText() and similar functionality.
- pfc_UpdatesPending/pfc_UpdatesPendingRef: Returns > 0 if there are updates pending; determines if the rest of the functionality proceeds.
- pfc_Validation: Checks the data. This is the place you'll most likely want to code, to put save-time validations. Not all validations are appropriate to be checked on ItemChanged, such as validations that involve more than one field.
- pfc_UpdatePrep: Any work needed to prepare the data for updating to the database or storage.
- pfc_Update: The sending of the changed data to the database or storage.
- pfc_PostUpdate: Work that needs to be done after the update.
- If you want to bypass this functionality, either for the entire window or by excluding individual updateable objects, windows and all updateable objects have an of_SetUpdateable(<Boolean>) function.
That's it! That's all you need to know to start programming in PFC. My word processor tells me that's between 900 and 1,000 words, which even includes duplication in branched logic you didn't need to read. At the internationally accepted average reading speed of 250 words per minute, I've got 11 minutes of your time left. Let's delve into some more details.
Why This Is Good Enough
So, you're confused. You still don't know what the error service is. You're not sure if what you're about to write is correct, PFC-wise or not. That's okay. Even if you start coding with the above information and do everything else "wrong," you've got the following.
After over 10 years doing PowerBuilder support online, you'd be surprised how often the following conversation occurs:
"I've found the following bug and need a workaround/need the following feature."
"Use this code."
"But I have 30,000 windows/DataWindows/CommandButtons!!"
"Put the code in your ancestor window/DataWindow/CommandButton."
"What? But my grandfather has never even used PowerBuilder!!"
Having ancestors to everything saves you more often than you can imagine. Not only is it good for bug fixes and feature implementation, it's good for consistency. I was brought in to fix an application that had no inheritance in it at all. As I walked through the code in about 80 windows, I could see they had written their "save" functionality, started copying it to new windows, then fixed it and started copying that code (without fixing the previous instances), then fixed it again and started copying that versionâ€¦. When I re-created all those windows (which were for the most part identical except for the dataobject), I put the save code in the ancestor, giving them the opportunity to fix/change their entire system in minutes instead of weeks.
The same way a picture is worth a thousand words, a good set of well-architected code is worth a thousand design patterns books. As you have the opportunity to explore PFC as you develop, you'll see all sorts of design patterns that will make much more sense to you than reading a theoretical monologue and trying to dig through the examples in LISP. (If you don't know what LISP is, ask your grandfather.)
Good Transaction Management
I had written a pretty good framework. I was quite proud of it, especially the transaction management. Then I saw PFC's Logical Unit of Work service. It made me completely and utterly miserable. "How could I have missed feature X. Why didn't I anticipate and respond to scenario Y?" It took me a while to realize this is one of the advantages of using a professionally built framework: to be able to start from and build on experts' knowledge and experience. I've seen a lot of transaction management algorithms from both professional frameworks and in-house development, and none have even come near the job that PFC does. Not only is it simple to implement (so simple that you implement it in your windows without knowing it), but it's incredibly extensible, robust and flexible. If I had to start a system with one element from any code or framework I'd ever seen before, I would start with the Logical Unit of Work service.
You can make a mess of the GUI and get away with it. You can make the disk footprint too large with little more than a hand slap (in fact, in dollar per gigabyte days, when PowerBuilder's competition uses frameworks over 20M, I personally think disk footprint issues are a bygone issue). But if you screw up the data, you might as well start checking the newspapers for security guard jobs, because you won't be working in IT any more.
A Running Start
There are lots of issues to focus on when starting a project. You want to bypass as much as you can and get development started. Bypass the framework issues and start with PFC. You can have a working application up and running as fast as you can drop DataWindows onto windows and provide access to open them.
After reading this article, you can start coding a PFC application. With some good search tools and good habits using the help files and manuals, you might find functions and services you can use right away. You might not, but that won't stop you from some day going back and adding calls to them, or even from adding the service calls to the ancestors of the objects you're building today (hint: that's what the empty PFE layer is for, but you can learn that another day). Will you make "mistakes" by violating a PFC rule, philosophy or principle? Probably. But the correct answer is, despite what was drilled into you in school: who cares? Your primary goal is to deploy an application that meets your business needs. If you can succeed at that, you've won. And, if in the process of developing that application, you've come a few steps closer to more fully realizing the power of a PFC application, so much the better.
Beyond PFC, you've got an infrastructure that makes sharing code easier. Designing generic functionality is a lot easier when you can make assumptions about your environment and architecture. While code that I may write that depends on w_Anc_Sheet.ib_ReadyToUpdate may not be useful to too many people, code that calls LUW functionality on w_Master can be ported to any PFC-based application. You're going to have a lot more opportunities to share code between applications, both within your company and with the outside world. PFC is architected with extensions and customizations in mind. Many of the old frameworks and many of the existing custom frameworks are architected so customizations have to be built into the objects that implement the framework's functionality. If a patched version of the framework came out, you had to reimplement your changes over top of the new framework objects. PFC is built so that you can implement customization and extensions yourself or from third parties, and you don't have to worry about them when implementing an upgrade.
With seven minutes left, you're wondering what to do with yourself now. I'm going to throw a few quick tips at you to help you on your way.
Your New Friends
Get to know the help file and the documentation. If you haven't, spend 20 minutes (some other day) learning the search tools that come with the help engine; the investment will be returned several times over in productivity.
Also, get to know a good code search tool. While PowerBuilder's search is now much more adequate than it was in the early days, PBL Peeper (www.techno-kitten.com) will get you a step further. If you learn the difference between PBL Peeper's rich text search output and DataWindow search output, you'll be able to leverage the tool better. (Hint: The former can bold your match term; the latter can search, sort, and filter your results, and put you a button press away from the actual code.) Learn what PBL Peeper's Lists can give you (particularly the obscured View/Show menu option), as searching them can often be more efficient. And saving the output from PBL Peeper's Reports like the Comment Report can also leave you with a valuable resource.
Thin, Not Deep
Starting off, avoid the temptation to dig deep into any one service. Some developers I've met feel a compulsion to read and understand the code before using it. This is counterproductive with respect to frameworks in general. The point of a framework is that you don't need to understand a functionality's contents, only the usage of the implementation. Your first objective should probably be to read a couple of lines of description on each service and object, then review a couple of lines on each function. Sure, the code is there to examine and learn from. Just don't make it an activity that will prevent you from using it.
I can't believe the "superstitions" I've heard regarding PFC over the years. "It makes your application crash." "It makes migrations harder." "It makes it harder to debug." Ridiculous! PFC is nothing more than PowerBuilder objects with PowerScript code. There is nothing that makes it special (other than the level of the coders and the volume of testing) any more than any other framework that someone would implement sitting at your workstation. It will make your application crash no more or less than using any other framework, if you ignore the fact that it has been tested by thousands and bug-fixed for years. Migrating a PFC application is no more difficult than migrating any other set of code. (And, no, upgrading PFC is not a required step for migrating, but that's a topic for another day.) Debugging is no more difficult than dealing with any other large framework. PFC is not mystical monkey mojo. It is PowerBuilder code written by developers. Now, I believe those developers had mystical monkey mojo, but that's just a personal theory.
There are times in your life when it pays to force yourself to read from start to finish. It builds character. At least that's what I tell my son when it's chin-dents-in-your-chest boring. PFC reading shouldn't be like that. After reading the one or two line descriptions on each object and service, make yourself a list of the ones that you think you'll most likely to use (hint: if the DataWindow and DataStore aren't high on your list, you might want to drop PFC and do some Introductory PowerBuilder material) or most interested in implementing. As you'll notice from PBL Peeper (or maybe you won't, the functionality has become so naturally expected from all Windows applications), I'm in love with the Resize service, which resizes and repositions controls as the window resizes. When I implemented that, I got incredible satisfaction, as I think this is important to a user interface design. In my case, this was high on my first-to-learn list. Jump around the manuals. Keep it interesting. Keep your learning time from becoming your nap time.
"And They're Off!"
Steep learning curve, my foot!! You're off to the races, and I have four minutes to spare. I'm going to get a coffee. You sit and admire the pretty colors the publisher used.