*-- ARRAY2.HOW *-- 03/19/1996 -- Ken Mayer (CIS: 71333,1030) ------------------------------------- ARRAYS (Intermediate) in Visual dBASE ------------------------------------- This HOW TO document is aimed at: 1) Examining OOP methods of working with Arrays; 2) Examining the use of Arrays with user-interface objects; If you're looking for some basics on what an array is, or some other aspects of arrays not covered in this HOW TO file, download ARRAY1.HOW in the same place you found this document and/or check the Language Reference or online Help. ---------------------------------------------- Object-Oriented Methods of Working with Arrays ---------------------------------------------- If you read ARRAY1.HOW, or have worked with arrays previously (either in dBASE or in other programming languages) you will have a basic understanding of how arrays work. It is important that you understand that arrays are objects in Visual dBASE. As such, all of the usual methods of working with and manipulating objects work as they do for other objects in Visual dBASE. -------------------------------------------------------------------- NOTE: For more information on Object-Oriented Programming and Visual dBASE, download OOP.HOW from library 10 (Knowledgebase) of the VDBASE forum on Compuserve. Also take a look at CLASS.HOW in the same library/forum. -------------------------------------------------------------------- Nearly all of the methods discussed in this .HOW file have corresponding dBASE Functions (usually the same name with "A" tacked onto the front of the method name). Arrays, like all objects, have methods associated with them. I will cover these methods in groups, based on their purposes, and will attempt to cover the corresponding functions for those more comfortable with using functions. In a couple of cases, I will discuss a function that does not have an associated method. The main difference is that a method is called this way: aArrayName.methodname(parameters) While a function is called this way: aFunction(aArrayName,parameters) -------------------------------------------------------------------- NOTE: In an attempt to make this as useful as possible, there are a couple of quick and dirty routines at the end of this .HOW used to display the contents of an array. The first is ONLY useful for single-column arrays. The second should not be used for single-column arrays due to the method used to determine the array's size. To use either of them -- copy them out of this document by either re-entering them or using Windows' clipboard and save them as programs ...: do show1 with "aCity" do show2 with "aCity" -------------------------------------------------------------------- ------------------------------------- Instantiating an Array (Creating One) ------------------------------------- The "NEW"/OOP method -------------------- The simplest method of creating an array in Visual dBASE is to use the NEW method -- aMyArray = new array() This tells dBASE that "aMyArray" will be an array, with all of the methods associated with one, but it does _not_ give any sort of starting values (number of rows, columns, dimensions ...). You can get more explicit, and tell dBASE that your array will start with 10 rows and 2 columns, by using: aMyArray = new array(10,2) The Procedural Method --------------------- To do this using the older procedural code, you would use: declare aMyArray[10,2] The advantage to the first method is that it is object-oriented, and the longer you use Visual dBASE, the more you will find yourself using these forms of coding syntax. The Literal Array Method ------------------------ You can also define an array this way (which is useful for one- dimensional arrays -- i.e., one column, but not for multi-dimensional), if you know what the values will be in advance: aMyArray = {"element1","element2","element3","etc."} ---------------------------- Placing Elements In An Array ---------------------------- Placing elements in an array can be as easy as assigning values like you would any other memory variable, or as complicated as scanning a table and obtaining the needed values. This section of the HOW TO file will cover as many variations as the author can think of. The ADD() Method ---------------- This is the most elegant way to add elements to a one-dimensional (single-column) array. The array must already be instantiated (using one of the methods shown above), but once you have done this, you can add elements very easily: aMyArray.Add("value") Where "value" is the value you wish to add to the array (note: a value in this example may be character, numeric, logical, date, array ...). If you have an array that contains a list of cities, for example, you might want to add another city to the list. Assume the following array (called aCity): Column 1 Row 1 San Francisco Row 2 Los Angeles Row 3 Miami Row 4 New York To add "Sydney" to the list: aCity.Add("Sydney") When this has been executed, the array will now look like: Column 1 Row 1 San Francisco Row 2 Los Angeles Row 3 Miami Row 4 New York Row 5 Sydney The GROW Method (array.Grow() and aGrow() ) ------------------------------------------- If you have a multi-columnar array, the Add method will not work properly. It will allow you to add items in the first column, but not any others. You should consider, instead, using the GROW() method to handle this. Syntax: arrayname.Grow() where = 1 for a row 2 for a column Example -- if we have this array: Column 1 Column 2 Row 1 San Francisco 4,000,000 Row 2 Los Angeles 10,000,000 Row 3 Miami 8,000,000 Row 4 New York 25,000,000 And we want to add elements to both columns: aCity.Grow(1) && add a row aCity[5,1] = "Sydney" aCity[5,2] = " 2,000,000" Reminder -- the ROW is the first part of the subscript, and the COLUMN is the second. In a situation like this, if you do not know how many rows are currently in the array, you might want to use the function aLen(), to ensure you are in the correct row: aCity.Grow(1) && add a row nRow = aLen(aCity,1) && number of rows in array aCity[nRow,1] = "Sydney" aCity[nRow,2] = " 2,000,000" Which would give us an array that looks like: Column 1 Column 2 Row 1 San Francisco 4,000,000 Row 2 Los Angeles 10,000,000 Row 3 Miami 8,000,000 Row 4 New York 25,000,000 Row 5 Sydney 2,000,000 The GROW() method is useful as well if you wish to add a column to your array. Backing this up a step, if we had aCity defined as a single-column array: Column 1 Row 1 San Francisco Row 2 Los Angeles Row 3 Miami Row 4 New York Row 5 Sydney and we wanted to add a second column, we could do this by using the GROW() method: aCity.Grow(2) && add a column Of course, at this point, the array would look like: Column 1 Column 2 Row 1 San Francisco .f. Row 2 Los Angeles .f. Row 3 Miami .f. Row 4 New York .f. Row 5 Sydney .f. Which means we would want to add data to the column. This could be done with: aCity[1,2] = "4,000,000" aCity[2,2] = "10,000,000" aCity[3,2] = "8,000,000" aCity[4,2] = "25,000,000" aCity[5,2] = "2,000,000" This will give us the array: Column 1 Column 2 Row 1 San Francisco 4,000,000 Row 2 Los Angeles 10,000,000 Row 3 Miami 8,000,000 Row 4 New York 25,000,000 Row 5 Sydney 2,000,000 The INSERT() Method (and aInsert() ) ------------------------------------ Sometimes it is desireable to add an element to an array at a location other than the end of the array. To do this, you would want to use the INSERT method to place the element(s) into the array at the appropriate location(s). Syntax: arrayname.Insert([,]) where = position you wish to insert _at_ = 1 for a row (optional for a row) 2 for a column Working with a single column array, assuming your array looked like the following: Column 1 Row 1 San Francisco Row 2 Los Angeles Row 3 New York Row 4 Sydney And you wanted to insert a row at 3, that contained "Miami": aCity.Insert(3) This will give you an array that looks like: Column 1 Row 1 San Francisco Row 2 Los Angeles Row 3 .f. Row 4 New York And then you would need to change the value of the element in row 3: aCity[3] = "Miami" NOTE: the last row now contains "New York", and what you would expect to be the last row, "Sydney" is missing. This is because the Insert() method (and the AINSERT() function) literally insert something into the current array, but do not resize it. This is as designed. In order to make the array one row larger, as well as inserting a row into the array, you would need to use the Grow() method (or AGROW() function): aCity.Grow(1) && 1 for row, 2 for column aCity.Insert(3) && new row at 3 aCity[3] = "Miami" Which would produce: Column 1 Row 1 San Francisco Row 2 Los Angeles Row 3 Miami Row 4 New York Row 5 Sydney If you are working with a multi-column array, you might wish to insert a new column into the array. This would be done in a similar fashion. If you had an array that looked like: Column 1 Column 2 Row 1 San Francisco 4,000,000 Row 2 Los Angeles 10,000,000 Row 3 Miami 8,000,000 Row 4 New York 25,000,000 Row 5 Sydney 2,000,000 And you wanted to shift column 2 to the right, making it column 3, and have a new column 2 that you could use for some other set of values: aCity.Grow(2) && add a new column aCity.Insert(2,2) && insert a new column at column 2 This would give an array that looked like: Column 1 Column 2 Column 3 Row 1 San Francisco .f. 4,000,000 Row 2 Los Angeles .f. 10,000,000 Row 3 Miami .f. 8,000,000 Row 4 New York .f. 25,000,000 Row 5 Sydney .f. 2,000,000 And again, you could replace the values in the new column: aCity[1,2] = whatever etc. FILL() (and aFill() ) --------------------- Fill is used to place values into an array -- filling an array with default values, for example. Syntax: arrayname.Fill([,[,]]) where = value to fill the array with = (optional) start position = (optional) number of elements to fill Example -- If you were using an array to total some values, you might want to default everything in the array to 0: aTotal = new array(20) aTotal.Fill(0) You can specify a starting location, so that if you only wanted the last 15 elements to be filled, you could specify: aTotal.Fill(0,6) && start at element six You can also specify the _number_ of elements to fill. If you wanted just the first five elements set to 0: aTotal.Fill(0,1,5) Directory Listings -- DIR() and DIREXT() (and the aDir()/aDirExt() functions) ----------------------------------------------------------------------- For some situations (we will look at ComboBoxes and Listboxes in a bit), it is desirable to grab information from the directory. You may wish to give your user an option to open a specific table, for example. To do that, it is useful to give them a list of all tables in the directory. This can be done with the DIR() (and if you're using Win 95, the DIREXT()) method. Syntax: arrayname.Dir() or arrayname.DirExt() where = file skeleton -- you can specify the file type, for example, using standard DOS wildcards (*, ?). Example: aTables = new array() aTables.Dir("*.DBF") Will get you all the .DBF files in the current directory. The only problem is, this will return the following columns: 1 2 3 4 5 Filename Size Date Time File Attributes (DOS) Character Numeric Date Character Character What if all you really wanted was the filenames? One simple solution is to copy the elements from the first column to another array: aTable1 = new array() aTable1.Dir("*.DBF") aTable2 = new Array() for nRow = 1 to aLen(aTable1,1) && number of rows in first array aTable2.Add(aTable1[nRow,1]) && add just the element in the && first column next The second array will contain _only_ the file names. If you're working with Windows 95, you may wish to allow the user to see the "Long Filename". To get this information, instead of using the DIR() method, use the DIREXT() method: aTable1.DirExt("*.DBF") This will return an array with the same information as the DIR() method, but it will add additional columns to the right: 6 7 8 9 Filename "Alias" Date Created Time Created Last Access Date DOS Filename Date Character Date (Short name) The first column will hold the Windows 95 "long" filename. If you are developing a system where you do not know if it will be used in Win 3.1 or Windows 95, one way to check for this is with: fNameMax() Under Windows 3.1, FNAMEMAX() returns a value of 12 (8.3 = 12). So, to make sure you are using the correct version: if FNAMEMAX() > 12 && greater than 12, means Win 95 aTable1.DirExt("*.DBF") else && otherwise, Win 3.1 aTable1.Dir("*.DBF") endif Storing the Records (or just certain fields) in a Table into an Array --------------------------------------------------------------------- COPY TO ARRAY [FIELDS ] [] APPEND FROM ARRAY ... REPLACE FROM ARRAY These commands allow you to copy a record (or group of records) into an array for processing, and to copy the contents of an array (back) to a table. There are two ways to copy data from a table to an array. If you define an array that is one dimensional, you will only copy one record to the array -- but you must define the number of fields to be copied -- for example, if you wanted just the first five fields: aMyArray = new array(5) copy to array aMyArray Will copy the first five fields of the current record to the array aMyArray. Note, that this effectively stores your record in a single column. If you define an array that has two columns (or more, but only two will be used by this command), you must define the number of records (which equates to the number of rows in the array) and the number of fields (which equates to the number of columns in the array). The commands: aMyArray = new array(2,3) copy to array aMyArray will copy the first three fields of the current record and the next record in the table (two records). This is different than the first example, because dBASE actually stores the data in the array as if it were a table -- the rows are the records, the columns are the fields (sort of like a BROWSE in dBASE). If you wanted to copy all records and all fields in a table, you might want to use a combination of the RECCOUNT() and FLDCOUNT() functions with the instantiation of the array: aMyArray = new array(reccount(),fldcount()) copy to array aMyArray In addition, you can specify specific fields to copy: aMyArray = new array(reccount(),1) && record count, 1 column copy to array aMyArray fields FirstName You can also specify a "scope" with the "NEXT " or "FOR " operators ... There are quite a few options for this, check the Online Help for COPY TO ARRAY. Coding Your Own --------------- There are many ways to code a routine to fill an array. Below are some examples: scan arrayname.Add(table->field) endscan do while .not. eof() arrayname.Add(table->field) skip enddo for i = 1 to somevalue arrayname.Add(something) next ------------------------------- Removing Elements From An Array ------------------------------- There are times when that you may want to delete an element, row, or column. There is a trick to this, because when you use the methods/ functions in dBASE to handle deleting of these (elements, columns and rows), dBASE moves the element/row/column to the end of the array, and replaces the element(s)'s values with .F. (in reality "empty" -- see discussions of the EMPTY() function in ARRAY1.HOW). If you delete an element/row/column in an array, then use the array sorting function/ method in dBASE, you may get an error. To delete an element in a single-dimensional array: --------------------------------------------------- Syntax: arrayname.Delete() where = element number to delete or if using optional type parameter the row or column to delete For example, in the aCity array, which looks like: Column 1 Row 1 San Francisco Row 2 Los Angeles Row 3 New York Row 4 Miami Row 5 Sydney If we wanted to remove the second element: aCity.Delete(2) The array would contain the following values _after_ executing this command: Column 1 Row 1 San Francisco Row 2 New York Row 3 Miami Row 4 Sydney Row 5 .f. To delete a row (single or multiple dimensional array): ------------------------------------------------------- arrayname.Delete(nRow,1) Assuming a version of the aCity array that looked like: Column 1 Column 2 Row 1 San Francisco 4,000,000 Row 2 Los Angeles 10,000,000 Row 3 New York 25,000,000 Row 4 Miami 8,000,000 Row 5 Sydney 1,000,000 To delete the second row of this array, the command: aCity.Delete(2,1) Would change the array to the following: Column 1 Column 2 Row 1 San Francisco 4,000,000 Row 2 New York 25,000,000 Row 3 Miami 8,000,000 Row 4 Sydney 1,000,000 Row 5 .f. .f. To delete a column (multiple columnar array): --------------------------------------------------------- arrayname.Delete(nColumn,2) && 2 means "column" Assuming the aCity array looks like: Column 1 Column 2 Column 3 Row 1 San Francisco 4,000,000 678 Row 2 Los Angeles 10,000,000 123 Row 3 New York 25,000,000 456 Row 4 Miami 8,000,000 678 Row 5 Sydney 1,000,000 1234 The command to delete the second column would be: aCity.Delete(2,2) Which would give us an array that looked like: Column 1 Column 2 Column 3 Row 1 San Francisco 678 .f. Row 2 Los Angeles 123 .f. Row 3 New York 456 .f. Row 4 Miami 678 .f. Row 5 Sydney 1234 .f. Removing the deleted elements/rows/columns of the array: -------------------------------------------------------- In all three cases shown, the problem is, as noted, the element/row/ column deleted will still exist in the array. So, you then use the aReSize function/method: Syntax: arrayname.Resize([,]) where = new number of rows for array = (optional) new number of columns for array, if multi-column array. If using this optional parameter, the number of rows parameter _must_ have a value greater than zero. The following examples are combined code for the previous examples: Removing an element from a single-dimension array: aCity.Delete(2) nRows = aLen(aCity,1) aCity.Resize(nRows-1) Removing a row of a multi-column array: aCity.Delete(2,1) nRows = aLen(aCity,1) aCity.Resize(nRows-1) Removing a column from a multi-column array: aCity.Delete(2,2) nColumns = aLen(aCity,2) nRows = aLen(aCity,1) ** Note, the ROWS parameter here ** _must_ contain a value > 0. aCity.Resize(nRows,nColumns-1) ---------------- Copying An Array ---------------- Sometimes it is useful to copy the contents of an array. With a little thought you can probably come up with a few situations where this would be useful, including, but not limited to, keeping a "backup" of the original, in case the user does something truly weird to the data. The dBASE function aCopy() is useful here, as it allows you to copy the contents of one array to another. The simplest way to do this is to ensure that the "target" array has been instantiated and is big enough to hold the contents of the first. Syntax: aCopy(,) where = the array you are copying from = the array you are copying to If you are copying a one-dimensional array: nRows = aLen(aCity,1) aBackup = new array(nRows) aCopy(aCity,aBackup) If you need to copy a multi-column array: nRows = aLen(aCity,1) nCols = aLen(aCity,2) aBackup = new array(nRows,nCols) aCopy(aCity,aBackup) In some situations, you may want to only copy some of the data from one array to another. The aCopy() function has parameters to handle this (see the Language Reference for details). In addition, you may simply wish to code your own routine. One file in the VDBASE forum on Compuserve that does a lot of this type of manipulation is MOVERW.ZIP (Library 7). It basically uses a pair of listbox controls on a form, with arrays for the datasource. The user has the option to move items from the first list to the second, and back again ... ------------------ Searching An Array ------------------ The ability to search an array to find an element can be very useful. In Visual dBASE, this is done with the SCAN() method (or the ASCAN() function). Basically, this method returns the element of the item you are scanning for, if it is contained in the array. Syntax: arrayname.scan() where = item being searched for. For example, if you wanted to look in the (one-column version of the) aCity array for the city "Miami", the scan() method can tell you which row it is in: nFoundRow = aCity.Scan("Miami") ? aCity[nFoundRow] The SCAN() method is case-sensitive. If you are using this with a multi-column array, it gets more complex, as you will be given the element number, not the row. To make this easier, you can combine the returned value with the SUBSCRIPT() method. nElement = aCity.Scan("Miami") nRow = aCity.Subscript(nElement,1) nColumn = aCity.Subscript(nElement,2) ? aCity[nRow,nColumn] ---------------- Sorting An Array ---------------- Sometimes the data entered into an array is in a jumble, and you want it sorted. Syntax: arrayname.sort([]) where = (optional) column to sort on. If left blank, the sort method will sort on the first column. Example: aCity.Sort() This sorts the array aCity on the first column. If there are more columns, the rows will all move with the elements in the first column. Assume the following array _before_ the sort: Column 1 Column 2 Row 1 San Francisco 4,000,000 Row 2 Los Angeles 10,000,000 Row 3 New York 25,000,000 Row 4 Miami 8,000,000 Row 5 Sydney 1,000,000 And after: Column 1 Column 2 Row 1 Los Angeles 10,000,000 Row 2 Miami 8,000,000 Row 3 New York 25,000,000 Row 4 San Francisco 4,000,000 Row 5 Sydney 1,000,000 To sort on the _second_ column: aCity.Sort(2) Which would give us: Column 1 Column 2 Row 1 Sydney 1,000,000 Row 2 San Francisco 4,000,000 Row 3 Miami 8,000,000 Row 4 Los Angeles 10,000,000 Row 5 New York 25,000,000 ----- Misc. ----- There are a few methods/functions that can be useful in conjunction with the others, and I'll take a brief look at them below. Element() --------- This method is useful to determine the element number of a specific element in an array. Syntax: arrayname.Element([,]) where = the element number, or the row when using a multi-dimensional array. = (optional) column number If you're working through an array in some form of processing, you could find the specific element with: nElement = aCity.Element(1) In a one-dimensional array, the ELEMENT() method is redundant -- it's much more useful in a multi-dimensional array. The parameters allow you to specify subscripts -- for example, in a two-column array, you could specify the row and the column: ?aCity.Element(3,2) && row 3, column 2 This should return a value of 6, as this would be the sixth element in this array. If the array had more columns, the value returned would not be 6. For example, in a three column array, using the same command above, would return a value of 8. Subscript() ----------- This can be really useful. If you know the element number, you can determine the specific subscript for a multi-column (two-dimensional) array. Syntax: arrayname.Subscript(,) where = element number you are looking for the subscript for = 1 for row, 2 for column. Example: ?aCity.Subscript(nElement,1) && row ?aCity.Subscript(nElement,2) && column This will give you the actual subscript (row/column) information. You could, instead, display the contents of a specific element, which can be useful if you used the Scan() method: nElement = aCity.Scan("25,000,000") nRow = aCity.Subscript(nElement,1) nColumn = aCity.Subscript(nElement,2) ? aCity[nRow,nColumn] aLen() ------ A rather useful function in dBASE, it returns the length of an array, either by rows, columns, or number of elements. This is used quite a bit in the examples here. ----------------------------------------------------- Using Arrays With Visual dBASE User Interface Objects ----------------------------------------------------- If you have worked with interface objects at all (any object you place on a form is an interface object), you have probably at least looked at comboboxes, listboxes, and tabboxes. Each of these needs to have a datasource property -- this is where the lists come from. One option for a datasource is an array. The purpose of this HOW TO is to discuss arrays, so if you do not know how to use a Combobox, Listbox, or TabBox, then you may want to stop here, as I will not be discussing these in much detail, except where the datasource property comes into play. Comboboxes and Listboxes ------------------------ Comboboxes and Listboxes can use arrays as the datasource. One caveat -- if you try to use a multi-dimensional or multi-column array, you may find your lists looking a bit strange. Example: Column 1 Column 2 Row 1 Sydney 1,000,000 Row 2 San Francisco 4,000,000 Row 3 Miami 8,000,000 Row 4 Los Angeles 10,000,000 Row 5 New York 25,000,000 If you set your datasource property to read: "ARRAY aCity", where "aCity" is an array that looks like the above, what you will get in your combobox or listbox display is: Sydney 1,000,000 San Francisco 4,000,000 Miami 8,000,000 Los Angeles 10,000,000 New York 25,000,000 The chances are, this is not what you wanted to happen. (A possible solution is to copy just the first column of the array into another array ...) In addition, one problem that sometimes drives developers a bit crazy is, "Where do you define your array so that the forms designer doesn't kill it, and it is available when you run the form?" The reason this question comes up is that when you are designing a form, if the array does not exist, the Forms Designer will allow you to continue. However, if you run the form without the array in memory, you will get an error. So, where do you want to define your array? ------------------------------------------- A lot depends on where you are getting the data for the array. If the array is a hard-coded array (meaning that the values in it never change), and the only use you have for the array is the list (meaning you do not need to perform any lookups in the array in any other code in your form), you should use a literal array. If the array is coming from a field in a table, you should define this information when the form opens (using the OnOpen method). The Literal Array ----------------- You can define your literal array either by hand, or by using the built-in visual array designer. To do it by hand, in the DataSource property, type: ARRAY {"San Francisco","Los Angeles", etc.} To use the visual array designer, click on the tool button by the DataSource Property. This will bring up the designer. This is so easy to use that there is no real need to discuss it -- the buttons and lists are very straight-forward. The one drawback to a literal array defined in this fashion is, there is _NO_ name for the array. If, for any reason, you need to look at the contents, it will take some extra work on your part (one method is to actually have dBASE show the value of the datasource: form.combobox1.datasource -- you can store this into a character memory variable and parse through it, but it's not the most efficient method of working with an array). If you _do_ need to use the datasource array elsewhere in your code, do _NOT_ use a literal array. Defining a DataSource Without Using a Literal Array --------------------------------------------------- If you do not want to (or decide it is not in your best interests to) use a literal array, then where and how do you define your array for your datasource? Two places immediately jump to mind: In the Form's OnOpen Method In the Object's OnOpen Method In either case, it is a good idea to make the array a custom property of either the form or the object. Otherwise, as soon as you leave the method, the array is no longer in scope (you will get errors that claim the array does not exist) -- of course you could make the array "PUBLIC", but this causes problems, such as remembering when you close the form to release your array(s). -------------------------------------------------------------------- NOTE: You may want to download FORMVAR.HOW as a source of more information on using these methods for a form, and for a form's objects ... -------------------------------------------------------------------- To do this, when you instantiate the array, you simply add the object reference to the array. Remember -- an array is just an object, but in this case, you can make the array a property of another object as well! form.aCity = new array() or form.ComboBox1.aCity = new array() The only drawback is that you must refer to the full "path" to the array any time you need to reference it. The advantage is, you do not need to make this a public array (PUBLIC aCity), and do not need to worry about releasing the array from memory when you are done with it. When you close the form, the array object will disappear as well, since it is a property of the form -- this makes managing memory a snap! Once you have instantiated the array, you can then add elements to it as you need to (this is just one example -- see earlier in this HOW TO for lots of other methods of filling an array): form.aCity.Add("San Francisco") form.aCity.Add("Los Angeles") form.aCity.Add("Miami") form.aCity.Add("New York") form.aCity.Add("Sydney") When you do this, do _not_ set the datasource until after you have filled the array. Then add the statement: form.Combobox1.Datasource = "ARRAY form.aCity" or form.Combobox1.Datasource = "ARRAY form.Combobox1.aCity" What Happens If the Array Changes? ---------------------------------- In some processing that takes place, the contents of an array may change (see MOVERW.ZIP in library 7 of the VDBASE forum on Compuserve). Visual dBASE does not always display the changes, and this can be rather confusing. The simple fix is to add the following statement after you have updated the contents of the array: form.Combobox1.Datasource = form.Combobox1.Datasource This forces dBASE to update the datasource itself, and this will cause the display to update as well. TabBoxes -------- TabBoxes, while being a rather useful tool for multi-page forms, do not require a complex array design for the datasource. All you really need is a literal array, unless for some reason you need to refer to the contents of the array later. Most of the time, this is not needed. However, a recent posting on the VDBASE forum by a fellow TeamBer showed me a rather simple and elegant method of using a literal array for the datasource for the TabBox control, and being able to use the text of the currently selected tab. Try the following in the code associated with the TabBox's OnSelChange method: PROCEDURE TabBox1_OnSelChange *-- if doing multiple pages: form.PageNo = this.CurSel *-- store the literal array into a temporary array, *-- removing the word "array" from the datasource ...: aTabArray = right(this.datasource,aLen(this.datasource)-6)) *-- store the current tabbox text to a custom property *-- of the tabbox control: this.TabText = &aTabArray.[this.CurSel] If you need to know the text at some point, it will be contained in this custom property. An example or test would be to add to this code: msgbox(this.TabText) Which would show the contents of the current tab's text ... -------------------------------------------------------------------- DISCLAIMER: the author is a member of TeamB for dBASE, a group of volunteers who provide technical support for Borland on the DBASE and VDBASE forums on Compuserve. If you have questions regarding this .HOW document, or about dBASE/DOS or Visual dBASE, you can communicate directly with the author and TeamB in the appropriate forum on CIS. Technical support is not currently provided on the World-Wide Web, via the Internet or by private E-Mail on CIS by members of TeamB. .HOW files are created as a free service by members of TeamB to help users learn to use Visual dBASE more effectively. They are posted first on the Compuserve VDBASE forum, edited by both TeamB members and Borland Technical Support (to ensure quality), and then may be cross-posted to Borland's WWW Site. This .HOW file MAY NOT BE POSTED ELSEWHERE without the explicit permission of the author. Copyright 1996, Kenneth J. Mayer. All rights reserved. -------------------------------------------------------------------- *-------------------------------------------- *-- End of Text: What follows is sample code: *-------------------------------------------- *-----------8<---------- Cut Here-------- >8--------------------------* *---------------------------------------------------------------------- *-- Program...: SHOW1.PRG *-- Programmer: Ken Mayer (CIS: 71333,1030) *-- Date......: 11/13/1995 *-- Notes.....: Designed to show the contents of a one-column array *---------------------------------------------------------------------- parameter cArrayName nLen = aLen(&cArrayName.,1) for nRow = 1 to nLen ? "Element "+ltrim(str(nRow))+" - " ?? &cArrayName.[nRow] && continue, same line next *-- EoP: SHOW1.PRG *-----------8<---------- Cut Here-------- >8--------------------------* *---------------------------------------------------------------------- *-- Program...: SHOW2.PRG *-- Programmer: Ken Mayer (CIS: 71333,1030) *-- Date......: 11/15/1995 *-- Notes.....: Designed to show the contents of a multi-column array. *-- Do _NOT_ use this on a single-dimensional array -- *-- the variable nColumns will contain a value of '0', *-- and your output will be useless. Use SHOW1.PRG instead. *---------------------------------------------------------------------- parameter cArrayName nRows = aLen(&cArrayName.,1) nColumns = aLen(&cArrayName.,2) for nColumn = 1 to nColumns ?? "Column "+ltrim(str(nColumn)) at (20*(nColumn-1))+7 next ? for nRow = 1 to nRows ? "Row "+ltrim(str(nRow)) for nColumn = 1 to nColumns ?? &cArrayName.[nRow,nColumn] at (20*(nColumn-1))+7 next ? next *-- EoP: SHOW2.PRG *-----------8<---------- Cut Here-------- >8--------------------------* *-- EoHT: ARRAY2.HOW -- 03/19/1996 -- Ken Mayer