I know hundreds of people have asked similar questions about combo
boxes. There are so many questions and answers that a search of the
Google archives would take days to find anything. So I'm asking here,
even though somebody may have answered this already:

I need to use a drop-down combobox in VFP 7 (8 is not an option) in
the following manner:

RowSourceType = array
Style = Drop-down combo

Users need to be able to add new items to the combo list and have them
added to the array.
Users need to be able to delete items from the list and have them
deleted from the array (and really deleted from the list, so that
there are no blank lines in the drop-down).
Users need to be able to edit existing items in the list without
adding a new item to the list. (A messagebox that asks whether they
want to add a new item or edit the existing one when they change the
DisplayValue is fine.)
This all needs to be done in the display area of the combo box--no
textbox/button set for adding, editing, or deleting.

One major reason for this is to divorce the user interface from the
data for an n-tier design. So substituting SQL SELECT statements or
field lists for the array row source is not an option. Binding the
combobox control source to a table field is also not an option.

Another major issue is screen real estate. That's why I want to use a
combobox instead of a listbox with separate editing controls.

Grids are out for space consideration reasons and because they won't
accept an array RowSourceType.

Apparently AddItem, RemoveItem, and a whole bunch of combobox itemID
and Index properties simply don't work when the row source type is an
array. My main problem, therefore, is that I cannot get a reliable
numerical index value for the combo box item the user is
changing/adding/deleting that corresponds to the proper row number in
the array row source.

Does anybody have any suggestions? Is what I want to do even possible?

Thanks very much in advance.

Ken Dibble

Re: Fully Editable Combo Box with Array RowSourceType? by Anders

Anders
Sat Nov 01 01:32:00 CST 2003

Hi Ken,
As soon as Combo.Value=0 or combo:lostindex = 0 but Combo.text isn't empty
then you can assume that the user has entered or modified listitem. Handle
this directly with the array using DIMENSION, ASORT, ADEL and AINS. Then
call Combo.Requery() and the modified array will be displayed.
I think I'd rather have a readonly Combo.Style=2 (combolist) and a button
the opens a form for adding or deleting items.
-Anders

"Ken Dibble" <balderdash@spongemop.com> wrote in message
news:55c6qvsm0v7o0suir0to2doub0tqo4lvub@4ax.com...
> I know hundreds of people have asked similar questions about combo
> boxes. There are so many questions and answers that a search of the
> Google archives would take days to find anything. So I'm asking here,
> even though somebody may have answered this already:
>
> I need to use a drop-down combobox in VFP 7 (8 is not an option) in
> the following manner:
>
> RowSourceType = array
> Style = Drop-down combo
>
> Users need to be able to add new items to the combo list and have them
> added to the array.
> Users need to be able to delete items from the list and have them
> deleted from the array (and really deleted from the list, so that
> there are no blank lines in the drop-down).
> Users need to be able to edit existing items in the list without
> adding a new item to the list. (A messagebox that asks whether they
> want to add a new item or edit the existing one when they change the
> DisplayValue is fine.)
> This all needs to be done in the display area of the combo box--no
> textbox/button set for adding, editing, or deleting.
>
> One major reason for this is to divorce the user interface from the
> data for an n-tier design. So substituting SQL SELECT statements or
> field lists for the array row source is not an option. Binding the
> combobox control source to a table field is also not an option.
>
> Another major issue is screen real estate. That's why I want to use a
> combobox instead of a listbox with separate editing controls.
>
> Grids are out for space consideration reasons and because they won't
> accept an array RowSourceType.
>
> Apparently AddItem, RemoveItem, and a whole bunch of combobox itemID
> and Index properties simply don't work when the row source type is an
> array. My main problem, therefore, is that I cannot get a reliable
> numerical index value for the combo box item the user is
> changing/adding/deleting that corresponds to the proper row number in
> the array row source.
>
> Does anybody have any suggestions? Is what I want to do even possible?
>
> Thanks very much in advance.
>
> Ken Dibble


Re: Fully Editable Combo Box with Array RowSourceType? by Ken

Ken
Sat Nov 01 21:42:21 CST 2003

Thanks, Anders.

I wasn't having a problem determining when a user had modified a list
item. I was having trouble getting a numeric index number for that
item. The problem was that ListIndex isn't static when a user starts
typing in something new. I had to capture the value of ListIndex in
InteractiveChange() on the first keystroke like so:

IF SomeProperty = 0
SomeProperty = THIS.ListIndex
ENDIF

After that first keystroke, ListIndex becomes 0 and it's too late to
use it for determining which array row to modify.

Once I figured that out, I had half the problem solved. The other half
was figuring out when and where to reset SomeProperty to 0 after I'd
handled the array manipulation.

There was a lot of funky stuff to get the combobox to work more
intuitively for users, too.

I've now got a little working prototype program that models a fully
editable dropdown combobox in an n-tier system. I didn't post it
because it's pretty long and may not be of any interest.

I agree with you: It would be easier to use a dropdown-list-style
combo and pop up a little modal form to let users enter stuff into the
list--and I will do that for multi-column lists--but it seemed to me
that doing this for a one-column list would be unnecessarily
inconvenient for users.

I don't really have any good data on which to base that assumption,
though. Do you know any good sources on user interface design? I don't
need code examples; I'm looking for examples of how to organize an
interface system. I don't get to see a wide variety of applications,
so I don't have a good sense of what interface designs are most
intuitive and easy to use for end-users. I'd like to see some
interface layouts and discussions of principles for ease and
efficiency of use.

Thanks again.

Ken

On Sat, 1 Nov 2003 08:32:00 +0100, "Anders Altberg"
<x_pragma@telia.com> wrote:

>Hi Ken,
>As soon as Combo.Value=0 or combo:lostindex = 0 but Combo.text isn't empty
>then you can assume that the user has entered or modified listitem. Handle
>this directly with the array using DIMENSION, ASORT, ADEL and AINS. Then
>call Combo.Requery() and the modified array will be displayed.
>I think I'd rather have a readonly Combo.Style=2 (combolist) and a button
>the opens a form for adding or deleting items.
>-Anders
>
>"Ken Dibble" <balderdash@spongemop.com> wrote in message
>news:55c6qvsm0v7o0suir0to2doub0tqo4lvub@4ax.com...
>> I know hundreds of people have asked similar questions about combo
>> boxes. There are so many questions and answers that a search of the
>> Google archives would take days to find anything. So I'm asking here,
>> even though somebody may have answered this already:
>>
>> I need to use a drop-down combobox in VFP 7 (8 is not an option) in
>> the following manner:
>>
>> RowSourceType = array
>> Style = Drop-down combo
>>
>> Users need to be able to add new items to the combo list and have them
>> added to the array.
>> Users need to be able to delete items from the list and have them
>> deleted from the array (and really deleted from the list, so that
>> there are no blank lines in the drop-down).
>> Users need to be able to edit existing items in the list without
>> adding a new item to the list. (A messagebox that asks whether they
>> want to add a new item or edit the existing one when they change the
>> DisplayValue is fine.)
>> This all needs to be done in the display area of the combo box--no
>> textbox/button set for adding, editing, or deleting.
>>
>> One major reason for this is to divorce the user interface from the
>> data for an n-tier design. So substituting SQL SELECT statements or
>> field lists for the array row source is not an option. Binding the
>> combobox control source to a table field is also not an option.
>>
>> Another major issue is screen real estate. That's why I want to use a
>> combobox instead of a listbox with separate editing controls.
>>
>> Grids are out for space consideration reasons and because they won't
>> accept an array RowSourceType.
>>
>> Apparently AddItem, RemoveItem, and a whole bunch of combobox itemID
>> and Index properties simply don't work when the row source type is an
>> array. My main problem, therefore, is that I cannot get a reliable
>> numerical index value for the combo box item the user is
>> changing/adding/deleting that corresponds to the proper row number in
>> the array row source.
>>
>> Does anybody have any suggestions? Is what I want to do even possible?
>>
>> Thanks very much in advance.
>>
>> Ken Dibble


Re: Fully Editable Combo Box with Array RowSourceType? by Roger

Roger
Sun Nov 02 12:10:11 CST 2003

Ken Dibble <balderdash@spongemop.com> wrote:
> I know hundreds of people have asked similar questions about combo
> boxes. There are so many questions and answers that a search of the
> Google archives would take days to find anything. So I'm asking here,
> even though somebody may have answered this already:
>
> I need to use a drop-down combobox in VFP 7 (8 is not an option) in
> the following manner:
>
> RowSourceType = array
> Style = Drop-down combo


OK ... I've fiddled with this recently in a different scenario but maybe
something like this will get you started.
It's far from complete!
(Paste the code in a prg and run it).

*!*Start combotest.prg
oForm=CREATEOBJECT("Myform")
oForm.Visible=.T.
READ EVENTS

DEFINE CLASS MyForm as Form
Windowtype=1
AutoCenter=.T.
PROCEDURE Init
This.AddObject("cbo1","mycombo")
This.AddObject("txt1","Textbox")
This.txt1.Top=This.cbo1.Top+30
This.cbo1.Visible=.T.
This.txt1.Visible=.T.
ENDPROC

PROCEDURE Unload
CLEAR Events
ENDPROC
ENDDEFINE

DEFINE CLASS mycombo AS ComboBox
DIMENSION myarray[1,2]
style=0
LastId=0
LastDispVal=""

PROCEDURE Init
LOCAL i,lnId
lnId=64
FOR i = 1 TO 10
lnId=lnId+1
This.LastId=This.LastId+1
DIMENSION This.myarray(i,2)
AINS(This.myarray,1)
This.myarray(1,1)=CHR(lnId)
This.myarray(1,2)=This.LastId
NEXT
This.rowsourcetype=5
This.rowsource="This.myarray"
This.boundcolumn=1
This.Requery
This.DisplayValue=This.myarray[1,1]
ENDPROC

PROCEDURE Valid
LOCAL lnElement,lnRows
lnRows=This.ListCount
lnElement=ASCAN(This.myarray,This.DisplayValue,1,lnRows,1,7)
DO CASE
CASE !EMPTY(This.DisplayValue) AND lnElement=0
lnRows=lnRows+1
DIMENSION This.myarray(lnRows,2)
AINS(This.myarray,1)
This.myarray[1,1]=This.DisplayValue
This.LastId=This.LastId+1
This.myarray[1,2]=This.LastId
This.Requery

CASE EMPTY(This.DisplayValue) AND ! EMPTY(This.LastDispVal)
lnResponse=MESSAGEBOX("Delete this item?",4+32,"Confirm")
IF lnResponse=6
lnElement=ASCAN(This.myarray,This.LastDispVal,1,lnRows,1,7)
ADEL(This.myarray,lnElement)
DIMENSION This.myarray(This.ListCount-1,2)
This.Requery
This.DisplayValue=This.myarray[1,1]
ENDIF
ENDCASE
ENDPROC

PROCEDURE GotFocus
This.LastDispVal=This.DisplayValue
ENDPROC
ENDDEFINE

*!* End combotest.prg

[snipped rest]
--
Roger Ansell
Australian President
FE AVP et Cie
Adelaide, Australia

My real email address is ransell at senet dot com dot au



Re: Fully Editable Combo Box with Array RowSourceType? by Ken

Ken
Sun Nov 02 21:28:46 CST 2003


>OK ... I've fiddled with this recently in a different scenario but maybe
>something like this will get you started.
>It's far from complete!

Thanks very much, Roger.

Your use of ASCAN() gave me an idea I hadn't even thought
of--detecting duplicate entries and giving the user a choice of
avoiding them. My solution doesn't do that all the time--it won't
detect a duplicate when typed in over an existing entry. That's
something that confuses me about comboboxes anyway--if you enter
something, you'd expect it to remain in the display. But if that
happens, you no longer have a blank display in which to enter the next
thing.

Anyway, here's where I'm at with it. Perhaps you'll find something you
can use here.

Ken

*** EDITABLE N-TIER DROP-DOWN COMBOBOX ***

oForm = CREATEOBJECT("Form")

oForm.Height = 460

oForm.AutoCenter = .T.

oForm.AddObject("TopLabel","MyLabel")

oForm.TopLabel.Caption = "FULLY EDITABLE N-TIER DROPDOWN COMBOBOX"
oForm.TopLabel.Width = 280
oForm.TopLabel.FontBold = .T.
oForm.TopLabel.Top = 3
oForm.TopLabel.Left = 50
oForm.TopLabel.Visible = .T.

oForm.AddObject("oData","mydata")

oForm.AddObject("Combo1","EditableCombo")

oForm.Combo1.Left = 5
oForm.Combo1.Top = 46
oForm.Combo1.Width = 100
oForm.Combo1.Visible = .T.

oForm.AddObject("Label1","MyLabel")

oForm.Label1.Caption = "States"
oForm.Label1.Top = 28
oForm.Label1.Left = 5
oForm.Label1.Visible = .T.

oForm.AddObject("Button1","SaveButton")

oForm.Button1.Top = 46
oForm.Button1.Left = 115
oForm.Button1.Visible = .T.

oForm.AddObject("SaveLabel","MyLabel")

oForm.SaveLabel.Caption = "<-- Saves changes to the underlying table"
oForm.SaveLabel.FontSize = 8
oForm.SaveLabel.Width = 204
oForm.SaveLabel.Top = 48
oForm.SaveLabel.Left = 170
oForm.SaveLabel.Visible = .T.

oForm.AddObject("Button2","NewButton")

oForm.Button2.Top = 80
oForm.Button2.Left = 115
oForm.Button2.Visible = .T.

oForm.AddObject("NewLabel","MyLabel")

oForm.NewLabel.Caption = "<-- Reconstructs original data"
oForm.NewLabel.FontSize = 8
oForm.NewLabel.Width = 204
oForm.NewLabel.Top = 82
oForm.NewLabel.Left = 170
oForm.NewLabel.Visible = .T.

oForm.AddObject("Shape2","Shape")

oForm.Shape2.SpecialEffect = 1
oForm.Shape2.BackColor = 16777215
oForm.Shape2.Width = 360
oForm.Shape2.Height = 157
oForm.Shape2.Left = 8
oForm.Shape2.Top = 112
oForm.Shape2.ZOrder(1)
oForm.Shape2.Visible = .T.

oForm.AddObject("InstructLabel1","MyLabel")

oForm.InstructLabel1.WordWrap = .T.
oForm.InstructLabel1.Width = 348
oForm.InstructLabel1.Height = 100
oForm.InstructLabel1.Top = 115
oForm.InstructLabel1.Left = 15
oForm.InstructLabel1.Caption = "You can enter a new item, edit an
existing item, or " ;
+ "delete an item from the combobox." ;
+ CHR(13) + CHR(13) ;
+ "If you type into the combobox when an
item is already " ;
+ "displayed, and press ENTER or TAB or
click the mouse, you'll be asked"
oForm.InstructLabel1.Visible = .T.

oForm.AddObject("InstructLabel2","MyLabel")

oForm.InstructLabel2.WordWrap = .T.
oForm.InstructLabel2.Width = 348
oForm.InstructLabel2.Height = 100
oForm.InstructLabel2.Top = 190
oForm.InstructLabel2.Left = 15
oForm.InstructLabel2.Caption = "if you want to create a new item.
Saying 'No' adds what you " ;
+ "typed to the displayed item. If the
combobox display is empty, " ;
+ "whatever you type is added as a new
item." ;
+ CHR(13) + CHR(13) ;
+ "CTRL+DEL deletes the displayed or
selected item from the list."
oForm.InstructLabel2.Visible = .T.

oForm.AddObject("Shape3","Shape")

oForm.Shape3.SpecialEffect = 1
oForm.Shape3.BackColor = 205
oForm.Shape3.Width = 360
oForm.Shape3.Height = 70
oForm.Shape3.Left = 8
oForm.Shape3.Top = 277
oForm.Shape3.ZOrder(1)
oForm.Shape3.Visible = .T.

oForm.AddObject("ProblemLabel","MyLabel")

oForm.ProblemLabel.ForeColor = 16777215
oForm.ProblemLabel.WordWrap = .T.
oForm.ProblemLabel.Width = 350
oForm.ProblemLabel.Height = 140
oForm.ProblemLabel.Top = 282
oForm.ProblemLabel.Left = 15
oForm.ProblemLabel.Caption = "The combo can't warn a user who types a
duplicate entry in over " ;
+ "an existing item, but it won't add the
duplicate either. Also, " ;
+ "if the user presses CTRL+DEL to delete
an item while the dropdown " ;
+ "is open, a blank line will appear in
the dropdown."
oForm.ProblemLabel.Visible = .T.

oForm.AddObject("ExplainLabel1","MyLabel")

oForm.ExplainLabel1.WordWrap = .T.
oForm.ExplainLabel1.Width = 350
oForm.ExplainLabel1.Height = 140
oForm.ExplainLabel1.Top = 360
oForm.ExplainLabel1.Left = 15
oForm.ExplainLabel1.Caption = "This system uses a data object to
manage retrieving " ;
+ "data from, and saving data to, the
table. Data shown " ;
+ "in the combobox is stored in an array
and manipulated " ;
+ "using array functions."
oForm.ExplainLabel1.Visible = .T.

oForm.AddObject("Shape1","Shape")

oForm.Shape1.SpecialEffect = 0
oForm.Shape1.Width = 360
oForm.Shape1.Height = 100
oForm.Shape1.Left = 8
oForm.Shape1.Top = 355
oForm.Shape1.ZOrder(1)
oForm.Shape1.Visible = .T.

oForm.AddObject("ExplainLabel2","MyLabel")

oForm.ExplainLabel2.WordWrap = .T.
oForm.ExplainLabel2.Width = 350
oForm.ExplainLabel2.Height = 140
oForm.ExplainLabel2.Top = 405
oForm.ExplainLabel2.Left = 15
oForm.ExplainLabel2.Caption = "Virtually all of this is " ;
+ "encapsulated within the combobox
class. Data is " ;
+ "tranferred between the combobox and
the data object " ;
+ "only when the SAVE or NEW buttons are
pressed."
oForm.ExplainLabel2.Visible = .T.

oForm.Show(1)

DEFINE CLASS EditableCombo AS ComboBox
Style = 0
OldListIndex = 0
OldDisplayValue = ""

PROCEDURE Init
THIS.AddProperty("aListItems[1,3]")

THIS.RowSourceType = 5

THIS.RowSource = "THIS.aListItems"

THIS.FillSourceArray()
ENDPROC

PROCEDURE FillSourceArray
LOCAL x

THIS.aListItems = ""

DIMENSION
THIS.aListItems(ALEN(THIS.PARENT.oData.aBusiness,1),3)

FOR x = 1 TO ALEN(THIS.PARENT.oData.aBusiness,1)
THIS.aListItems(x,1) = THIS.PARENT.oData.aBusiness(x,1)
THIS.aListItems(x,2) = THIS.PARENT.oData.aBusiness(x,2)
THIS.aListItems(x,3) = THIS.PARENT.oData.aBusiness(x,3)
ENDFOR

THIS.Requery()

*THIS.DisplayValue = THIS.aListItems(1,1)
ENDPROC

PROCEDURE InteractiveChange
IF THIS.OldListIndex = 0
THIS.OldListIndex = THIS.ListIndex
ENDIF
ENDPROC

PROCEDURE Valid
LOCAL theresult

theresult = .T.

IF NOT THIS.Value == THIS.DisplayValue
IF NOT EMPTY(THIS.DisplayValue) AND THIS.OldListIndex
> 0
IF MESSAGEBOX("New list item?",36,"") = 7
theresult = THIS.EditListItem()
ELSE
theresult = THIS.AddNewListItem()
ENDIF
ELSE
IF THIS.OldListIndex > 0
theresult = THIS.DeleteListItem()
ELSE
theresult = THIS.AddNewListItem()
ENDIF
ENDIF
ENDIF

THIS.OldListIndex = 0

RETURN theresult
ENDPROC

PROTECTED PROCEDURE EditListItem
LOCAL searchresult

searchresult =
ASCAN(THIS.aListItems,THIS.DisplayValue,1,THIS.ListCount,1,7)

IF searchresult > 0
IF MESSAGEBOX(ALLTRIM(THIS.DisplayValue) + " is already
in the list." + CHR(13) + CHR(13) ;
+ "Add it again anyway?",36,"") = 7

THIS.DisplayValue = THIS.Value

RETURN .F.
ENDIF
ENDIF

THIS.aListItems(THIS.OldListIndex,1) = THIS.DisplayValue

ASORT(THIS.aListItems,1)

THIS.Requery()

THIS.Value = THIS.DisplayValue

RETURN .T.
ENDPROC

PROTECTED PROCEDURE AddNewListItem
LOCAL searchresult, newvalue

IF EMPTY(THIS.DisplayValue)
RETURN .T.
ENDIF

searchresult =
ASCAN(THIS.aListItems,THIS.DisplayValue,1,THIS.ListCount,1,7)

IF searchresult > 0
IF MESSAGEBOX(ALLTRIM(THIS.DisplayValue) + " is already
in the list." + CHR(13) + CHR(13) ;
+ "Add it again anyway?",36,"") = 7

THIS.DisplayValue = ""

THIS.Value = THIS.DisplayValue

RETURN .F.
ENDIF
ENDIF

DIMENSION THIS.aListItems((ALEN(THIS.aListItems,1) + 1), 3)

newvalue = THIS.DisplayValue

AINS(THIS.aListItems,1)

THIS.aListItems(1,1) = THIS.DisplayValue

ASORT(THIS.aListItems,1)

THIS.Requery()

THIS.DisplayValue = newvalue

THIS.Value = THIS.DisplayValue

RETURN .T.
ENDPROC

PROTECTED PROCEDURE DeleteListItem
IF EMPTY(THIS.OldListIndex)
RETURN .T.
ENDIF

ADEL(THIS.aListItems,THIS.OldListIndex)

DIMENSION THIS.aListItems((ALEN(THIS.aListItems,1) - 1), 3)

ASORT(THIS.aListItems,1)

THIS.Requery()

THIS.Value = THIS.DisplayValue

RETURN .T.
ENDPROC

PROCEDURE SaveData
** This method transfers data from the combobox rowsource
array
** to the corresponding array in the Data Object. This is a
rough
** demo of the concept. The system I'm developing is much
more
** robustly automated. It will have a Data Manager to create
and
** control the Data Object, and it will use a separate
Business
** Object to handle validation of user input and mediate
between
** the user interface and the Data Manager.

LOCAL x

THIS.PARENT.oData.aBusiness = ""

DIMENSION
THIS.PARENT.oData.aBusiness(ALEN(THIS.aListItems,1),3)

FOR x = 1 TO ALEN(THIS.aListItems,1)
THIS.PARENT.oData.aBusiness(x,1) = THIS.aListItems(x,1)
THIS.PARENT.oData.aBusiness(x,2) = THIS.aListItems(x,2)
THIS.PARENT.oData.aBusiness(x,3) = THIS.aListItems(x,3)
ENDFOR

THIS.PARENT.oData.SaveData()

THIS.FillSourceArray()
ENDPROC

PROCEDURE KeyPress
** Lets users delete a displayed item from the list by
pressing CTRL+DEL.
LPARAMETERS nKeyCode, nShiftAltCtrl

IF nKeyCode = 147
* This gets us a list index number for the item to
delete.
THIS.InteractiveChange()

THIS.DeleteListItem()

* This was added to prevent a second item from being
deleted
* even though the display was empty, if the user
pressed CTRL+DEL
* again after deleting an item.
THIS.OldListIndex = 0
ENDIF
ENDPROC
ENDDEFINE

DEFINE CLASS SaveButton AS CommandButton
Width = 50
Height = 23
Caption = "SAVE"

PROCEDURE Click
THIS.PARENT.Combo1.SaveData()
ENDPROC
ENDDEFINE

DEFINE CLASS NewButton AS CommandButton
Width = 50
Height = 23
Caption = "NEW"

PROCEDURE Click
THIS.PARENT.oData.ReconstructData()
THIS.PARENT.Combo1.FillSourceArray()
THIS.Parent.Combo1.DisplayValue = ""
ENDPROC
ENDDEFINE

DEFINE CLASS mydata AS Custom
PROCEDURE Init
THIS.CheckForData()

THIS.AddProperty("aBusiness[1,3]")

THIS.FillBusinessArray()
ENDPROC

PROCEDURE CheckForData
IF NOT FILE("testtable.dbf")
SELECT 0

CREATE TABLE TestTable ( ;
Statename C(20), ;
StateID I, ;
ForKey I)

INSERT INTO TestTable (Statename, StateID, ForKey) ;
VALUES ("Virginia", 1, 20)

INSERT INTO TestTable (Statename, StateID, ForKey) ;
VALUES ("Maryland", 2, 30)

INSERT INTO TestTable (Statename, StateID, ForKey) ;
VALUES ("Ohio", 3, 40)
ENDIF
ENDPROC

PROCEDURE FillBusinessArray
THIS.aBusiness = ""

DIMENSION THIS.aBusiness(1,3)

SELECT * ;
FROM TestTable ;
INTO ARRAY THIS.aBusiness ;
WHERE NOT DELETED() ;
ORDER BY Statename
ENDPROC

PROCEDURE SaveData
THIS.SaveArrayToTable()

THIS.FillBusinessArray()
ENDPROC

PROTECTED PROCEDURE SaveArrayToTable
LOCAL x, foundit

STORE ALEN(THIS.aBusiness,1) TO rowcount

IF NOT USED("TestTable")
SELECT 0
USE TestTable
ELSE
SELECT TestTable
ENDIF

FOR x = 1 TO rowcount
IF EMPTY(THIS.aBusiness(x,2))
THIS.aBusiness(x,2) = THIS.GetNewKey()

APPEND BLANK

REPLACE StateID WITH THIS.aBusiness(x,2)
ENDIF

GO TOP

LOCATE FOR NOT DELETED() AND StateID =
THIS.aBusiness(x,2)

IF FOUND()
REPLACE StateName WITH THIS.aBusiness(x,1)
ENDIF
ENDFOR

SCAN FOR NOT DELETED()
x = 1

foundit = .T.

FOR x = 1 TO rowcount
IF StateID = THIS.aBusiness(x,2)
foundit = .T.

EXIT
ELSE
foundit = .F.
ENDIF
ENDFOR

IF foundit = .F.
DELETE
ENDIF
ENDSCAN
ENDPROC

PROTECTED PROCEDURE GetNewKey
RETURN (RECCOUNT() + 1)
ENDPROC

PROCEDURE KillData
IF USED("TestTable")
SELECT TestTable
USE
ENDIF

ERASE TestTable.dbf
ENDPROC

PROCEDURE ReconstructData
THIS.KillData()

THIS.CheckForData()

THIS.FillBusinessArray()
ENDPROC

PROCEDURE Destroy
IF MESSAGEBOX("Delete test data?",36,"") = 6
THIS.KillData()
ENDIF
ENDPROC
ENDDEFINE

DEFINE CLASS MyLabel AS Label
BackStyle = 0
ForeColor = 0
ENDDEFINE

*** END CODE ***