I am writing this, and a companion article, in reponse to questions I received after last months Xbasic CliniX article.
It is my hope that the Xbasic CliniX series will be driven by you, the readers.
Please do not hesitate to drop me an email with suggestions for future articles, or clarifications on current material.
The first question came to me from my good friend Tom Patten. He wrote as follows:
"The main problem for me is refreshing the browse when you enter records in a
dialog. I've eliminated as many dialog forms as I can. But I'd prefer to have them work. Fetching First and Last works, but it would be nice to end up on the record in the browse that you just entered or edited. "
Refreshing the browse can, at times, be problematic when 'going behind the scenes'
using table pointers. This is rarely the case with the Table.current() and Table.get() methods. It can be more of an issue when using the Table.open.
I will expand upon this issue by referring to the AlphaSports Application that
ships with Alpha Five version 5. Last month I used the Invoice_Items table as the guinea pig, writing records to this table using the three methods mentioned above. The Invoice_Items table is a simple table and in a real world situation, it would make an excellent candidate for writing records to it via form methods. Many times, however, we need to deal with tables having many more fields. A form, as opposed to a browse, is many times a more appropriate medium for entering data in more complex tables. In the specific example of the Invoice_Items table there is an additional concern. There is a table lookup field rule defined for the Product_ID field. Entering a new record directly into the form, via Xbasic, will trigger this field rule lookup, complicating the process.
With this as background, let us proceed to develop a solution to Tom's desire to
see the browse focus on the new record added via Xbasic using table pointers. I'll start with the following script from last months article utilizing the 'Table.get()' method:
T = table.get("Invoice_Items")
Vmode = t.mode_get()
Select
Case vMode = 1
t.change_end()
t.enter_begin()
Case vMode = 2
t.enter_end()
t.enter_begin()
Case else
t.enter_begin()
End select
t.product_id = "Jims Stuff"
t.price = 10.05
t.quantity = 2
t.enter_end()
parentform.resynch()
Let's take a moment and look at the last command in this script,
'parentform.resynch()'. A pointer to a table gotten using the table.get() or table.current() commands, is a pointer to an already open instance of the table. As such, we must remember that both the Xbasic script AND the form that spawned the table are referencing the same instance of the table. When we manipulate the table pointer via Xbasic, the form knows nothing of this. If, after moving the record pointer via Xbasic, focus is put back on the form, the form will still show the same record as it did before the Xbasic script began. The form internally, keeps track of what record it is displaying. If we then navigate to the next record using the form, the underlying table record pointer is resynched to the form and advanced to the next record. Hence, in the above script, we have created a new record using the table pointer returned by the table.get() command. The parentform.resynch() command synchronizes the form to the record pointer currently being manipulated by the Xbasic script. In other words, the form shifts to the new record we just created in response to the parentform.resynch() command.
Cosmetically, the problem with this is that the browse scrolls to the point that
only the new record is showing, all previous records are scrolled up out of sight.
Tom wants the browse to show the other records, with the new record, having current
focus, at the bottom of the browse. To do this, I will add one line of Xbasic just before the parentform.resynch() command, and four more lines following the parentform.resynch() command. The script now looks like this:
T = table.get("Invoice_Items")
Vmode = t.mode_get()
Select
Case vMode = 1
t.change_end()
t.enter_begin()
Case vMode = 2
t.enter_end()
t.enter_begin()
Case else
t.enter_begin()
End select
t.product_id = "Jims Stuff"
t.price = 10.05
t.quantity = 2
t.enter_end()
vrec = t.recno()
parentform.resynch()
parentform:browse1.fetch_first()
while .not. t.fetch_eof() .and. t.recno() <> vrec
parentform:browse1.fetch_next()
end while
The first added line of Xbasic, vrec = t.recno(), gets the record number of the
just added record. It stores the record number in the variable, vrec.
The next added line of Xbasic, parentform:browse1.fetch_first(), puts focus on the
first record in the Invoice_Items browse. Notice here that we are now manipulating the form, we are not going directly to the table. Then I use a 'while……… end while' loop to step focus through the records in the browse until I get to the new record just added. This would be similar to a user pressing the down arrow key on the keyboard. This loop stops when either the correct record number is found or the last record in the browse is reached. This script produces the following output:
The exact same method can be used with a script that uses the table.open() method.
Before we leave this example I want to point out some implications of this script.
If we had a large number of records in the browse, this script may not create an acceptable visual presentation. You would see the records scrolling through the browse. Issuing a ui_freeze(.t.) command before beginning the while…..end while loop, and a ui_freeze(.f.) after ending the loop, should take care of the scrolling records. In the case of many records in a browse, one might consider starting from the last record and going backwards the appropriate number of records, equal to the lines showing in the browse. Then paging back down to the correct record. The would prevent the unnecessary paging through all records from first to last.
The other issue that I can think of would be a situation where there is a sort order
on the browse other than record number order. If this were the case, the last added record may not be the last record in the browse. In this case you'd either leave the script as we have it, starting at the first record and paging down until the correct record is found, or, if there were many records in the browse, use a 't.fetch_goto(vrec) command to go directly to the desired record, then fetch up the number of records that you browse displays, then back down, to be the desired record with focus at the bottom of the browse.
Email Jim with your
questions and comments