1. Trang chủ
  2. » Công Nghệ Thông Tin

1001 Things You Wanted To Know About Visual FoxPro phần 2 ppt

54 410 1

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Định dạng
Số trang 54
Dung lượng 762,39 KB

Nội dung

38 1001 Things You Always Wanted to Know About Visual FoxPro ********************************************************************** * Program : nthSomeDayOfMonth * Compiler : Visual FoxPro 06.00.8492.00 for Windows * Abstract : Returns the date of a specific type of day; e.g., the * : second Tuesday in November of the year 2001 * : nthSomedayOfMonth( 4, 3, 7, 2000 ) returns the date of * : the 3rd Wednesday in July of the year 2000 * Parameters.: tnDayNum: Day number 1=Sunday 7=Saturday * : tnWhich : Which one to find; 1st, 2nd, etc. * : If tnwhich > the number of this kind of day * : in the month, the last one is returned * : tnMonth : Month Number in which to find the day * : tnYear : Year in which to find the day ********************************************************************** FUNCTION nthSomedayOfMonth( tnDayNum, tnWhich, tnMonth, tnYear ) LOCAL ldDate, lnCnt *** Start at the first day of the specified month ldDate = DATE( tnYear, tnMonth, 01 ) *** Find the first one of the specified day of the week DO WHILE DOW( ldDate ) # tnDayNum ldDate = ldDate + 1 ENDDO *** Find the specified one of these e.g, 2nd, 3rd, or last IF tnWhich > 1 lnCnt = 1 DO WHILE lnCnt < tnWhich lnCnt = lnCnt + 1 *** Move forward one week to get the next one of these in the month ldDate = ldDate + 7 *** Are we are still in the correct month? IF MONTH( ldDate ) # tnMonth *** If not, jump back to the last one of these we found and exit ldDate = ldDate - 7 EXIT ENDIF ENDDO ENDIF RETURN ldDate Setting up a payment schedule Another interesting problem is that of setting up a monthly schedule. Take, for example, a schedule of monthly payments to be collected via direct debit of a debtor's checking account. Obviously these payments cannot be collected on Sundays or holidays. They also cannot be collected earlier than the day specified when the schedule is first set up. This poses some interesting problems if the initial seed date for the schedule is between the 28 th and the 31 st of the month. So, in this case, simply using the GOMONTH() function may return an unacceptable date. This function handles weekends, holidays, and GOMONTH() and assumes that you have created your holiday table with two columns: one for the date and one for the name of the Chapter 2: Functions and Procedures 39 holiday. An index on the holiday date is also desirable. Also keep in mind that to be useful, this holiday table must contain, at the very least, the holidays for both this year and next year. FUNCTION MonthlySchedule ( tdStartDate, tnNumberOfMonths ) LOCAL laDates[1], lnCnt, ldDate, llOK, llUsed *** Make sure we have the class library loaded IF 'CH02' $ SET( 'CLASSLIB' ) *** Do nothing class library is loaded ELSE SET CLASSLIB TO CH02 ADDITIVE ENDIF *** Make sure we have the Holidays table available IF !USED( 'Holidays' ) USE Holidays In 0 llUsed = .F. ELSE llUsed = .T. ENDIF SELECT Holidays SET ORDER TO dHoliday FOR lnCnt = 1 TO tnNumberOfMonths *** we want to return the passed date as date[1] IF lnCnt > 1 ldDate = GOMONTH( tdStartDate, lnCnt-1 ) ELSE ldDate = tdStartDate ENDIF *** Now we have to check to be sure that GoMonth didn't give us back a day *** that is earlier than the seed date can't do a direct debit BEFORE the *** specified date i.e., the 28th of the month IF DAY(tdStartDate) > 28 IF BETWEEN( DAY( ldDate ), 28, DAY( tdStartDate ) - 1 ) ldDate = ldDate + 1 ENDIF ENDIF llOK = .F. DO WHILE !llOK *** If current date is a Saturday, go to Monday IF DOW( ldDate ) = 7 ldDate = ldDate + 2 ELSE *** If current date is a Sunday, go to Monday IF DOW( ldDate ) = 1 ldDate = ldDate + 1 ENDIF ENDIF *** OK, now check for Holidays IF !SEEK( ldDate, 'Holidays', 'dHoliday' ) llOK = .T. ELSE ldDate = ldDate + 1 ENDIF ENDDO DIMENSION laDates[lnCnt] laDates[lnCnt] = ldDate 40 1001 Things You Always Wanted to Know About Visual FoxPro ENDFOR IF !llUsed USE IN Holidays ENDIF RETURN CREATEOBJECT( 'xParameters', @laDates ) What date is ten business days from today? A somewhat similar problem is how to calculate a date that is a specified number of business days from a given date. As with the previous example, this assumes the existence of a holiday table that is both region and application specific. FUNCTION BusinessDays ( tdStartDate, tnNumberOfDays ) LOCAL lnCnt, ldDate, llOK, llUsed *** Make sure we have the Holidays table available IF !USED( 'Holidays' ) USE Holidays In 0 llUsed = .F. ELSE llUsed = .T. ENDIF SELECT Holidays SET ORDER TO dHoliday ldDate = tdStartDate FOR lnCnt = 1 TO tnNumberOfDays ldDate = ldDate + 1 llOK = .F. DO WHILE !llOK *** If current date is a Saturday, go to Monday IF DOW( ldDate ) = 7 ldDate = ldDate + 2 ELSE *** If current date is a Sunday, go to Monday IF DOW( ldDate ) = 1 ldDate = ldDate + 1 ENDIF ENDIF *** OK, now check for Holidays IF !SEEK( ldDate, 'Holidays', 'dHoliday' ) llOK = .T. ELSE ldDate = ldDate + 1 ENDIF ENDDO ENDFOR IF !llUsed USE IN Holidays ENDIF RETURN ldDate Chapter 2: Functions and Procedures 41 Gotcha! Strict date format and parameterized views Visual FoxPro's StrictDate format is especially comforting with the specter of the millennium bug looming large in front of us. At least it is as we are writing this. There, is however, one small bug that you should be aware of. If you have SET STRICTDATE TO 2 and try to open a parameterized view that takes a date as its parameter, you will be in for trouble. If the view parameter is not defined or is not in scope when you open or re-query the view, the friendly little dialog box prompting for the view parameter will not accept anything you enter. It will keep saying you have entered an ambiguous date/datetime constant. The workaround is to ensure your view parameter is defined and in scope before trying to open or re-query the view. This means that, if your view is part of a form’s data environment, its NoDataOnLoad property must be set to avoid getting the dialog as the form loads. The other workaround, setting StrictDate to 0 and then back to 2, is not recommended. As we have already mentioned, using a global solution for a local problem is a little bit like swatting flies with a sledgehammer. Working with numbers Mathematical calculations have been handled fairly well since the days of Eniac and Maniac, except for the notable bug in the Pentium math co-processor. The most common problems arise because many calculations produce irrational results such as numbers that carry on for an infinite number of decimal places. Rounding errors are impossible to avoid because computing demands these numbers be represented in a finite form. The study of numerical analysis deals with how to minimize these errors by changing the order in which mathematical operations are performed as well as providing methods such as the trapezoidal method for calculating the area under the curve. A discussion of this topic is beyond the scope of this book, but we can give you some tips and gotchas to watch out for when working with numbers in your application. Converting numbers to strings Converting integers to strings is fairly straightforward. ALLTRIM( STR( lnSomeNumber ) ) will handle the conversion if the integer contains ten digits or less. If the integer contains more than ten digits, this function will produce a string in scientific notation format unless you specify the length of the string result as the second parameter. When converting numeric values containing decimal points or currency values, it is probably better to use another function. Although it can be accomplished using the STR() function, it is difficult to write a generic conversion routine. In order to convert the entire number you must specify both the total length of the number (including the decimal point) and the number of digits to the right of the decimal point. Thus STR(1234.5678) will produce '1235' as its result, and to get the correct conversion you must specify STR(1234.5678, 9, 4) . In Visual FoxPro 6.0, the TRANSFORM() function has been extended so that when called without any formatting parameters, it simply returns the passed value as its equivalent character string. Thus TRANSFORM(1234.5678) will correctly return '1234.5678'. In all versions of Visual FoxPro you can use ALLTRIM( PADL ( lnSomeNumber, 32 ) ) to get the same result (providing that the total length of lnSomeNumber is less than thirty-two digits). 42 1001 Things You Always Wanted to Know About Visual FoxPro Gotcha! calculations that involve money This one can bite if you are not careful. Try this in the command window and you will see what we mean. ? ( $1000 / 3 ) returns the expected result of 333.3333 since currency values are always calculated to a precision of four decimal places. However, ? ( $1000 * ( 1/3 ) ) returns 333.3000, which is not a very accurate result! Especially when you consider the result of the equivalent numeric calculation: SET DECIMALS TO 4 ? ( 1000 * ( 1/3 ) ) returns 333.3333. The actual precision of the displayed result depends on the setting of SET DECIMALS, although the result is actually calculated to 8 places by default. The moral of this story is that currency values should always be converted to numeric prior to using them in arithmetic operations. The functions MTON() and NTOM() are essential in this scenario, although watch out for unexpected results if you do not convert both ways! ? ( MTON( $1000 ) * ( 1/3 ) ) displays 333.333333 even with decimals set to 2. While ? NTOM( ( MTON( $1000 ) * ( 1/3 ) ) ) finally gets the expected result of 333.3333. String functions Visual FoxPro has several native string manipulation functions to handle almost everything you could ever need. ALLTRIM() to remove leading and trailing spaces, PADL() and PADR() to left and right pad, and STRTRAN() and CHRTRAN() to replace individual characters within a string. But did you know that you can use this line of code: cString1 – cString2 – cString3 to accomplish the same thing as this one? RTRIM( cString1 ) + RTRIM( cString2 ) + RTRIM( cString3 ) Gotcha! string concatenation Even if the tables in your application do not allow null values, you may still need to deal with them. Very often, SQL statements using outer joins result in one or more columns that contain null values. This can be troublesome in cases where you may want to display a concatenated value from a result set, for example, in a drop down list. Try this in the command window: Chapter 2: Functions and Procedures 43 c1 = 'Yada Yada Yada' c2 = .NULL. ? c1 + c2 As you might expect, Visual FoxPro complains about an operator/operand type mismatch. If, however, you do this instead: ? c1 + ALLTRIM( c2 ) you will see .NULL. displayed on the Visual FoxPro screen. No error, just .NULL. If you do not cater for null values by using NVL() to trap for them, you may find this behavior a little difficult to debug when it occurs in your application. We sure did the first time we encountered this behavior! Converting between strings and data The following are examples of functions that Visual FoxPro doesn't have, but in our opinion definitely should have. We keep these in our general all-purpose procedure file because we use them so frequently. Combo and List boxes store their internal lists as string values. So when you need to use these to update or seek values of other data types, you need to convert these strings to the appropriate data type before you are able to use them. The first of these functions is used to do just that: FUNCTION Str2Exp( tcExp, tcType ) *** Convert the passed string to the passed data type LOCAL luRetVal, lcType *** Remove double quotes (if any) tcExp = STRTRAN( ALLTRIM( tcExp ), CHR( 34 ), "" ) *** If no type passed map to expression type lcType = IIF( TYPE( 'tcType' ) = 'C', UPPER(ALLTRIM( tcType )), TYPE( tcExp ) ) *** Convert from Character to the correct type DO CASE CASE INLIST( lcType, 'I', 'N' ) AND ; INT( VAL( tcExp ) ) == VAL( tcExp ) && Integer luRetVal = INT( VAL( tcExp ) ) CASE INLIST( lcType, 'N', 'Y', ‘B’ ) && Numeric or Currency luRetVal = VAL( tcExp ) CASE INLIST( lcType, 'C', 'M' ) && Character or memo luRetVal = tcExp CASE lcType = 'L' && Logical luRetVal = IIF( !EMPTY( tcExp ), .T., .F.) CASE lcType = 'D' && Date luRetVal = CTOD( tcExp ) CASE lcType = 'T' && DateTime luRetVal = CTOT( tcExp ) OTHERWISE *** There is no otherwise unless, of course, Visual FoxPro adds *** a new data type. In this case, the function must be modified ENDCASE *** Return value as Data Type RETURN luRetVal 44 1001 Things You Always Wanted to Know About Visual FoxPro If you write client/server applications, you already know that you must convert all expressions to strings before using them within a SQLEXEC(). Even if you are not doing client/server development, you will require this functionality in order to build any kind of SQL on the fly. The following function not only converts the passed parameter to a character value, it also wraps the result in quotation marks where appropriate. This is especially useful when invoking the function from an onthefly SQL generator. It is even easier in Visual FoxPro 6.0 because you can use the TRANSFORM function without a format string to convert the first argument to character. TRANSFORM( 1234.56 ) produces the same result as ALLTRIM( PADL( 1234.56, 32 ) ). FUNCTION Exp2Str( tuExp, tcType ) *** Convert the passed expression to string LOCAL lcRetVal, lcType *** If no type passed map to expression type lcType=IIF( TYPE('tcType' )='C', UPPER( ALLTRIM( tcType ) ), TYPE( 'tuExp' ) ) *** Convert from type to char DO CASE CASE INLIST( lcType, 'I', 'N' ) AND INT( tuExp ) = tuExp && Integer lcRetVal = ALLTRIM( STR( tuExp, 16, 0 ) ) CASE INLIST( lcType, 'N', 'Y', 'B' ) && Numeric or Currency lcRetVal = ALLTRIM( PADL( tuExp, 32 ) ) CASE lcType = 'C' && Character lcRetVal = '"' + ALLTRIM( tuExp ) + '"' CASE lcType = 'L' && Logical lcRetVal = IIF( !EMPTY( tuExp ), '.T.', '.F.') CASE lcType = 'D' && Date lcRetVal = '"' + ALLTRIM( DTOC( tuExp ) ) + '"' CASE lcType = 'T' && DateTime lcRetVal = '"' + ALLTRIM( TTOC( tuExp ) ) + '"' OTHERWISE *** There is no otherwise unless, of course, Visual FoxPro adds *** a new data type. In this case, the function must be modified ENDCASE *** Return value as character RETURN lcRetVal Other useful functions There are several other generic functions that can live in your general procedure file or base procedure class. One obvious example is the SetPath() function (presented in Chapter One). We find the following functions particularly useful and hope you will too. How do I determine if a tag exists? Wouldn't it be nice if Visual FoxPro had a native function that returned true if a tag existed? This would be especially useful, for example, when creating a custom grid class that allows the user to click on a column header to sort the grid by the tag on that column. It would also be useful to test for the existence of an index if it must be created programmatically. This code provides that functionality. Chapter 2: Functions and Procedures 45 FUNCTION ISTAG( tcTagName, tcTable ) LOCAL lnCnt, llRetVal, lnSelect IF TYPE( 'tcTagName' ) # 'C' *** Error - must pass a Tag Name ERROR '9000: Must Pass a Tag Name when calling ISTAG()' RETURN .F. ENDIF *** Save Work Area Number lnSelect = SELECT() IF TYPE( 'tcTable' ) = 'C' AND ! EMPTY( tcTable ) *** If a table specified, select it SELECT lcTable ENDIF *** Check Tags FOR lnCnt = 1 TO TAGCOUNT() IF UPPER(ALLTRIM( tcTagName ) ) == UPPER( ALLTRIM( TAG( lnCnt ) ) ) llRetVal = .T. EXIT ENDIF NEXT *** Restore Work Area SELECT (lnSelect) *** Return Whether Tag Found RETURN llRetVal By the way, notice the use of the ERROR command in this function. Rather than simply displaying a message when a parameter fails validation, this function raises an application error that can be trapped by an error handler, just like a normal Visual FoxPro error. How do I determine if a string contains at least one alphabetic character? The Visual FoxPro ISALPHA() returns .T. if the string passed to it begins with a letter. Similarly, ISDIGIT() will do the same if the string begins with a number. But what if you need to know if the string contains any alphabetic characters? Code like this would work, but it is slow and bulky: FUNCTION ContainsAlpha( tcString ) LOCAL lnChar, llRetVal llRetVal = .F. *** Loop through the string and test each character FOR lnChar = 1 TO LEN( tcString ) IF ISALPHA( SUBSTR( tcString, lnChar, 1 ) llRetVal = .T. EXIT ENDIF ENDFOR RETURN llRetVal However, why write ten lines of code when two will do the same job? 46 1001 Things You Always Wanted to Know About Visual FoxPro FUNCTION ContainsAlpha( tcString ) RETURN LEN( CHRTRAN( UPPER( tcString ), "ABCDEFGHIJKLMNOPQRSTUVWXYZ", "" ) ); # LEN( tcString ) Obviously, a similar methodology can be used to determine if a string contains any digits. However, we refuse to insult our readers' intelligence by listing it here. After all, you were all smart enough to buy this book, weren't you? How to convert numbers to words One common problem is that of converting numbers into character strings, for printing checks, or as confirmation of an invoice or order total. There have been many solutions proposed for this over the years, but we still like this one the best because it handles large numbers, negative numbers and adopts an innovative approach to decimals too. ********************************************************************** * Program : NumToStr * Compiler : Visual FoxPro 06.00.8492.00 for Windows * Abstract : Convert number into a text string * Notes : Handles Numbers up to 99,999,999 and will accommodate * : negative numbers. Decimals are rounded to Two Places * : And returned as 'and xxxx hundredths' ************************************************************************ FUNCTION NumToStr LPARAMETERS tnvalue LOCAL lnHund, lnThou, lnHTho, lnMill, lnInt, lnDec LOCAL llDecFlag, llHFlag, llTFlag, llMFlag, llNegFlag LOCAL lcRetVal *** Evaluate Parameters DO CASE CASE TYPE('tnValue') # 'N' RETURN('') CASE tnvalue = 0 RETURN 'Zero' CASE tnvalue < 0 *** Set the Negative Flag and convert value to positive llNegFlag = .T. tnvalue = ABS(tnvalue) OTHERWISE llNegFlag = .F. ENDCASE *** Initialise Variables STORE .F. TO llHFlag,llTFlag,llMFlag STORE 0 TO lnHund, lnThou, lnMill STORE "" TO lcRetVal *** Get the Integer portion lnInt = INT(tnvalue) *** Check for Decimals IF MOD( tnValue, 1) # 0 lnDec = ROUND(MOD(tnvalue,1),2) llDecFlag = .T. ELSE llDecFlag = .F. ENDIF Chapter 2: Functions and Procedures 47 *** Do the Integer Portion first DO WHILE .T. DO CASE CASE lnInt < 100 && TENS IF EMPTY(lcRetVal) lcRetVal = lcRetVal + ALLTRIM(con_tens(lnInt)) ELSE IF RIGHT(lcRetVal,5)#" and " lcRetVal = lcRetVal+' and ' ENDIF lcRetVal = lcRetVal + ALLTRIM(con_tens(lnInt)) ENDIF CASE lnInt < 1000 && HUNDREDS lnHund = INT(lnInt/100) lnInt = lnInt - (lnHund*100) lcRetVal = lcRetVal + ALLTRIM(con_tens(lnHund)) + " Hundred" IF lnInt # 0 lcRetVal = lcRetVal+" and " LOOP ENDIF CASE lnInt < 100000 && THOUSANDS lnThou = INT(lnInt/1000) lnInt = lnInt - (lnThou*1000) lcRetVal = lcRetVal + ALLTRIM(con_tens(lnThou)) + " Thousand" IF lnInt # 0 lcRetVal = lcRetVal + " " LOOP ENDIF CASE lnInt < 1000000 && Hundred Thousands lnHTho = INT(lnInt/100000) lnInt = lnInt - (lnHTho * 100000) lcRetVal = lcRetVal + ALLTRIM(con_tens(lnHTho)) + " Hundred" IF lnInt # 0 lcRetVal = lcRetVal + " and " LOOP ELSE lcRetVal = lcRetVal + " Thousand" ENDIF CASE lnInt < 100000000 && Millions lnMill = INT(lnInt/1000000) lnInt = lnInt - (lnMill * 1000000) lcRetVal = lcRetVal + ALLTRIM(con_tens(lnMill)) + " Million" IF lnInt # 0 lcRetVal = lcRetVal + ", " LOOP ENDIF ENDCASE EXIT ENDDO *** Now Handle any Decimals IF llDecFlag lnDec = lnDec * 100 lcRetVal = lcRetVal + " and " + ALLTRIM(con_tens(lnDec)) + ' Hundredths' ENDIF *** Finally Handle the Negative Flag IF llNegFlag lcRetVal = "[MINUS " + ALLTRIM(lcRetVal) + "]" ENDIF *** Return the finished string [...]... ANDY%KR# 02 ANDY%KR# 02 ANDY%KR# 02 We are sure you will find ways of improving or adapting these functions, but they have served us well for several years now and we hope you like them Where do you want to GOTO? We all use the GOTO command from time to time, but one of a Visual FoxPro programmer’s little annoyances is that GOTO does not do any error checking of its own If you tell Visual FoxPro to GOTO... way of doing things The correct approach is, as we explain in the "So how do you go about designing a class" section later in this chapter, is to design your classes properly and instead of relying entirely on inheritance, to use composition to add specific functionality when it is needed 60 1001 Things You Always Wanted to Know About Visual FoxPro Composition Composition is a term used to describe... 70 1001 Things You Always Wanted to Know About Visual FoxPro only needs to know how to get something done without actually knowing what code is being implemented which, in this case, has been implemented by using delegation All buttons can now have the same code in their OnClick method (and it is just one line) though they each pass a different parameter as shown: First Button: Prev Button: Next Button:... Working with your classes Once you have defined your classes you will, of course, want to start using them in your daily work instead of merely hacking the Visual FoxPro base classes on an 'as needed' basis To do this effectively you need to be able to instruct FoxPro when to use your classes instead of the standard defaults provided with the product How do I get my classes into the form controls toolbar?... same 62 1001 Things You Always Wanted to Know About Visual FoxPro name in several different objects but which actually do different things in different objects The call to a method is only meaningful in the context of a specific object and there is therefore no possibility of confusion This is a very powerful tool in the context of application development and maintenance because it allows objects to be... ERROR &lcOldError RETURN lLRetVal 54 1001 Things You Always Wanted to Know About Visual FoxPro One thing to notice in this program is the use of the ON(“ERROR”) function to save off the current error handler so that we can safely suppress the normal error handling with ON ERROR * and restore things at the end of the function This is a very important point and is all too easily forgotten in the heat of... simply overriding the Navigate method when necessary Similarly we can change the button behavior or refresh behavior, without affecting anything else 72 1001 Things You Always Wanted to Know About Visual FoxPro This last point is very important If you examine the RefreshParent method you will notice that it refers explicitly to ThisForm: *** This Method refreshes the parent form for the Navigation Bar ***... it finds It assumes that, unless specified otherwise, you want the first item and the separator is a comma However, both elements can be specified Here it is: 50 1001 Things You Always Wanted to Know About Visual FoxPro ********************************************************************** * Program : GetItem.PRG * Compiler : Visual FoxPro 06.00.84 92. 00 for Windows * Abstract : Extracts the specified... the class libraries Even though only partially built, the basic structure is already clear 68 1001 Things You Always Wanted to Know About Visual FoxPro Figure 3 .2 Example class structure We will have more to say about the naming convention later For the moment let's look at the class structure here What you see is the beginnings of our 'generic' class libraries As the name implies these libraries... form part of the class definition 64 1001 Things You Always Wanted to Know About Visual FoxPro Things which come in the 'Could Do' category are normally indicators that the class may itself require either one or more subclasses (or, more rarely, the cooperation of objects of another class) In other words these are the things that it would be possible for the class to do, but which would not actually . Type RETURN luRetVal 44 1001 Things You Always Wanted to Know About Visual FoxPro If you write client/server applications, you already know that you must convert all expressions to strings before using. thirty-two digits). 42 1001 Things You Always Wanted to Know About Visual FoxPro Gotcha! calculations that involve money This one can bite if you are not careful. Try this in the command window and you will. it is: 50 1001 Things You Always Wanted to Know About Visual FoxPro ********************************************************************** * Program : GetItem.PRG * Compiler : Visual FoxPro 06.00.84 92. 00

Ngày đăng: 05/08/2014, 10:20

TỪ KHÓA LIÊN QUAN

w