FoxTalk. SECURITY is always an issue anytime you deploy an enterprise-wide. User-Defined. Field-Level Security. Steve Zimmelman. February

Size: px
Start display at page:

Download "FoxTalk. SECURITY is always an issue anytime you deploy an enterprise-wide. User-Defined. Field-Level Security. Steve Zimmelman. February"

Transcription

1 FoxTalk Solutions for Microsoft FoxPro and Visual FoxPro Developers User-Defined Field-Level Security Steve Zimmelman Just because your new development is all done in Visual FoxPro 6.0 doesn t mean that your legacy 2.x applications have disappeared. However, you don t want to get stuck with a lot of ongoing maintenance. In this article, Steve discusses how to add a feature in an old DOS application in a way that won t require substantial amounts of follow-up work. SECURITY is always an issue anytime you deploy an enterprise-wide application, and it can easily become one of the many black holes in application maintenance. A client recently asked me if they could have field-level security in their FoxPro 2.6 DOS application. They wanted to block users from specific fields depending on their access rights. After careful evaluation and realizing that this request could easily turn into one of those black holes I wanted to give them something they could maintain without my intervention. The application already had some basic security when users log in, they re assigned an access level from MASTER to LEVEL9. So I incorporated the access levels and used a table to store the screen, field names, and access levels that could be used to gain entry to specific fields. This way, it can store security for any object on any screen in the application. Structure for database: SECURITY.DBF Field Field Name Type Width 1 SCREEN Character 15 2 VARNAME Character 21 3 MASTER Logical 1 4 LEVEL1 Logical 1 5 LEVEL2 Logical 1 6 LEVEL3 Logical 1 7 LEVEL4 Logical 1 8 LEVEL5 Logical 1 Continues on page 3 1 User-Defined Field-Level Security Steve Zimmelman 5 Best Practices: Seeing Patterns: The Strategy Jefferey A. Donnici 10 Reusable Tools: A Last Look at the FFC Doug Hennig 14 What s Really Inside: When Does it Happen? Jim Booth 16 Creating a Class Help Generator Koriana Kent 20 Comparing VFP and SQL Server 7.0 Datatypes Michael Levy 23 February Subscriber Downloads EA EA EA EA 6.0 Applies to VFP v6.0 (Tahoe) February 1999 Volume 11, Number 2 Editorial: A Recent Compilation of Cool Tools Whil Hentzen ActiveX and Automation Review: Outlook One More Time! John V. Petersen The Kit Box: View from the Top Paul Maskens and Andy Kramek Visual Basic for Dataheads: The Basics of Writing Code Whil Hentzen Applies to VFP v5.0 Applies to VFP v3.0 Accompanying files available online at Applies specifically to one of these platforms. Applies to FoxPro v2.x

2 2 FoxTalk February

3 Field-Level Security... Continued from page 1 9 LEVEL6 Logical 1 10 LEVEL7 Logical 1 11 LEVEL8 Logical 1 12 LEVEL9 Logical 1 Here s how it works: If a user has an access level of MASTER, the application initializes a hot-key sequence to the security maintenance routine and passes with it two parameters: _CurObj, the current screen object number, and WOUTPUT(), the current screen s name: If (Upper(m.cUserLevel) = 'MASTER') On Key Label ALT+U Do F_Security ; With _CurObj, WOutPut() The security maintenance routine (F_Security) uses the value of _CurObj to find the field s name. It then looks in SECURITY.DBF for a record containing a match for the Screen and Var name. If it doesn t find one, it sets the flag m.lnew to.t. so the save routine will know whether it needs to add a record or save the changes back to the current record (see Figure 1). Procedure F_Security Parameter nobject, cscreen Push Key Clear Private narea,cvar,lnew narea = Select() If Empty(m.cScreen) cscreen = 'SCREEN' If! Used('Security') Use Security In 0 Order Security Select Security *-- Declare all field memvars private --* For I = 1 To FCount() Private (Field(i)) EndFor *-- Pad memvars to same length as fields --* cscreen = Padr(Upper(m.cScreen),Len(Security.Screen)) cvar = Padr(ObjVar(m.nObject),Len(Security.VarName)) *-- Look for corresponding record in Security.dbf. *-- If one doesn't exist, add it. * If! Seek(m.cScreen+m.cVar) Scatter Memvar Blank Screen = m.cscreen VarName = m.cvar lnew =.T. Else Scatter Memvar lnew =.F. Function SecSave *-- Called from OK button in Security.SPR --* Private lsave,s,i lsave = m.master *-- If m.master isn't checked, check for other *-- levels. If no other levels are checked, don't *-- save the record. If! m.lsave For i = 1 To 9 s = 'm.level'+str(i,1) If Eval(m.s) lsave =.T. Exit EndFor Do Case Case! m.lsave And! m.lnew *-- Nothing was checked, --* *-- reset record to be reused. --* m.screen = '' m.varname = '' Gather Memvar Case m.lsave If m.lnew If Seek(Space(15),'Security') Gather Memvar Else Insert Into Security From Memvar Else Gather Memvar EndCase Return.T. When a user goes to edit a screen, the file SECURITY.DBF is queried and any objects that don t pass security are disabled for the current user. Putting it all together The final step is to add a function to the screen s When event and pass the name of the screen to the function ScrnWhen() (see Figure 2). The function ScrnWhen() spins through every object on the screen, passing the object number and the screen name to the function FldRights(), which, in turn, verifies whether the user has the proper access level to gain entry to the field (see Figure 3). If not, then the field is disabled. Function ScrnWhen Parameter cscrn Private i Do Security.SPR Pop Key Select (m.narea) Return The screen file Security.spr calls the function SecSave() to save or discard any changes: Figure 1. MASTER users can specify what security levels are necessary to gain access to the current field. Figure 2. Add the function ScrnWhen() to the When event of the screen. FoxTalk February

4 i=1 DO WHILE (Type('ObjVar(i)')='C') If! FldRights(m.i,m.cScrn) Show Object (m.i) Disabled i=i+1 ENDDO Return.T. Function FldRights Parameter nobject,cscreen Private Rtn,cEval,lCheck,s,i,cVar Rtn =.T. * *-- Pad memvars to same length as fields --* cscreen = Padr(Upper(m.cScreen),Len(Security.Screen)) cvar = Padr(ObjVar(m.nObject),Len(Security.VarName)) * If Empty(m.cVar) Return.T. If! (Upper(m.cUserLevel) = 'MASTER') If Seek(m.cScreen+m.cVar,'Security') lcheck = Security.Master * *-- At least one level must be true * to be a valid entry. * If! m.lcheck For i = 1 To 9 s = 'Security.LEVEL'+Str(i,1) If Eval(m.s) lcheck =.T. Exit EndFor If m.lcheck ceval = 'Security.'+m.cUserLevel Rtn = Eval(m.cEval) Return (m.rtn) Figure 3. The user doesn t have access to the Name and Phone fields or the Delete button. This concept works on almost every editable screen object, with only one small caveat: If you use Button Sets, it will only recognize the first button in the set because they all use the same MemVar. So it s best to keep the buttons as individual objects with their own MemVar pointer. 02ZIMMEL.ZIP at Steve Zimmelman is a lead systems analyst for Professional Support, Inc., in Wilmington, MA, and a contributor to FoxTalk, Delphi Developer, and InfoWorld magazine. He also plays a pretty good blues guitar. skz@bicnet.net. 4 FoxTalk February

5 Best Practices Seeing Patterns: The Strategy Jefferey A. Donnici 6.0 FoxTalk This month s Best Practices column is the fourth installment in a continuing a series that discusses some common objectoriented design patterns. The primary focus of the series is to briefly describe each pattern and then demonstrate how it can be identified and implemented in Visual FoxPro applications. The design pattern discussed this month is the Strategy pattern, which allows a set of similar algorithms to be used interchangeably at runtime, as needed by the state of the system. THIS fourth installment in the Seeing Patterns series discusses the Strategy pattern. For those who are just tuning in, the purpose of this series is to discuss the real-world use of several object-oriented design patterns, using Visual FoxPro examples for illustration. As I ve mentioned in the past, I encourage you to look through the last few Best Practices columns for a brief introduction to the concepts involved with design patterns. And just to repeat myself again, make sure to have a copy of Design Patterns: Elements of Reusable Object-Oriented Software by E. Gamma, R. Helm, R. Johnson and J. Vlissides (Addison-Wesley, ISBN ) handy for this series. I know I ve mentioned and recommended this book several times, so I apologize in advance to those who have heard me repeat myself each time. I just want to make sure that new readers are able to follow along with our patternsrelated discussions. Before we jump into the Strategy pattern, remember that object-oriented design patterns aren t a languagespecific concept. While there are patterns that are specific to certain environments or types of applications, the term design patterns generally applies to patterns that can solve a design problem in any object-oriented development environment. Naturally, this series discusses patterns in a Visual FoxPro-centric fashion, but it s important to realize that these patterns are just as valuable and relevant in other languages. Again, I ve mentioned this in the past, but I want to make sure my discussion and examples here, and in previous columns, don t lead you to believe that these are the only possible implementations of a patterns-based design. Dissecting the Strategy Like some of the previous patterns mentioned in this series, the Strategy pattern has been discussed in FoxTalk in the past. In the January 1997 issue (see OOP Design Patterns: Add Design Flexibility with a Stretegy Pattern ), Steven Black introduced the Strategy pattern to FoxTalk readers, explaining that the Strategy preserves flexible behavior by decoupling a process from the one or more potential behaviors (or algorithms) that implement it. In other words, the Strategy pattern prescribes removing specific functionality from the various places in an application that use it. The idea is that there are often behaviors that occur in several places throughout a system and are only slightly different in their internal logic. By abstracting that behavior into an independent algorithm class, the various behaviors can be reused in the system without the clients that implement them requiring modification. As it turns out, the Strategy also happens to be my personal favorite of all the patterns mentioned in Design Patterns (only true geeks have favorite design patterns, right?). I think this is because a design incorporating the Strategy pattern usually includes variations on several other patterns. Most often, the Bridge pattern is used, as in the first example that follows. The Strategy pattern can be described as a type of Bridge pattern where one side of the Bridge is determined on the fly and is unknown at design time. Also, the Strategy pattern is the first pattern I felt I really understood, and it provided a solution in a design I happened to be working on while reading the book (which always helps when deciding on your favorite pattern). One example of the Strategy pattern is a set of classes that save and restore user preferences for an application. While the uses for the preference values vary, the way that the application sets and gets those values is largely the same. On the front end of this design requirement, each of these preference settings needs an interface through which the user can modify the setting. If the application has several options for specifying which font the user prefers (for a report, grid, and so forth), then applying the Strategy pattern to the design can greatly reduce the amount of code and maintenance involved. By providing a single container class that contains the user interface (the client) and then using several behavior classes (the algorithms) to deal with setting/restoring the different preference values, the Strategy pattern provides an extremely high level of reuse. FoxTalk February

6 Putting Strategy to use and reuse When considering the Strategy pattern in your design, there are some advantages and drawbacks to remember that might affect how you approach the actual design: The Strategy pattern provides for a very high level of reuse, but this reuse comes with a cost. That cost is complexity. By abstracting similar behaviors out of the components that use them, the diagram of the relationships between classes becomes significantly more complex. It s important to document the design decisions so that future maintenance and updates are made with the original design considerations in mind. The individual behavior classes that are dynamically used at runtime will often have common functionality between them. Look for similarities between these behaviors, and consider using abstract classes so that inheritance can provide added reuse in those common areas. Carefully plan the mechanism that will decide which behavior to use at runtime. While a CASE statement can be used for small component designs, a more dynamic mechanism will provide greater scalability. After all, the monolithic CASE structure is one of the things that the Strategy helps to do away with. By using a separate Strategy object to make the decision about which behavior to instantiate, the client that benefits from these behaviors can remain static as behaviors are added to, changed, and deleted from the system. Allow for a common interface among all of the behavior classes within the pattern. Because the Strategy object and/or client won t know until runtime which behavior is being used, it s important that the programmatic interface be consistent. This lets the Strategy object not worry about what to pass to the behavior class, as well as what will be returned. appear inefficient, it s another cost of using a common interface between all behaviors and dynamically choosing a behavior at runtime. Revisiting an old example The first column in this series discussed the Bridge pattern and mentioned that the Strategy pattern should be considered when a portion of the Bridge relationship can t be determined at design time. In this example, which was a part of that column, the mechanism that creates an export file uses a Strategy pattern to determine which file type to create. Figure 1 is a UML (Unified Modeling Language) diagram of the export component, with the Strategy object, cstexporter, and the individual behaviors labeled in the diagram. In this example, the cstexportinterface class uses an array property to determine which file export types are available, as well as which behavior classes provide the type-specific export mechanism. In a production environment, this array might be populated dynamically, based on a cursor or table, so that the application s metadata drives the provided functionality. By using the cstexporter abstract class, the common behavior of checking drives, directories, and files is written only once and is accessible via inheritance to the typespecific subclasses. DEFINE CLASS cstexportinterface AS Custom cfilename = "" cfiletype = "" oexportprocess =.NULL. DIMENSION aexporttypes[3,2] aexporttypes[1,1] = "XLS" aexporttypes[1,2] = "cstexcelexport" aexporttypes[2,1] = "TXT" aexporttypes[2,2] = "csttextexport" aexporttypes[3,1] = "DBF" aexporttypes[3,2] = "cstdbfexport" FUNCTION Export LPARAMETERS tcfilename, tcfiletype LOCAL llretval THIS.cFilename = UPPER(ALLTRIM( tcfilename )) Because the interface for all behavior classes is the same, there also exists the potential for overloading of the communication between the Strategy object/client and the behavior classes. Some of the information passed to the behavior classes might be unnecessary for the behavior to perform its role. While this might Figure 1. The Strategy pattern decouples the behavior classes, which create different types of export files, from the application s client components that allow exporting. 6 FoxTalk February

7 THIS.cFileType = UPPER(ALLTRIM( tcfiletype )) THIS.PreProcess() llretval = THIS.CreateExport() THIS.PostProcess() RETURN llretval ENDFUNC FUNCTION PreProcess *-- Any pre-processing of the current cursor, *-- prior to export, occurs here. ENDFUNC FUNCTION CreateExport LOCAL lncounter, llretval *-- Loop through the array of supported file *-- types to find the class to use for the *-- export type. FOR lncounter = 1 TO ; ALEN(THIS.aExportTypes, 1) IF THIS.aExportTypes[lnCounter,1] = ; THIS.cFileType THIS.oExportProcess = CREATEOBJECT( ; THIS.aExportTypes[lnCounter,2]) llretval = THIS.oExportProcess.Export( ; THIS.cFilename) ENDIF ENDFOR && lncounter = 1 TO ALEN(THIS.aExportTypes, 1) RETURN llretval ENDFUNC FUNCTION PostProcess *-- Post-processing of the current cursor, *-- prior to export, occurs here. ENDFUNC ENDDEFINE DEFINE CLASS cstexporter AS Custom cfileclause = "" cfilename = "" FUNCTION Export LPARAMETERS tcfilename THIS.cFilename = tcfilename THIS.CheckPath() THIS.CheckFile() THIS.CreateFile() RETURN FILE(tcFilename) ENDFUNC FUNCTION CheckPath *-- The purpose of this method is to verify *-- that the drive/dir indicated is valid, *-- that the user has rights to write to that *-- location, etc. Code omitted for clarity. ENDFUNC FUNCTION CheckFile *-- The purpose of this method is to verify *-- that the filename indicated is valid, *-- that it's not too long for the current *-- OS or network drive, that there are no *-- invalid characters, etc. This method *-- might also check to see whether the file *-- already exists and provide the user the *-- opportunity to replace it. Again, the *-- code for this is omitted for clarity. ENDFUNC FUNCTION CreateFile LOCAL lcfilename, lcfileclause lcfilename = UPPER(ALLTRIM(THIS.cFilename)) lcfileclause = THIS.cFileClause *-- Create the file using the clause indicated *-- by this filetype. COPY TO (lcfilename) TYPE &lcfileclause ENDFUNC ENDDEFINE DEFINE CLASS cstexcelexport AS cstexporter cfileclause = "XLS" FUNCTION CreateFile cstexporter::createfile() *-- At this point, it might be desirable to *-- make some Excel-specific changes to the *-- exported file. These might include opening *-- it via OLE Automation for formatting the *-- worksheet, changing field names for each *-- column to an English name from a data *-- dictionary, etc. ENDFUNC ENDDEFINE DEFINE CLASS csttextexport AS cstexporter cfileclause = "DELIMITED" ENDDEFINE DEFINE CLASS cstdbfexport AS cstexporter cfileclause = "FOX2X" ENDDEFINE Revisiting Codebook In the Visual FoxPro 3 Codebook, by Y. Alan Griver (published by Sybex), the accompanying framework provides a number of examples of design patterns at work. One of the features provided by the framework is the ability to read and write system-wide settings. These settings can be user preferences, data directory locations, or internal settings that determine the system s capabilities at runtime. Because VFP 3.0 could be run in both 32-bit and 16-bit environments, it wasn t possible to determine at design time whether to use Registry values or an application INI file. After all, a well-behaved 32-bit program uses the Registry for system settings, but 16-bit operating systems don t have a Registry to use. To work around this Catch-22, the application object determines which mechanism to use at runtime and instantiates the correct object accordingly. In the GetSystemSettingClass() method, properties of the application object are referenced to see whether the developer has specified one method over another. If not, then the operating system is checked to see whether it s a 32-bit system that will support Registry use. lcsystemsettingclass = "cinifile" THIS.cRegistryKey = "" THIS.cINIFile = THIS.GetINIFile() IF NOT THIS.lUseINIFile DO CASE CASE UPPER(OS()) = "WINDOWS NT" ; OR LEFT(UPPER(OS()),9) = "WINDOWS 4" lcsystemsettingclass = "cregistry" THIS.cRegistryKey = THIS.GetRegistryKey() THIS.cINIFile = "" ENDCASE ENDIF RETURN lcsystemsettingclass The return value of this method is used in the Init() event method to add the.osystemsetting member object FoxTalk February

8 to the application. After the application is instantiated, all needs to read and write system settings are handled by the application s GetSetting() and SetSetting() methods. The classes that provide the underlying functionality, cregistry and cinifile, both contain Get() and Set() methods with an identical interface. This is provided because both classes inherit from the same abstract parent class, csystemsetting. Only the mechanism-specific subclasses contain the implementation details of reading/ writing the Registry or the INI file. To read a system setting, the GetSetting() method on the application receives three parameters: the section of the system s preferences where the setting is, the name of the system setting entry to retrieve, and a default value to return if no entry is found. Those parameters are massaged slightly and then passed to the Get() method of the.osystemsetting member object on the application. If the INI file mechanism is being used, then the name of the INI file is passed as the fourth parameter. Otherwise, the fourth parameter is a logical False. Here s a portion of the code from GetSetting(): LPARAMETERS tcsection, tcentryname, tudefault *-- Some code omitted for clarity lcsection = THIS.cRegistryKey + tcsection luinifile = IIF(EMPTY(THIS.cINIFile),.F., THIS.cINIFile) RETURN THIS.oSystemSetting.Get(lcSection, tcentryname, ; ludefault, luinifile) Because the interface for the cinifile and cregistry Get() methods are the same, this method works for either mechanism. When the Registry mechanism is being used, the False value is ignored by the cregistry.get() method. When the INI file mechanism is used, it tells the cinifile class which INI file contains the application s settings. A similar mechanism is used to provide for writing system settings to either an INI file or the system Registry. In this case, both classes have a Set() method with identical interfaces. Because there isn t a need to deal with a default return value, there are only three parameters passed to the application object s SetSetting() method. These parameters are the section of the preferences to write the value into, the name of the key to write, and then the value to store to that key. Once again, the parameters are passed through to the Set() method of the.osystemsetting member object: LPARAMETERS tcsection, tcentryname, tuvalue *-- Some code omitted for clarity lcsection = THIS.cRegistryKey + tcsection luinifile = IIF(EMPTY(THIS.cINIFile),.F., THIS.cINIFile) RETURN THIS.oSystemSetting.Set(lcSection, tcentryname, ; tuvalue, luinifile) In this case, notice that there are still four parameters being passed to the Set() method of the behavior classes. That s because the INI file class still needs to know which INI file to write the value to. If the Registry mechanism is being used at runtime, the fourth parameter is again set to a logical False so that it will be ignored by the cregistry class s Set() method. While this design uses only two different types of behavior classes to handle the functionality at runtime, it could easily be extended to store preferences in a table. To do this, a new class would be created as a subclass of csystemsetting, which provides the interface, and the Get() and Set() methods would be implemented in a manner that dealt only with tables. The only change to the design then would be to provide the application object, which is serving as the Strategy object as discussed previously, with a way of determining which of the three mechanisms to use. Figure 2 shows a UML diagram of this type of design with a new, table-specific settings class outlined with a dashed line. And finally... In this, the last example of the Strategy pattern, I m using a series of different classes to determine what sort of discount to apply to a product. The discount varies according to the type of customer requesting the price Figure 2. By abstracting the pieces that save and restore application settings, the system can dynamically determine which mechanism to use. Using the csystemsetting class allows common requirements to be inherited by each mechanism. 8 FoxTalk February

9 quote. When a new product or customer type is selected from the form s combo boxes, the text boxes are updated to reflect the new price to give to the customer (see Figure 3). The form, QUOTES.SCX, along with all the other files required for this example, are included in this month s Subscriber Downloads at For each customer type listed in the CUSTTYPE.DBF table, a class has been designated to calculate that customer type s product discount. For the sake of simplicity, this table is put into an array when the form is instantiated. The layout of this table looks like this: Customer type New/Existing Customer VIP Customer Corporate Customer Non-Profit Organization Discount class cstnewdiscount cstvipdiscount cstcorpdiscount cstnpodiscount Each of the class definitions listed here is subclassed from cstabstractdiscount, which provides the programmatic interface for the subclasses. All five class definitions can be found in the QUOTES.VCX class library. When the dealer cost and retail price values are sent to the CalcDiscount() method in the discount class, it returns the discounted price that the customer receives. The discount classes also have an ndiscountfactor property, which is used differently by each class s CalcDiscount() method. The point is that the implementation of each discount Strategy in this example is very different, though the form that uses it (specifically, the form s ReCalc() method) doesn t know any of the implementation details. In fact, because the decision over which discount class to use is made on the fly based on the rows in a table, even the decision of which class to use is removed from the form s responsibilities. For this example, the discount class is instantiated only for as long as it takes to calculate the discounted price to quote to the customer. This is handled by the form s Recalc() method, which also updates the form s other controls to reflect the current price, product, and discounting information. Here are the contents of that method: LOCAL lcdiscountclass, lodiscount, lncost, ; lnmsrp, lnquote lcdiscountclass = THISFORM.GetDiscountClass() *-- If we got a class definition, use it to calculate *-- the discounted price to quote to the customer. IF NOT EMPTY(lcDiscountClass) SET CLASSLIB TO CURDIR() + "quotes.vcx" ADDITIVE lodiscount = CREATEOBJECT(lcDiscountClass) lncost = THISFORM.aProducts ; [THISFORM.cboProducts.ListIndex, 3] lnmsrp = THISFORM.aProducts ; [THISFORM.cboProducts.ListIndex, 4] lnquote = lodiscount.calcdiscount(lncost, lnmsrp) ELSE lncost = 0 lnmsrp = 0 lnquote = 0 ENDIF THISFORM.txtCost.Value = lncost THISFORM.txtMSRP.Value = lnmsrp THISFORM.txtQuote.Value = lnquote THISFORM.Refresh() Obviously, there s not a lot of fancy calculation or black magic happening here. Remember, the Strategy pattern isn t a complex pattern to see or implement in your own designs the bulk of the work in understanding the pattern or using it in a design is in how to determine which Strategy to use at runtime. So now you re thinking that the contents of that GetDiscountClass() method called previously are pretty complex, right? Here s that method in its entirety: LOCAL lcretval *-- Return the third element in the customer types *-- array - using the listindex from the combo to *-- determine the row. IF THISFORM.cboCustTypes.ListIndex > 0 lcretval = ALLTRIM(THISFORM.aCustTypes ; [THISFORM.cboCustTypes.ListIndex, 3]) ELSE lcretval = "" ENDIF RETURN lcretval Pretty simple, right? The discounting class to use for each customer type is specified in the table itself, which was queried into an array by the form. To get the correct class name, I just have to determine the current row of the Customer Type combo box and reference that row in the acusttypes array. There are, of course, a lot of things about this example that would change if it were to be used in a real application. For example, I d expect there to be a lot more error handling or trapping of exceptions in the form. You Figure 3. A different class definition is used to determine the can also imagine that the CalcDiscount() methods for each discounting mechanism for each customer type. As the customer of those discounting classes would be quite a bit more type or product changes, the discounting class is used to complex than they are here. At any rate, the purpose is to calculate the information for the form s text box controls. Continues on page 23 FoxTalk February

10 A Last Look at the FFC Reusable Tools Doug Hennig 6.0 FoxTalk This month, Doug takes a last look at the FFC, digging into classes in a couple more categories of the FoxPro Foundation Classes: Menus and Utilities. THE Menus subfolder of the Foundation Classes folder in the Visual FoxPro Catalog of the Component Gallery (from now on, I ll just refer to the subfolder without mentioning where it can be found, since they re all in the Foundation Classes folder) has two classes: Shortcut Menu Class (_ShortcutMenu in _MENU.VCX) and Navigation Shortcut Menu (_NavMenu in _TABLE2.VCX). _ShortcutMenu is an object-oriented wrapper for VFP s non-object-oriented shortcut menu system. It s one of the most useful classes in the FFC because it provides a simple way to programmatically (and therefore dynamically) create shortcut menus. The sample that demonstrates it is pretty good; you can run it from the Component Gallery by right-clicking on the class, choosing View Sample, and then Run. This class has the following methods: AddMenuBar: Adds a new bar to the menu. You can specify the prompt and optionally the code to execute when the bar is selected (if nothing is specified, the code in the conselection property is executed) or another menu object to display as a submenu, other clauses for the bar (such as a SKIP FOR clause), the location of the bar in the menu, whether the bar is marked or not, whether the bar is disabled or not, and whether the bar appears in bold or not. AddMenuSeparator: Adds a separator bar to the menu. ShowMenu: Displays the menu, executes the appropriate action for the selected bar, and then hides the menu. ClearMenu: Removes all bars from the menu. NewMenu: Instantiates another _ShortcutMenu object without having to use CREATEOBJECT() or NEWOBJECT(). This is frequently used when a menu bar has a submenu. For example, here s code from the _NavMenu class that defines a submenu (ogomenu) first, then the main menu (This.oMenu), with the submenu used as the action to execute when the first bar in the main menu is selected (gomenu is passed as the action in the AddMenuBar method): ogomenu = THIS.oMenu.NewMenu() WITH ogomenu.addmenubar(menu_top_loc,"othis.onav.gotop()").addmenubar(menu_bottom_loc,"othis.onav.gobottom()").addmenubar(menu_next_loc,"othis.onav.gonext()").addmenubar(menu_prev_loc,"othis.onav.goprevious()").addmenubar(menu_record_loc,"othis.dogoto") ENDWITH WITH THIS.oMenu.AddMenuBar(MENU_GOTO_LOC,oGoMenu).AddMenuSeparator.AddMenuBar(MENU_ADD_LOC,"oTHIS.AddRecord").AddMenuBar(MENU_DELETE_LOC,"oTHIS.DeleteRecord").AddMenuSeparator.AddMenuBar(MENU_SORT_LOC,"oTHIS.DoSort").AddMenuBar(MENU_FILTER_LOC,"oTHIS.DoFilter").AddMenuBar(MENU_FILTER2_LOC,"oTHIS.DoFilter2") ENDWITH _ShortcutMenu is pretty complete, except for one thing: You have to know the name of the array containing the menu choices (it s called amenu) and do ALEN() on it to know how many bars have been defined. Wouldn t you already know how many bars there are since you populated the menu yourself? Not necessarily; you might want to populate the menu, then allow a hooked object to add its choices to the menu. So, I subclassed _ShortCutMenu into SFShortcutMenu (in SFFFC.VCX, like the other FFC subclasses I ve created) and added an nbarcount property, with access and assign methods. The assign method has this single line of code: error 1743, 'nbarcount' This makes the property read-only. The access method has the following code: return iif(empty(this.amenu[1]) or ; isnull(this.amenu[1]), 0, alen(this.amenu, 1)) This returns 0 if there are no bars defined or the number of rows in the amenu array if there are any bars defined. Notice that this code doesn t actually assign a value to nbarcount; it always remains 0, but the access method calculates and returns a value when anything asks for it, so it s not important that the property actually contain any value. I like _ShortcutMenu (and SFShortcutMenu) so much that I implemented it in every class in SFCTRLS.VCX (our base class library). I removed the cshortcutmenuname property (which was used to hold the name of a popup 10 FoxTalk February

11 menu that would be created on the fly) and added an omenu property to contain an object reference to an SFShortcutMenu object and an luseformshortcutmenu property, which contains.t. if the menu of the form should be included in the menu for an object on that form. I also made the Destroy method set omenu to NULL to avoid dangling object references. I changed the custom ShowMenu method (which is called from RightClick); rather than creating a shortcut menu using DEFINE POPUP, it instantiates an SFShortcutMenu object, calls the custom ShortcutMenu method to populate it, calls the ShortcutMenu method of a hooked object (if there is one) to add to it, and then displays the menu if it has any bars. Here s the code: private loobject, ; lohook, ; loform with This loobject = This lohook =.ohook loform = Thisform * Define the menu if it hasn't already been defined. if vartype(.omenu) <> 'O'.oMenu = newobject('sfshortcutmenu', 'SFFFC.VCX') if vartype(.omenu) = 'O' * Populate it using a custom method..shortcutmenu(.omenu, 'loobject') * Use the hook object (if there is one) to do any * further population of the menu. if vartype(lohook) = 'O' and ; pemstatus(lohook, 'ShortcutMenu', 5) lohook.shortcutmenu(.omenu, 'lohook') endif vartype(lohook) = 'O'... * If desired, use the form's shortcut menu as well. if.luseformshortcutmenu and ; type('thisform.name') = 'C' and ; pemstatus(loform, 'ShortcutMenu', 5) loform.shortcutmenu(.omenu, 'loform') endif.luseformshortcutmenu... endif vartype(.omenu) = 'O' endif vartype(.omenu) <> 'O' * Activate the menu if necessary. if vartype(.omenu) = 'O' and.omenu.nbarcount > 0.oMenu.ShowMenu() endif vartype(.omenu) = 'O'... endwith Note the use of PRIVATE, rather than LOCAL, variables. They define references to objects in case the action for a bar is to call a method of the object. You can t do this using code like This.Method() because This isn t applicable in a menu. Instead, the object reference is put into a variable and that variable is referenced; for example, loobject.method(). The ShortcutMenu method is used to actually fill the shortcut menu object with menu bars. It accepts two parameters: an object reference to the menu object to populate and the name of the variable containing the object reference to this object. Why pass the menu reference when it s stored in the omenu property? Because we might want to hook several classes together, populating the menu object of the first one. For example, you might want to have the right-click menu for a text box include not only items for the text box, but for the form it s on as well. To do that, set the luseformshortcutmenu property of the text box to.t. The ShowMenu method of the text box will call the ShortcutMenu method of the form, passing a reference to the text box s menu object and the name of the variable the text box defined that references the form (loform). That way, the form can add its items to the text box s menu and everything will work. In most classes, ShortcutMenu is an abstract method since I couldn t think of any useful menu choices that would be used in every instance and subclass. Those that have a text component (SFComboBox, SFEditBox, SFSpinner, and SFTextBox), however, have menu bars for cut, copy, paste, clear, and select all. Here s the code from SFTextBox.ShortcutMenu: lparameters tomenu, ; tcobject with tomenu.addmenubar('cu\<t', ; "sys(1500, '_MED_CUT', '_MEDIT')").AddMenuBar('\<Copy', ; "sys(1500, '_MED_COPY', '_MEDIT')").AddMenuBar('\<Paste', ; "sys(1500, '_MED_PASTE', '_MEDIT')").AddMenuBar('Cle\<ar', ; "sys(1500, '_MED_CLEAR', '_MEDIT')").AddMenuSeparator().AddMenuBar('Se\<lect All', ; "sys(1500, '_MED_SLCTA', '_MEDIT')") endwith Other obvious choices for menu items are add, delete, save, cancel, and other methods for data entry form classes, dial this number for a phone number class, and send for an address class. _NavClass, the other class in the Menus folder, is more of a demo of how to use the _TableNav FFC class, which provides table navigation methods (like GoTop, GoNext, GoPrevious, and so forth) than a useful class on its own, mostly because it lacks the flexibility you d likely need in a real-life application. However, it might be worth looking at to get ideas for how you might create your own, more flexible version. Utilities We ve already looked at some of the classes in the Utilities folder; Application Registry, INI Access, ODBC Registry, and Registry Access were discussed in my December 1998 column (see Mining for Gold in the FFC ). Array Handler (the _ArrayLib class in _UTILITY.VCX) has three array-handling methods, which are very useful if you don t already have routines that perform these functions. AColScan looks for a value in a particular column of an array (as opposed to ASCAN, FoxTalk February

12 which will look in every element of the array) and can return the row number it was found in (as opposed to ASCAN, which returns the element number on which you almost always have to then use ASUBSCRIPT to get the row number). InsAItem inserts an element into an array and sets that element to the specified value; in the case of a two-dimensional array, it adds an entire row and can set either the first element or each element in the array to the specified value. DelAItem deletes an element (an entire row in the case of a two-dimensional array) from an array. File Version (_FileVerson in _UTILITY.VCX) is a wrapper for the VFP 6 AGETFILEVERSION() function, which puts information about a file (such as copyright and version information) into an array. Its GetVersion method ensures that the specified file exists and puts the results of AGETFILEVERSION() into its aversion property. DisplayVersion() displays the version information using MESSAGEBOX(). This class is somewhat useful, but I think I ll just use AGETFILEVERSION() directly when I need it. Find Files (_Filer in _UTILITY.VCX) is a wrapper class for FILER.DLL, a cool text search tool found in the TOOLS\FILER subdirectory of the VFP home directory. Although you can use FILER.DLL directly, _Filer provides an instantiation wrapper for this COM object: If it isn t registered on the system, it offers to register it for you (assuming the DLL can be found, of course). It has properties that map to the COM object s properties; for example, _Filer.cFileExpression maps to the FileExpression property of the COM object. It can automatically handle empty properties in some cases; for example, if cfileexpression is blank, it puts *.* into the FileExpression property of the COM object, and if csearchpath is empty, it can optionally display a dialog box asking for the directory to search. It has one method you ll use Find which returns the number of files it found and puts an object reference to the Files collection of the COM object into its ofiles property. One major drawback to this class (and FILER.DLL): Microsoft doesn t allow us to distribute the DLL, so you can t use it to provide text search capabilities in your applications; you can only use it to create tools for yourself or other VFP developers. Messagebox Handler (_MsgBox in _DIALOGS.VCX) is a wrapper for the MESSAGEBOX() function. It has cmessage, ntype, and ctitle properties that map to the parameters for MESSAGEBOX(), so, once set, you don t have to specify them each time (although you re likely to change cmessage every time you use this class). It also has an lbeep property that determines whether the class should beep as the dialog box is displayed. The Init method can accept message, type, title, and beep parameters and stores them in the applicable property. You can call the Display method to display the message box using the current values of these properties, or Show to specify any of these values (for example, once ntype, ctitle, and lbeep have been set, you could call Show( my message ) to display the specific message you want). This class is useful, although I built one a long time ago that I ll continue to use. Type Library (_TypeLib in _UTILITY.VCX) is a wrapper for the FoxTLib ActiveX control; it gets the type library information for COM servers. This might be useful if you need to create a developer tool with this capability (although the Class Browser can also display type library information), but I can t see using it in a real-world application. String Library (_StringLib in _UTILITY.VCX) sure sounds promising, doesn t it? However, it s probably the lamest class in the FFC; it has a single method, TrimCRLF, that strips carriage returns and line feeds from the end of the string (gee, I wonder why they didn t create a sample form for this class <g>). I can think of a lot of other functions that could have gone into this class; this is one I ve used so infrequently over the years, I can t imagine why you d build a class just for it. If you need this function, I think you d be better off creating your own string library and stealing the code from this method, and skipping the class altogether. Shell Execute (_ShellExecute in _ENVIRON.VCX) is a great little class. It provides a wrapper for the ShellExecute Windows API function, which executes a file, so you don t have to worry about how to DECLARE or call it. The neat thing about this function (and the class) is that it handles file types registered on your system; passing the filename to ShellExecute will run the associated application if the file isn t an EXE. For example, executing MYDOC.HTML will bring up the default browser for your system and display this file. You don t have to know which application is required or where it s installed on the system. _ShellExecute just has one method, ShellExecute, which accepts up to three parameters: the name of the file to execute, the working directory to set for the application (if it isn t specified, the application s directory is the working directory), and an operation string ( open is used if it isn t specified). This method returns a value indicating success or what type of error occurred (see the Help topic for this class for a list of values). Incidentally, _ENVIRON.VCX contains a couple of other classes, neither of which appear in the Component Gallery. _Set stores the current value for a SET setting (such as TALK, PRINT, NEAR, and so on) and sets it to another value. When the object is destroyed, it automatically restores the setting (although this behavior can be disabled if desired). Using this class saves you from having to use code like the following: lccurrexact = set('exact') set exact off * do something here if lccurrexact = 'ON' set exact on endif lccurrexact = 'ON' 12 FoxTalk February

13 This code would be much worse if you had multiple exit points for the routine; you d have to reset the EXACT setting in each place (yet another reason to avoid multiple exits in a routine). Instead, you d just do the following: loexact = newobject('_set', '_ENVIRON.VCX', '', ; 'EXACT', 'OFF') * do something here When this code ends, loexact goes out of scope, and EXACT is set back to its former setting. _RunCode, the other class in _ENVIRON.VCX, is a code block interpreter. As you know, you can make VFP execute a single line of code at runtime by storing it into a variable and then macro expanding the variable; this allows you to determine what code should execute at runtime rather than design time. However, this technique doesn t work if the code is several lines long (for example, some script code stored in a memo field in a table that should be executed when a record is selected). While you could split the code into individual lines and macro expand each one, this won t work if the code contains program structures such as loops. The _RunCode class has a RunCode method that can execute a block of code. You pass the code as a string or the name of a text file containing the code as the first parameter,.t. for the second parameter if the first parameter is a filename, and.t. if you want errors to be ignored. One use for _RunCode would be to allow users of your application to script certain behaviors (not a typical end-user, but perhaps an administrator or another developer who doesn t have access to the source code). To do this, you d provide a hook or add-in mechanism for the functions whose behavior the user can change. For example, say you allow the user to define add-ins for the add record method of a form. That method might look like this: In this case, entry-level users would get a wizard that leads them through what information is captured when a record is added; the normal add operation is used for everyone else. The add-in code might be stored in a file (in which case the add-in registry would contain the name of the file) or in a memo field of an add-ins table (which would require providing a means for the user to edit the contents of the memo). Conclusion This was our last look at the classes in the FFC. Over the past three columns, we actually looked at fewer than half of the classes available, but it s time to move on to other things. Hopefully, I ve inspired you enough to dig into the FFC and see which classes you ll want to use. 02DHENSC.ZIP at Doug Hennig is a partner with Stonefield Systems Group Inc. in Regina, Saskatchewan, Canada. He is the author of Stonefield s add-on tools for FoxPro developers, including Stonefield Database Toolkit and Stonefield Query. He is also the author of The Visual FoxPro Data Dictionary in Pinnacle Publishing s The Pros Talk Visual FoxPro series. Doug has spoken at the 1997 and 1998 Microsoft FoxPro Developers Conferences (DevCon) as well as user groups and regional conferences all over North America. He is a Microsoft Most Valuable Professional (MVP) @compuserve.com, dhennig@stonefield.com. if This.oAddinManager.Execute('AddRecord') return else append blank This.Refresh() endif This.oAddinManager.Execute('AddRecord') In this scenario, the form has an add-in manager that, when passed a method name, checks an add-ins registry (it could be a table or the Windows Registry) to see whether an add-in has been defined for this method, and if so, executes it and returns.t. so the calling routine knows the add-in executed. The add-in might be code written by the administrator that does something like this: if ouser.nuserlevel = 1 do form ADDWIZARD return.t. else return.f. endif ouser.nuserlevel = 1 FoxTalk February

14 When Does it Happen? What s Really Inside FoxTalk Jim Booth 6.0 In developing applications in Visual FoxPro, we re always depending on events being processed. Every keystroke and mouse click is critically important to the proper functioning of the application. How much do you actually know about when things happen in VFP? This month, Jim will examine some of the important events and find out exactly when they occur. BEFORE I venture into the events that fire during various operations in VFP, let s classify the events and methods that are available in the product. My preference is to refer to these code locations as eventmethods and methods. The difference between an eventmethod and a simple method is how it s fired. A simple method only fires if a line of code has called it. It might have default behavior that comes from VFP, or it might simply be a place where you can write code and then call that code when needed. An event-method is automatically fired based on some event occurring. For example, the KeyPress eventmethod of a control will automatically fire whenever the user presses a key while focus is in that control. It s important to differentiate between the event (pressing a key) and the event-method (KeyPress) that fires as a result of the event occurring. Calling an event-method in program code does not cause the associated event to occur. It simply runs the code in that event-method. Getting and losing focus There are a number of event-methods and methods associated with a control receiving and/or losing focus. These are When (event-method), GotFocus (eventmethod), SetFocus (method), Valid (event-method), and LostFocus (event-method). To take the best advantage of the event model in VFP, it s necessary to clearly understand the event-methods that fire and the point in time at which that they fire. In the process of receiving focus, the sequence of eventmethods is 1 When and then 2 GotFocus. The When event-method can be thought of as the place to determine whether or not focus will be allowed to arrive at the control. Returning a value of.f. from the When event-method will prevent focus from ever arriving at the control. Once the When event-method returns a value of.t., the GotFocus will fire (if the When returns.f., the GotFocus doesn t fire). The GotFocus can be classified as the event-method in which you prepare for the control to receive focus, since it only fires if the control is going to receive focus. The SetFocus method also plays a role in that it has the default behavior of beginning the process of receiving focus for a control. A control s SetFocus method is similar to the FoxPro 2.X _curobj variable. There are two controls that act slightly different in respect to the When event-method the ListBox and the ComboBox. With these two controls, the When fires as it does for other controls, as focus arrives at the object and before the object actually receives focus. The When can return.f. to prevent focus from arriving on that object. However, with lists and combos, the When also fires every time the currently selected item changes. Because of this behavior, it s important to be careful with what code you put in a list or combo s When event-method. You should be aware that the When will fire multiple times for these two controls as the user navigates through the list. The process of losing focus is similar to that of receiving focus. There are two event-methods involved Valid and LostFocus. The Valid fires first and can be used to prevent focus from leaving the control. A return of.f. from the Valid will cause the control to retain the focus. A return of.t. from the Valid will allow focus to leave the control and thus it will fire the LostFocus. Like the When and GotFocus, the Valid can be used to decide whether focus should be allowed to leave the control, and the LostFocus can be used to react to a control losing focus. Updating data VFP is a data management tool, and as such it has some very powerful and easy-to-use data-binding capabilities. Many of the controls in VFP are able to be bound to a data source by setting the value of the control s ControlSource property. Using the ControlSource property causes a control to automatically update the ControlSource when the value of the control is changed. However, like anything else in life, the updating of the ControlSource takes place at a specific point in time. Knowing exactly when the update occurs is critical to getting the most flexibility out of the controls in VFP. A number of questions have arisen regarding what some folks want to classify as bugs related to the updating of a ControlSource. One such situation was for a developer who was issuing a THISFORM.Refresh() in the InteractiveChange event of a text box and was complaining because the text box kept reverting to the original value of the ControlSource. Others have found 14 FoxTalk February

Splitting Up is Hard to Do Doug Hennig

Splitting Up is Hard to Do Doug Hennig Splitting Up is Hard to Do Doug Hennig While they aren t used everywhere, splitter controls can add a professional look to your applications when you have left/right or top/bottom panes in a window. This

More information

Manage Your Applications Doug Hennig

Manage Your Applications Doug Hennig Manage Your Applications Doug Hennig This month s article presents a simple yet useful tool, and discusses several reusable techniques used in this tool. If you re like me, your hard drive (or your server

More information

May I See Your License? Doug Hennig

May I See Your License? Doug Hennig May I See Your License? Doug Hennig If you develop commercial applications, you may want to provide multiple levels of your application depending on what your users paid for, and prevent unauthorized users

More information

Mining for Gold in the FFC

Mining for Gold in the FFC Mining for Gold in the FFC Doug Hennig Introduction One of the design goals for VFP 6 was to make it easier for programmers new to VFP to get up and running with the tool. The FoxPro Foundation Classes,

More information

Taking Control Doug Hennig

Taking Control Doug Hennig Taking Control Doug Hennig This month, Doug Hennig discusses a simple way to make anchoring work the way you expect it to and how to control the appearance and behavior of a report preview window. There

More information

The Mother of All TreeViews, Part 2 Doug Hennig

The Mother of All TreeViews, Part 2 Doug Hennig The Mother of All TreeViews, Part 2 Doug Hennig Last month, Doug presented a reusable class that encapsulates most of the desired behavior for a TreeView control. He discussed controlling the appearance

More information

uilding Your Own Builders with BuilderB Doug Hennig and Yuanitta Morhart

uilding Your Own Builders with BuilderB Doug Hennig and Yuanitta Morhart uilding Your Own Builders with BuilderB Doug Hennig and Yuanitta Morhart Builders make it easy to set properties for objects at design time, which is especially handy for containers which you normally

More information

A New Beginning Doug Hennig

A New Beginning Doug Hennig A New Beginning Doug Hennig The development world is moving to reusable components. As goes the world, so goes Doug s column. We ll start off the new Best Tools column with SFThermometer, a handy progress

More information

IntelliSense at Runtime Doug Hennig

IntelliSense at Runtime Doug Hennig IntelliSense at Runtime Doug Hennig VFP 9 provides support for IntelliSense at runtime. This month, Doug Hennig examines why this is useful, discusses how to implement it, and extends Favorites for IntelliSense

More information

Data-Drive Your Applications Doug Hennig

Data-Drive Your Applications Doug Hennig Data-Drive Your Applications Doug Hennig As VFP developers, we re used to storing application data in tables. However, another use for tables is to store information about application behavior. This month

More information

Extending the VFP 9 IDE Doug Hennig

Extending the VFP 9 IDE Doug Hennig Extending the VFP 9 IDE Doug Hennig One of the key themes in VFP 9 is extensibility. You can extend the VFP 9 Report Designer through report events and the reporting engine through the new ReportListener

More information

Christmas Stocking Stuffers Doug Hennig

Christmas Stocking Stuffers Doug Hennig Christmas Stocking Stuffers Doug Hennig Visual FoxPro has a lot more places to put code than FoxPro 2.x. This month s column examines the advantages and disadvantages of creating classes for library routines.

More information

A File Open Dialog Doug Hennig

A File Open Dialog Doug Hennig A File Open Dialog Doug Hennig A File Open dialog is a better approach than having a long list of forms appear in the File menu. This article presents a reusable File Open dialog that supports grouping

More information

A New IDE Add-on: FoxTabs Doug Hennig

A New IDE Add-on: FoxTabs Doug Hennig A New IDE Add-on: FoxTabs Doug Hennig FoxTabs provides easy access to all open windows in your VFP IDE. However, not only is it a great tool, it uses some very cool techniques to do its magic, including

More information

Zip it, Zip it Good Doug Hennig

Zip it, Zip it Good Doug Hennig Zip it, Zip it Good Doug Hennig This month s article presents a class to zip and pack the files in a project, and a class to interface VFP with a zipping utility like WinZip. Combining these classes with

More information

CATCH Me if You Can Doug Hennig

CATCH Me if You Can Doug Hennig CATCH Me if You Can Doug Hennig VFP 8 has structured error handling, featuring the new TRY... CATCH... FINALLY... ENDTRY structure. This powerful new feature provides a third layer of error handling and

More information

FoxTalk. GOING through the classes provided in the FoxPro. More Gems in the FFC. Doug Hennig 6.0

FoxTalk. GOING through the classes provided in the FoxPro. More Gems in the FFC. Doug Hennig 6.0 FoxTalk Solutions for Microsoft FoxPro and Visual FoxPro Developers This is an exclusive supplement for FoxTalk subscribers. For more information about FoxTalk, call us at 1-800-788-1900 or visit our Web

More information

A Generic Import Utility, Part 2

A Generic Import Utility, Part 2 A Generic Import Utility, Part 2 Doug Hennig Part 1 of this two-part series presented a set of classes making up a generic import utility you can add to your applications to provide import capabilities

More information

Web Page Components Doug Hennig

Web Page Components Doug Hennig Web Page Components Doug Hennig With its fast and powerful string functions, VFP is a great tool for generating HTML. This month, Doug Hennig shows how you can treat Web pages as a collection of reusable

More information

Base Classes Revisited Doug Hennig

Base Classes Revisited Doug Hennig Base Classes Revisited Doug Hennig Most VFP developers know you should never use the VFP base classes, but instead create your own set of base classes. It s time to blow the dust off the set of base classes

More information

Role-Based Security, Part III Doug Hennig

Role-Based Security, Part III Doug Hennig Role-Based Security, Part III Doug Hennig This month, Doug Hennig winds up his series on role-based security by examining a form class responsible for maintaining users and roles. In my previous two articles,

More information

I Got Rendered Where? Part II Doug Hennig

I Got Rendered Where? Part II Doug Hennig I Got Rendered Where? Part II Doug Hennig Thanks to the new ReportListener in VFP 9, we have a lot more control over where reports are rendered. Last month, Doug Hennig showed a listener that collaborates

More information

pdating an Application over the Internet, Part II Doug Hennig

pdating an Application over the Internet, Part II Doug Hennig pdating an Application over the Internet, Part II Doug Hennig This second of a two-part series looks at several strategies for automating the process of distributing application updates by downloading

More information

Data Handling Issues, Part I Doug Hennig

Data Handling Issues, Part I Doug Hennig Data Handling Issues, Part I Doug Hennig The ability to handle multiple sets of data is a frequent requirement in business applications. So is the management of primary key values for tables. In this first

More information

Putting Parameters in Perspective Doug Hennig

Putting Parameters in Perspective Doug Hennig Putting Parameters in Perspective Doug Hennig Concentrating on communication between routines can reduce up to one-third of the errors in your applications. This month s column examines some Best Practices

More information

Session V-STON Stonefield Query: The Next Generation of Reporting

Session V-STON Stonefield Query: The Next Generation of Reporting Session V-STON Stonefield Query: The Next Generation of Reporting Doug Hennig Overview Are you being inundated with requests from the users of your applications to create new reports or tweak existing

More information

## Version: FoxPro 7.0 ## Figures: ## File for Subscriber Downloads: Publishing Your First Web Service Whil Hentzen

## Version: FoxPro 7.0 ## Figures: ## File for Subscriber Downloads: Publishing Your First Web Service Whil Hentzen ## Version: FoxPro 7.0 ## Figures: ## File for Subscriber Downloads: Publishing Your First Web Service Whil Hentzen Web Services The Buzzword of the 02s! It s nothing really new, however, any more than

More information

Try Thor s Terrific Tools, Part 2

Try Thor s Terrific Tools, Part 2 Try Thor s Terrific Tools, Part 2 Thor offers lots of tools for working with classes and forms. Learning to use them can make you more productive. Tamar E. Granor, Ph.D. In my last article, I showed a

More information

The Best of Both Worlds

The Best of Both Worlds The Best of Both Worlds Doug Hennig You don t have to sacrifice performance for ease of installing new versions of applications. Using STARTAPP, you can run applications from local drives for best performance,

More information

The Ultimate Grid. Visual FoxPro 6 DORON FARBER. Structure of the Solution. Design Issues

The Ultimate Grid. Visual FoxPro 6 DORON FARBER. Structure of the Solution. Design Issues t o o l s Visual FoxPro 6 a u t e u r DORON FARBER The Ultimate Grid In a project I inherited from another developer,i needed a way for a user to look up a record from another table or data source. A ComboBox

More information

This chapter is intended to take you through the basic steps of using the Visual Basic

This chapter is intended to take you through the basic steps of using the Visual Basic CHAPTER 1 The Basics This chapter is intended to take you through the basic steps of using the Visual Basic Editor window and writing a simple piece of VBA code. It will show you how to use the Visual

More information

Speed in Object Creation and. Destruction. March 2016 Number 49. Tamar E. Granor, Ph.D.

Speed in Object Creation and. Destruction. March 2016 Number 49. Tamar E. Granor, Ph.D. Speed in Object Creation and Destruction Does the approach you choose for creating and destroying objects have an impact on performance? Tamar E. Granor, Ph.D. March 2016 Number 49 1 Know How... Speed

More information

Report Objects Doug Hennig

Report Objects Doug Hennig Report Objects Doug Hennig FoxPro developers have wanted an object-oriented report writer since VFP 3 was released. Although the classes presented in this article don t have a pretty visual designer tool,

More information

More Flexible Reporting With XFRX Doug Hennig

More Flexible Reporting With XFRX Doug Hennig More Flexible Reporting With XFRX Doug Hennig XFRX can make your reporting solutions more flexible since it allows you to output FRX reports to PDF, Microsoft Word, Microsoft Excel, and HTML files. This

More information

Chapter 18 Outputting Data

Chapter 18 Outputting Data Chapter 18: Outputting Data 231 Chapter 18 Outputting Data The main purpose of most business applications is to collect data and produce information. The most common way of returning the information is

More information

Outlook Web Access. In the next step, enter your address and password to gain access to your Outlook Web Access account.

Outlook Web Access. In the next step, enter your  address and password to gain access to your Outlook Web Access account. Outlook Web Access To access your mail, open Internet Explorer and type in the address http://www.scs.sk.ca/exchange as seen below. (Other browsers will work but there is some loss of functionality) In

More information

XP: Backup Your Important Files for Safety

XP: Backup Your Important Files for Safety XP: Backup Your Important Files for Safety X 380 / 1 Protect Your Personal Files Against Accidental Loss with XP s Backup Wizard Your computer contains a great many important files, but when it comes to

More information

Robert Ragan s TOP 3

Robert Ragan s TOP 3 Robert Ragan s TOP 3 Internet Genealogy Research POWER TECHNIQUES that Have Stunned Audiences POWER TECHNIQUES TWO: Robert s Unique "Gather, Store and Quick Find Method." You'll have to see it to believe

More information

NCMail: Microsoft Outlook User s Guide

NCMail: Microsoft Outlook User s Guide NCMail: Microsoft Outlook 2003 Email User s Guide Revision 1.0 11/10/2007 This document covers how to use Microsoft Outlook 2003 for accessing your email with the NCMail Exchange email system. The syntax

More information

A File Open Dialog Box Doug Hennig

A File Open Dialog Box Doug Hennig Seite 1 von 7 Issue Date: FoxTalk November 1997 A File Open Dialog Box Doug Hennig dhennig@stonefield.com A File Open dialog box is a superior alternative to having a long list of forms appear in the File

More information

This is a book about using Visual Basic for Applications (VBA), which is a

This is a book about using Visual Basic for Applications (VBA), which is a 01b_574116 ch01.qxd 7/27/04 9:04 PM Page 9 Chapter 1 Where VBA Fits In In This Chapter Describing Access Discovering VBA Seeing where VBA lurks Understanding how VBA works This is a book about using Visual

More information

Making the Most of the Toolbox

Making the Most of the Toolbox Making the Most of the Toolbox Session 15 Tamar E. Granor, Ph.D. Tomorrow s Solutions, LLC 8201 Cedar Road Elkins Park, PA 19027 Phone: 215-635-1958 Email: tamar@tomorrowssolutionsllc.com Web: www.tomorrowssolutionsllc.com

More information

Taking Advantage of Idle Cycles. Make Your Application Work When the User Isn't. The Idea. The Strategy. December, 2003

Taking Advantage of Idle Cycles. Make Your Application Work When the User Isn't. The Idea. The Strategy. December, 2003 December, 2003 Taking Advantage of Idle Cycles Make Your Application Work When the User Isn't by Tamar E. Granor, Technical Editor A couple of years ago at a conference, a fellow asked me if there was

More information

Microsoft Access Database How to Import/Link Data

Microsoft Access Database How to Import/Link Data Microsoft Access Database How to Import/Link Data Firstly, I would like to thank you for your interest in this Access database ebook guide; a useful reference guide on how to import/link data into an Access

More information

Sisulizer Three simple steps to localize

Sisulizer Three simple steps to localize About this manual Sisulizer Three simple steps to localize Copyright 2006 Sisulizer Ltd. & Co KG Content changes reserved. All rights reserved, especially the permission to copy, distribute and translate

More information

Part I: Programming Access Applications. Chapter 1: Overview of Programming for Access. Chapter 2: Extending Applications Using the Windows API

Part I: Programming Access Applications. Chapter 1: Overview of Programming for Access. Chapter 2: Extending Applications Using the Windows API 74029c01.qxd:WroxPro 9/27/07 1:43 PM Page 1 Part I: Programming Access Applications Chapter 1: Overview of Programming for Access Chapter 2: Extending Applications Using the Windows API Chapter 3: Programming

More information

Data Handling Issues, Part II Doug Hennig

Data Handling Issues, Part II Doug Hennig Data Handling Issues, Part II Doug Hennig While field and table validation rules protect your tables from invalid data, they also make data entry forms harder to use. In this second of a two-part article,

More information

Multi-User and Data Buffering Issues

Multi-User and Data Buffering Issues Multi-User and Data Buffering Issues Doug Hennig Partner Stonefield Systems Group Inc. 1112 Winnipeg Street, Suite 200 Regina, SK Canada S4R 1J6 Phone: (306) 586-3341 Fax: (306) 586-5080 Email: dhennig@stonefield.com

More information

Advisor Answers. January, Visual FoxPro 3.0 and 5.0

Advisor Answers. January, Visual FoxPro 3.0 and 5.0 January, 1998 Advisor Answers Visual FoxPro 3.0 and 5.0 Q: I would like to create a combo box that functions exactly like the FoxPro help index, that is, when the user types in a value, that value is automatically

More information

Chapter 2 The SAS Environment

Chapter 2 The SAS Environment Chapter 2 The SAS Environment Abstract In this chapter, we begin to become familiar with the basic SAS working environment. We introduce the basic 3-screen layout, how to navigate the SAS Explorer window,

More information

A Document Created By Lisa Diner Table of Contents Western Quebec School Board October, 2007

A Document Created By Lisa Diner Table of Contents Western Quebec School Board October, 2007 Table of Contents A Document Created By Lisa Diner Western Quebec School Board October, 2007 Table of Contents Some Basics... 3 Login Instructions... 4 To change your password... 6 Options As You Login...

More information

Advanced Uses for Dynamic Form

Advanced Uses for Dynamic Form Advanced Uses for Dynamic Form Doug Hennig Dynamic Form is an under-used project in VFPX. Its ability to create forms quickly and dynamically isn t something every developer needs but if you need it, Dynamic

More information

Give users a control that makes entering dates as easy as it is in Intuit Quicken.

Give users a control that makes entering dates as easy as it is in Intuit Quicken. April, 2005 Visual FoxPro 9/8/7 Easier Date Entry Give users a control that makes entering dates as easy as it is in Intuit Quicken. By Tamar E. Granor, technical editor As I've written previously, I think

More information

Windows Event Binding Made Easy Doug Hennig

Windows Event Binding Made Easy Doug Hennig Windows Event Binding Made Easy Doug Hennig A feature available in other development environments but missing in VFP is the ability to capture Windows events. VFP 9 extends the BINDEVENT() function to

More information

Working with the Registry. The Registry class makes it easy. The Registry Structure. January, By Tamar E. Granor

Working with the Registry. The Registry class makes it easy. The Registry Structure. January, By Tamar E. Granor January, 2002 Working with the Registry The Registry class makes it easy By Tamar E. Granor The Windows Registry is full of information about the user, his or her settings, the installed software and the

More information

Arduino IDE Friday, 26 October 2018

Arduino IDE Friday, 26 October 2018 Arduino IDE Friday, 26 October 2018 12:38 PM Looking Under The Hood Of The Arduino IDE FIND THE ARDUINO IDE DOWNLOAD First, jump on the internet with your favorite browser, and navigate to www.arduino.cc.

More information

Much ADO About Something Doug Hennig

Much ADO About Something Doug Hennig Much ADO About Something Doug Hennig The release of the OLE DB provider for VFP means better performance and scalability for applications that need to access VFP data via ADO. However, there are some interesting

More information

CheckBook Pro 2 Help

CheckBook Pro 2 Help Get started with CheckBook Pro 9 Introduction 9 Create your Accounts document 10 Name your first Account 11 Your Starting Balance 12 Currency 13 We're not done yet! 14 AutoCompletion 15 Descriptions 16

More information

MAPLOGIC CORPORATION. GIS Software Solutions. Getting Started. With MapLogic Layout Manager

MAPLOGIC CORPORATION. GIS Software Solutions. Getting Started. With MapLogic Layout Manager MAPLOGIC CORPORATION GIS Software Solutions Getting Started With MapLogic Layout Manager Getting Started with MapLogic Layout Manager 2008 MapLogic Corporation All Rights Reserved 330 West Canton Ave.,

More information

anguage Enhancements in VFP 7, Part I Doug Hennig

anguage Enhancements in VFP 7, Part I Doug Hennig anguage Enhancements in VFP 7, Part I Doug Hennig Want an early peek at what s new in VFP 7? This is the first article in a series discussing the language enhancements Microsoft is implementing and how

More information

COSC 2P91. Bringing it all together... Week 4b. Brock University. Brock University (Week 4b) Bringing it all together... 1 / 22

COSC 2P91. Bringing it all together... Week 4b. Brock University. Brock University (Week 4b) Bringing it all together... 1 / 22 COSC 2P91 Bringing it all together... Week 4b Brock University Brock University (Week 4b) Bringing it all together... 1 / 22 A note on practicality and program design... Writing a single, monolithic source

More information

Taskbar: Working with Several Windows at Once

Taskbar: Working with Several Windows at Once Taskbar: Working with Several Windows at Once Your Best Friend at the Bottom of the Screen How to Make the Most of Your Taskbar The taskbar is the wide bar that stretches across the bottom of your screen,

More information

Clean & Speed Up Windows with AWO

Clean & Speed Up Windows with AWO Clean & Speed Up Windows with AWO C 400 / 1 Manage Windows with this Powerful Collection of System Tools Every version of Windows comes with at least a few programs for managing different aspects of your

More information

1: Introduction to Object (1)

1: Introduction to Object (1) 1: Introduction to Object (1) 김동원 2003.01.20 Overview (1) The progress of abstraction Smalltalk Class & Object Interface The hidden implementation Reusing the implementation Inheritance: Reusing the interface

More information

Taking Advantage of ADSI

Taking Advantage of ADSI Taking Advantage of ADSI Active Directory Service Interfaces (ADSI), is a COM-based set of interfaces that allow you to interact and manipulate directory service interfaces. OK, now in English that means

More information

CHAPTER 1 COPYRIGHTED MATERIAL. Getting to Know AutoCAD. Opening a new drawing. Getting familiar with the AutoCAD and AutoCAD LT Graphics windows

CHAPTER 1 COPYRIGHTED MATERIAL. Getting to Know AutoCAD. Opening a new drawing. Getting familiar with the AutoCAD and AutoCAD LT Graphics windows CHAPTER 1 Getting to Know AutoCAD Opening a new drawing Getting familiar with the AutoCAD and AutoCAD LT Graphics windows Modifying the display Displaying and arranging toolbars COPYRIGHTED MATERIAL 2

More information

As a programmer, you know how easy it can be to get lost in the details

As a programmer, you know how easy it can be to get lost in the details Chapter 1 Congratulations, Your Problem Has Already Been Solved In This Chapter Introducing design patterns Knowing how design patterns can help Extending object-oriented programming Taking a look at some

More information

Intro. Scheme Basics. scm> 5 5. scm>

Intro. Scheme Basics. scm> 5 5. scm> Intro Let s take some time to talk about LISP. It stands for LISt Processing a way of coding using only lists! It sounds pretty radical, and it is. There are lots of cool things to know about LISP; if

More information

This book is about using Visual Basic for Applications (VBA), which is a

This book is about using Visual Basic for Applications (VBA), which is a In This Chapter Describing Access Discovering VBA Seeing where VBA lurks Understanding how VBA works Chapter 1 Where VBA Fits In This book is about using Visual Basic for Applications (VBA), which is a

More information

It Might Be Valid, But It's Still Wrong Paul Maskens and Andy Kramek

It Might Be Valid, But It's Still Wrong Paul Maskens and Andy Kramek Seite 1 von 5 Issue Date: FoxTalk July 2000 It Might Be Valid, But It's Still Wrong Paul Maskens and Andy Kramek This month, Paul Maskens and Andy Kramek discuss the problems of validating data entry.

More information

Advisor Discovery. Use BindEvent() to keep things in synch. BindEvent() Refresher. June, By Tamar E. Granor, technical editor

Advisor Discovery. Use BindEvent() to keep things in synch. BindEvent() Refresher. June, By Tamar E. Granor, technical editor June, 2006 Advisor Discovery Use BindEvent() to keep things in synch By Tamar E. Granor, technical editor I've been experimenting with BindEvent() since it was added in VFP 8; I've even written about it

More information

Oracle Cloud. Content and Experience Cloud ios Mobile Help E

Oracle Cloud. Content and Experience Cloud ios Mobile Help E Oracle Cloud Content and Experience Cloud ios Mobile Help E82090-01 February 2017 Oracle Cloud Content and Experience Cloud ios Mobile Help, E82090-01 Copyright 2017, 2017, Oracle and/or its affiliates.

More information

Centura is Dynamic Gianluca Pivato F

Centura is Dynamic Gianluca Pivato F Pro Centura TM Visit us at www.propublishing.com! Hot Ideas for Centura Developers The Secret s Out: Centura is Dynamic Gianluca Pivato F or years developers have had to find creative ways to overcome

More information

Part 1: Understanding Windows XP Basics

Part 1: Understanding Windows XP Basics 542362 Ch01.qxd 9/18/03 9:54 PM Page 1 Part 1: Understanding Windows XP Basics 1: Starting Up and Logging In 2: Logging Off and Shutting Down 3: Activating Windows 4: Enabling Fast Switching between Users

More information

In this chapter, I m going to show you how to create a working

In this chapter, I m going to show you how to create a working Codeless Database Programming In this chapter, I m going to show you how to create a working Visual Basic database program without writing a single line of code. I ll use the ADO Data Control and some

More information

Financial Statements Using Crystal Reports

Financial Statements Using Crystal Reports Sessions 6-7 & 6-8 Friday, October 13, 2017 8:30 am 1:00 pm Room 616B Sessions 6-7 & 6-8 Financial Statements Using Crystal Reports Presented By: David Hardy Progressive Reports Original Author(s): David

More information

Integrating Visual FoxPro and MailChimp

Integrating Visual FoxPro and MailChimp Integrating Visual FoxPro and MailChimp Whil Hentzen We've all written our own email applications. I finally decided to use an outside service to handle my emailing needs. Here's how I used VFP to integrate

More information

10 Tips For Effective Content

10 Tips For Effective  Content 10 Tips For Effective Email Content Nowadays when it comes to online marketing, and the Internet as a whole, so many people are being added to so many email lists. They're being bombarded constantly by

More information

Furl Furled Furling. Social on-line book marking for the masses. Jim Wenzloff Blog:

Furl Furled Furling. Social on-line book marking for the masses. Jim Wenzloff Blog: Furl Furled Furling Social on-line book marking for the masses. Jim Wenzloff jwenzloff@misd.net Blog: http://www.visitmyclass.com/blog/wenzloff February 7, 2005 This work is licensed under a Creative Commons

More information

SharePoint 2010 Site Owner s Manual by Yvonne M. Harryman

SharePoint 2010 Site Owner s Manual by Yvonne M. Harryman SharePoint 2010 Site Owner s Manual by Yvonne M. Harryman Chapter 9 Copyright 2012 Manning Publications Brief contents PART 1 GETTING STARTED WITH SHAREPOINT 1 1 Leveraging the power of SharePoint 3 2

More information

One of Excel 2000 s distinguishing new features relates to sharing information both

One of Excel 2000 s distinguishing new features relates to sharing information both Chapter 7 SHARING WORKBOOKS In This Chapter Using OLE with Excel Sharing Workbook Files Sharing Excel Data Over the Web Retrieving External Data with Excel One of Excel 2000 s distinguishing new features

More information

Customizing DAZ Studio

Customizing DAZ Studio Customizing DAZ Studio This tutorial covers from the beginning customization options such as setting tabs to the more advanced options such as setting hot keys and altering the menu layout. Introduction:

More information

Crash Course in Modernization. A whitepaper from mrc

Crash Course in Modernization. A whitepaper from mrc Crash Course in Modernization A whitepaper from mrc Introduction Modernization is a confusing subject for one main reason: It isn t the same across the board. Different vendors sell different forms of

More information

Learn Dreamweaver CS6

Learn Dreamweaver CS6 Table of Contents Chapter 4 Dreamweaver Help Accessing Help...3 Chapter 5 Keyboard Shortcuts Keyboard Shortcuts...9 Chapter 6 Setting Preferences Preferences...13 Chapter 7 Web Page Text Adding and Importing

More information

NCMail: Microsoft Outlook User s Guide

NCMail: Microsoft Outlook User s Guide NCMail: Microsoft Outlook 2007 Email User s Guide Revision 1.1 3/9/2009 This document covers how to use Microsoft Outlook 2007 for accessing your email with the NCMail Exchange email system. The syntax

More information

Word: Print Address Labels Using Mail Merge

Word: Print Address Labels Using Mail Merge Word: Print Address Labels Using Mail Merge No Typing! The Quick and Easy Way to Print Sheets of Address Labels Here at PC Knowledge for Seniors we re often asked how to print sticky address labels in

More information

Setting up ODBC, Part 2 Robert Abram

Setting up ODBC, Part 2 Robert Abram Seite 1 von 7 Issue Date: FoxTalk October 2000 Setting up ODBC, Part 2 Robert Abram rob_abram@smartfella.com In this second article of a series about setting up ODBC connections programmaticly through

More information

Handling crosstabs and other wide data in VFP reports

Handling crosstabs and other wide data in VFP reports Handling crosstabs and other wide data in VFP reports When the data you want to report on has many columns, you have a few options. One takes advantage of VFP s fl exibility. Tamar E. Granor, Ph.D. In

More information

One of the fundamental kinds of websites that SharePoint 2010 allows

One of the fundamental kinds of websites that SharePoint 2010 allows Chapter 1 Getting to Know Your Team Site In This Chapter Requesting a new team site and opening it in the browser Participating in a team site Changing your team site s home page One of the fundamental

More information

Without further ado, let s go over and have a look at what I ve come up with.

Without further ado, let s go over and have a look at what I ve come up with. JIRA Integration Transcript VLL Hi, my name is Jonathan Wilson and I m the service management practitioner with NHS Digital based in the United Kingdom. NHS Digital is the provider of services to the National

More information

Getting started 7. Setting properties 23

Getting started 7. Setting properties 23 Contents 1 2 3 Getting started 7 Introduction 8 Installing Visual Basic 10 Exploring the IDE 12 Starting a new project 14 Adding a visual control 16 Adding functional code 18 Saving projects 20 Reopening

More information

the NXT-G programming environment

the NXT-G programming environment 2 the NXT-G programming environment This chapter takes a close look at the NXT-G programming environment and presents a few simple programs. The NXT-G programming environment is fairly complex, with lots

More information

1-Step Appraisals Personal Property Appraisal Software

1-Step Appraisals Personal Property Appraisal Software User Guide for 1-Step Appraisals Personal Property Appraisal Software Home & Business Software for Windows Page Table of Contents Getting Started... Page 3 Image Preferences... Page 4 Adding Business/Company

More information

Navigating and Managing Files and Folders in Windows XP

Navigating and Managing Files and Folders in Windows XP Part 1 Navigating and Managing Files and Folders in Windows XP In the first part of this book, you ll become familiar with the Windows XP Home Edition interface and learn how to view and manage files,

More information

Cool Tools by Craig Boyd, Part II Doug Hennig

Cool Tools by Craig Boyd, Part II Doug Hennig Cool Tools by Craig Boyd, Part II Doug Hennig Doug Hennig continues his examination of cool tools provided to the VFP community by Craig Boyd. Last month, I discussed several tools generously provided

More information

Testing is a very big and important topic when it comes to software development. Testing has a number of aspects that need to be considered.

Testing is a very big and important topic when it comes to software development. Testing has a number of aspects that need to be considered. Testing Testing is a very big and important topic when it comes to software development. Testing has a number of aspects that need to be considered. System stability is the system going to crash or not?

More information

Download Free Pictures & Wallpaper from the Internet

Download Free Pictures & Wallpaper from the Internet Download Free Pictures & Wallpaper from the Internet D 600 / 1 Millions of Free Graphics and Images at Your Fingertips! Discover How To Get Your Hands on Them Almost any type of document you create can

More information

MAPLOGIC CORPORATION. GIS Software Solutions. Getting Started. With MapLogic Layout Manager

MAPLOGIC CORPORATION. GIS Software Solutions. Getting Started. With MapLogic Layout Manager MAPLOGIC CORPORATION GIS Software Solutions Getting Started With MapLogic Layout Manager Getting Started with MapLogic Layout Manager 2011 MapLogic Corporation All Rights Reserved 330 West Canton Ave.,

More information

Splitting a Procedure File

Splitting a Procedure File Splitting a Procedure File It s easier to maintain separate program files rather than one monolithic procedure file. This utility makes it easy. Tamar E. Granor, Ph.D. Procedure files have been part of

More information

Hello World! Computer Programming for Kids and Other Beginners. Chapter 1. by Warren Sande and Carter Sande. Copyright 2009 Manning Publications

Hello World! Computer Programming for Kids and Other Beginners. Chapter 1. by Warren Sande and Carter Sande. Copyright 2009 Manning Publications Hello World! Computer Programming for Kids and Other Beginners by Warren Sande and Carter Sande Chapter 1 Copyright 2009 Manning Publications brief contents Preface xiii Acknowledgments xix About this

More information