Microsoft Corporation Technical Documentation License Agreement (Standard) 
READ THIS!  This is a legal agreement between Microsoft Corporation (“Microsoft”) and the recipient of these materials, whether an individual or an entity (“You”).  BY ACCESSING, USING OR PROVIDING FEEDBACK ON THE ATTACHED MATERIALS (“Materials”), YOU AGREE TO THESE TERMS. 
1.  These Materials are Microsoft confidential information under Your most recent non-disclosure agreement with Microsoft (“Your NDA”).  However, You may use these Materials only as described in Paragraph 2 below. 
2.  You may review these Materials only (a) as a reference to assist You in planning and designing Your product, service or technology (“Product”) to interface with a Microsoft Product as described in these Materials; and (b) to provide feedback on these Materials to Microsoft. All other rights are retained by Microsoft; this agreement does not give You rights under any Microsoft patents. You may not (i) duplicate any part of these Materials, (ii) remove this agreement or any notices from these Materials, or (iii) give any part of these Materials, or assign or otherwise provide Your rights under this agreement, to anyone else. 
3.  These Materials may contain preliminary information or inaccuracies, and may not correctly represent any associated Microsoft Product as commercially released. All Materials are provided entirely “AS IS.”  To the extent permitted by law, MICROSOFT MAKES NO WARRANTY OF ANY KIND, DISCLAIMS ALL EXPRESS, IMPLIED AND STATUTORY WARRANTIES, AND ASSUMES NO LIABILITY TO YOU FOR ANY DAMAGES OF ANY TYPE IN CONNECTION WITH THESE MATERIALS OR ANY INTELLECTUAL PROPERTY IN THEM. 
4.  If You are an entity and (a) merge into another entity or (b) a controlling ownership interest in You changes, Your right to use these Materials automatically terminates and You must destroy them. 
5.  You have no obligation to give Microsoft any suggestions, comments or other feedback (“Feedback”) relating to these Materials.  However, any Feedback you voluntarily provide may be used in Microsoft Products and related specifications or other documentation (collectively, “Microsoft Offerings”) which in turn may be relied upon by other third parties to develop their own Products.  Accordingly, if You do give Microsoft Feedback on any version of these Materials or the Microsoft Offerings to which they apply, You agree: (a) Microsoft may freely use, reproduce, license, distribute, and otherwise commercialize Your Feedback in any Microsoft Offering; (b) You also grant third parties, without charge, only those patent rights necessary to enable other Products to use or interface with any specific parts of a Microsoft Product that incorporate Your Feedback; and (c) You will not give Microsoft any Feedback (i) that You have reason to believe is subject to any patent, copyright or other intellectual property claim or right of any third party; or (ii) subject to license terms which seek to require any Microsoft Offering incorporating or derived from such Feedback, or other Microsoft intellectual property, to be licensed to or otherwise shared with any third party. 
6.  Microsoft has no obligation to maintain confidentiality of any Microsoft Offering, but otherwise the confidentiality of Your Feedback, including Your identity as the source of such Feedback, is governed by Your NDA. 
7.  This agreement is governed by the laws of the State of Washington. Any dispute involving it must be brought in the federal or state superior courts located in King County, Washington, and You waive any defenses allowing the dispute to be litigated elsewhere.  If there is litigation, the losing party must pay the other party’s reasonable attorneys’ fees, costs and other expenses.  If any part of this agreement is unenforceable, it will be considered modified to the extent necessary to make it enforceable, and the remainder shall continue in effect.  This agreement is the entire agreement between You and Microsoft concerning these Materials; it may be changed only by a written document signed by both You and Microsoft.
CursorAdapter Class and Cursor Adapters
Microsoft Confidential.  © 2001 Microsoft Corporation.  All rights reserved. 
FOR DISCLOSURE UNDER NDA ONLY 
These materials are confidential to and maintained as a trade secret by Microsoft Corporation.  Information in these materials is restricted to Microsoft authorized recipients only.  Any use, distribution or public discussion of, and any feedback to, these materials is subject to the terms of the attached license.  By providing any feedback on these materials to Microsoft, you agree to the terms of that license. If the license agreement has been removed, review the terms at http://www.microsoft.com/licensing/specs/agrmt01.asp before using these materials.

1.1	Introduction
1.1.1	Overview
1.1.1.1	New and Enhanced Feature Summary:
Implementing this new functionality requires enhancements to the following product areas:
•	DataEnvironment Class – for details, see DataEnvironmentClass.doc 
•	CursorAdapter Class – A new base class, CursorAdapter, will be created and based on the present Cursor class. This new class will contain the new properties and methods required to support the cursor adapter functionality, and remove properties and methods not required for cursor adapter functionality.
•	Fox Cursors – The internal Fox cursor will be modified to support the new functionality obtained via the CursorAdapter class. The cursor that is opened by the CursorAdapter object will always be a buffered cursor, and will default to Optimistic Row Buffering. 
•	TableUpdate() – The TableUpdate() function will be updated to recognize and handle cursor adapters.
1.2	Class: CursorAdapter 
The CursorAdapter class supports extended functionality in cursor operations. The CursorAdapter class makes it possible to specify or determine properties and to create method and event code that controls the functionality of a native Fox cursor.
1.2.1.1	 CursorAdapter Properties, Events and Methods 
Methods, events and properties will be implemented in the CursorAdapter class to support use of data sources at the DE and CursorAdapter level, and to add support for new cursor adapter functionality.
This table lists the properties, methods, and events specific to the CursorAdapter class.
Name	Remarks	
Properties:		
DataSource  	Specifies data source to use	
DataSourceType 	Specifies the type of the data source.	
UseDeDataSource  	– specifies whether or not the cursor uses the DataSource  of an associated Data Environment class as it’s own data source.	
CursorSchema 	- defines the columns and data types of the CursorAdapter.	
SelectCmd 	- user code that retrieves the data from the data source.	
UpdateCmd 	- user code that updates the data source.	
AllowUpdate	- when true, allows update operations to occur.	
InsertCmd 	- user code that adds new data to the data source.	
AllowInsert	- when true, allows insert operations to occur.	
DeleteCmd 	– user code that deletes data from the data source.	
AllowDelete	- when true, allows delete operations to occur.	
UpdateCmdDataSource 	– specifies the data source for the UpdateCmd	
UpdateCmdSourceType 	– specifies the data source type  for the UpdateCmd	
InsertCmdDataSource 	– specifies the data source for the InsertCmd	
InsertCmdDataSourceType  	- specifies the data source type  for the InsertCmd	
DeleteCmdDataSource 	– specifies the data source for the DeleteCmd	
DeleteCmdSourceType 	– specifies the data source type for the DeleteCmd	
CursorStatus	- specifies the state of the associated cursor.	
DiffGram	- contains the XMLUpdateGram or DiffGram generated for XML updating.	
Flags	- contains any nFlags needed by a particular operation.	
ConversionFunc	- a list of field/function pairs that will be used to convert cursor field values being sent to the data source when automatic updating is used.	
Methods:		
CursorFill	- calls the appropriate command execution method or function, according to the data source type, using the command specified in the SelectCmd property.	
CursorRefresh	- calls the appropriate command execution refresh method or function, according to the data source type.	
CursorAttach	Associates an existing open cursor or table with the CursorAdapter instance.	
CursorDetach	Frees an associated cursor from the CursorAdapter instance.	
AutoOpen	Calls CursorFill() with no parameters. Used by the DataEnvironment class to automatically open CursorAdapters.	
Events:		
Before/AfterCursorFill 	– fires before and after the CursorFill method is called.	
Before/AfterCursorUpdate 	– fires before and after the TableUpdate function is called.	
Before/AfterUpdate 	– fires before and after a CursorUpdate update command execution.	
Before/AfterInsert	– fires before and after a CursorUpdate insert command execution.	
Before/AfterDelete 	– fires before and after a CursorUpdate delete command execution.	

1.2.1.2	CursorAdapter Intrinsic PEM's
This table lists the intrinsic PEM's that will be used by the Cursor class.
Property	Method	Event	
Application	AddProperty	Destroy	
Base Class	ReadExpression	Error	
Class	ReadMethod	Init	
ClassLibrary	ResetToDefault		
Comment	WriteExpression		
Name			
Parent			
ParentClass			
Tag			

1.2.1.3	CursorGet/SetProp Cursor Property changes
The following conventional cursor properties, accessible via CursorGetProp(), have CursorAdapter related changes:
•	SourceType Property – cursors will have SourceType as follows. This value will be set automatically, and be read-only:
•	1 - Specifies that the data source is a local SQL view.
•	If cursor created by CursorFill:  100 + old value (101). 
•	If cursor attached via CursorAttach:  200 + old value (201).
•	 2 – Specifies that the data source is a remote SQL view.
•	If cursor created by CursorFill:  100 + old value (102).
•	If cursor attached via CursorAttach: 200 + old value (202).
•	3 – Specifies that the data source is a table.
•	If cursor created by CursorFill:  100 + old value (103). 
•	If cursor attached via CursorAttach: 200 + old value (203).
•	4 – Specifies that the data source is an ADO Recordset
•	if cursor created by CursorFill: 104
•	if cursor attached via CursorAttach: 204
•	SQL Property
•	For cursors created by CursorAdapter, applies when DataSourceType is NATIVE, ODBC and ADO. If DataSourceType is XML:
•	Error 1468 - Property is invalid for table cursors.
1.2.1.4	CursorGet/SetProp Cursor Property Additions
The following conventional cursor property, accessible via CursorGetProp(), is new to VFP 8.0:
•	ADORecordset - An object reference to the ADO Recordset associated with this cursor, if any. Not valid for cursors not based on ADO Recordsets. Will return error:
•	1467 - Property is invalid for local cursors.
1.2.2	CursorAdapter Class Properties
Note: For Design Time/Run Time details see DataEnvironmentClass.doc
1.2.2.1	Conventional Cursor Properties exposed as Properties in CursorAdapter
Many of the cursor properties that pertain to normal as well as Local and Remote View cursors, i.e.; those properties accessed via CursorGetProp/CursorSetProp will also be properties of the CursorAdapter class, and may be changed via language or Property Sheet. In particular, when using AutoUpdating, the appropriate cursor properties must – i.e.; KeyFieldList, UpdatableFieldList, WhereType, UpdateType, etc. Note that the CursorAdapter properties are always used in operations with the associated cursor. Thus, changing a cursor property via CursorSetProp() will not affect the property of the same name in the CursorAdapter instance, nor will it's value be used.
1.2.2.1.1	Table of Properties and values.
The following table lists the CursorAdapter class properties inherited from cursors, and notes any differences for CursorAdapter. 
Property	eExpression values	
Alias	Defines the alias that will be used for the cursor that is generated by the SelectCmd. 
In order to resolve the correct alias name at design time, this property will be used at design time for tables/fields dragged/dropped to the form from the DE. If no alias is specified, VFP will goof when attempting to add to the DE Designer surface.
At run time, If no alias is specified, then the default alias will be used. User will not be allowed to specify alias via AS in the SELECT command, but must specify it in this property if other than the default alias is desired.	
BufferModeOverride	Default for Cursor adapters is 3 – Optimistic Row Buffering. This means default is always over-riding Forms buffer mode.	
BatchUpdateCount*	The number of update statements sent to the remote data source for buffered tables. 1 is the default. Adjusting this value can greatly increase update performance.	
CompareMemo	.T. - Memo fields (of type Memo, General, or Picture) are included in the WHERE clause for updates .F. – Memo fields are not included in the WHERE clause for updates. Applies when Auto updating is being used.	
FetchAsNeeded	.T. – Records are fetched as needed. .F. – For remote views, the number of records fetched is determined by the MaxRecord property. For local views, all records are fetched.	
FetchMemo*	.T. – Memo fields are fetched with the view results. .F. – Memo fields are not fetched with the view results.	
FetchSize*	Number of rows progressively fetched from the remote table result set. The default is 100 rows. Setting FetchSize to –1 retrieves the complete result set (limited by the MaxRecords setting).	
KeyFieldList	Comma delimited list of primary fields for the cursor. No default. You must include a list of field names for updates to work when using automatic updating.	
MaxRecords*	The maximum number of rows fetched when result sets are returned. The default is – 1 (all rows are returned). A value of 0 specifies that the view is executed but no results are fetched.	
Prepared	Specify true (.T.) to prepare SQL statements for subsequent REQUERY( ) function calls. REQUERY( ) is used to retrieve data again for a SQL view. See SQLPREPARE( ) for additional information about preparing SQL statements. The default is false (.F.).	
SendUpdates	.T. – Specifies that a SQL update query is sent to update tables when an update is made using the view. .F. – Specifies that a SQL update query is not sent to update tables. Note that for CursorAdapter, the default is True (.T.)	
Tables	Comma delimited list of the names of remote tables. No default. You must include a list of table names for updates to work.	
UpdatableFieldList	Comma delimited list of fields in the view. This list can include fields from local and remote tables. You must include a list of fields for updates to work when using automatic updating.	
UpdateNameList	Comma delimited list of field names and the local field names assigned to the cursor. This property must be set for updates to work when using automatic updating. 	
UpdateType	1 – Specifies that old data is updated with the new data (the default). 
2 – Specifies that updates occur by deleting the old data and inserting the new data.	
UseMemoSize*	Specifies the minimum size (in bytes) for which result columns are returned in memo fields. For example, if the width of a column result is greater than the value of UseMemoSize, the column result is stored in a memo field. UseMemoSize may vary from 1 to 255; the default value is 255.	
WhereType	The WHERE clause for updates to source tables. WhereType may assume the following values: 
1 or DB_KEY (from FOXPRO.H). The WHERE clause used to update remote tables consists of only the primary fields specified with the KeyFieldList property.
2 or DB_KEYANDUPDATABLE (from FOXPRO.H). The WHERE clause used to update remote tables consists of the primary fields specified with the KeyFieldList property and any updatable fields.
3 or DB_KEYANDMODIFIED (from FOXPRO.H) (default). The WHERE clause used to update remote tables consists of the primary fields specified with the KeyFieldList property and any other fields that are modified.
4 or DB_KEYANDTIMESTAMP (from FOXPRO.H). The WHERE clause used to update remote tables consists of the primary fields specified with the KeyFieldList property and a comparison of the time stamps.	
1.2.2.2	UseDeDataSource Property
True/False. If true, specifies that the cursor uses the DataSource of the associated Data Environment for it’s own data source. 

Property Details
Read-write at design time and run time. 
Applies to Cursor Class.

UE Localizable Text
Property Sheet
Specifies whether or not the data source of the associated Data Environment is used as the data source of the cursor.
Error Messages
Remarks
This property may only be used when the cursor is contained in a Data Environment object. Property is set by the Data Environment and Cursor class that is associated with the DEClass property. If the CursorAdapter. UseDeDataSource property is True (.T
If the DataSource and DataSourceType in the Data Environment class is not set – i.e.; both are the empty string ("") no errors are generated.
1.2.2.3	DataSource Property
Specifies the data source. Contains a reference to an existing data source of permitted data source types Same as DataEnvironment Class DataSource. See DataEnvironmentClass.doc for property details.
Note that the SelectCmd uses the CursorAdapters DataSource unless the UseDeDataSource property is set to True, and the CursorAdapter is a member of the DataEnvironment, in which case the DataSource and DataSourceType of the DataEnvironment will be used.
CursorAdapter specific implementation notes
•	When DataSourceType is ADO, then the CursorAdapter.DataSource must be set to a valid ADO Recordset object. See the CursorFill, and the Update/Insert/DeleteCmd.DataSource  & DataSourceType properties.
•	 When DataSourceType is XML, then the CursorAdapter.DataSource is ignored.

1.2.2.4	DataSourceType Property
Specifies the current data source type (ADO, XML, ODBC, Native). Same as DataEnvironment Class DataSourceType. See DataEnvironmentClass2.doc for property details. 

1.2.2.5	SelectCmd Property
Specifies the command to be executed when the CursorFill method is called. 

Property Details
Read-write at design time and run time. 
Applies to Cursor Class.

UE Description
Property Sheet
Specifies the command used for retrieving data.
Remarks
Character string or expression that evaluates to a valid command string for the CursorAdapter.DataSourceType.
1.2.2.6	UpdateCmd Property
Specifies the command to be executed when the TableUpdate function executes an update operation. 

Property Details
Read-write at design time and run time. 
Applies to Cursor Class.

Remarks
•	Character string or expression that evaluates to a valid command string for the UpdateCmdDataSource type. 
•	If empty (""), and AllowUpdate is True, an SQL UPDATE statement will be automatically generated.
•	Note that for automatic INSERT/UPDATE/DELETE generation the legacy 'Tables', 'KeyFieldlist', 'UpdatableFieldlist', and 'UpdateNameList' properties must be set via the corresponding CursorAdapter property – i.e.; CursorAdapter.Tables, CursorAdapter.KeyFieldlist, CursorAdapter.UpdateNameList, etc..
1.2.2.7	InsertCmd Property
Specifies the command to be executed when the TableUpdate method executes an insert operation. 

Property Details
Read-write at design time and run time. 
Applies to Cursor Class.

UE Description
Property Sheet
Specifies the command used for adding new rows.
Remarks
•	Character string or expression that evaluates to a valid command string for the InsertCmdDataSource type. 
•	If empty (""), and AllowInsert is True, an SQL INSERT statement will be auto generated.
•	Note that for automatic INSERT/UPDATE/DELETE generation the legacy 'Tables', 'KeyFieldlist', 'UpdatableFieldlist', and 'UpdateNameList' properties must be set via via the corresponding CursorAdapter property – i.e.; CursorAdapter.Tables, CursorAdapter.KeyFieldlist, CursorAdapter.UpdateNameList, etc..
1.2.2.8	DeleteCmd Property
Specifies the command to be executed when the TableUpdate method executes an delete operation. 

Property Details
Read-write at design time and run time. 
Applies to Cursor Class.

Remarks
•	Character string or expression that evaluates to a valid command string for the DeleteCmdDataSource type. 
•	If empty (""), and AllowDelete is True, an SQL DELETE statement will be auto generated.
•	Note that for automatic INSERT/UPDATE/DELETE generation the legacy 'Tables', 'KeyFieldlist', 'UpdatableFieldlist', and 'UpdateNameList' properties must be set via via the corresponding CursorAdapter property – i.e.; CursorAdapter.Tables, CursorAdapter.KeyFieldlist, CursorAdapter.UpdateNameList, etc..
1.2.2.9	AllowUpdate Property
Boolean (.T.). Specifies whether update operations will be allowed. When True, and UpdateCmd property is empty, an update statement will be auto-generated.

Property Details
Read-write at design time and run time. 
Applies to CursorAdapter Class.

Remarks
Note that for automatic INSERT/UPDATE/DELETE generation the legacy 'Tables', 'KeyFieldlist', 'UpdatableFieldlist', and 'UpdateNameList' properties must be set via via the corresponding CursorAdapter property – i.e.; CursorAdapter.Tables, CursorAdapter.KeyFieldlist, CursorAdapter.UpdateNameList, etc..

1.2.2.10	AllowInsert Property
Boolean (.T.). Specifies whether insert operations will be allowed. When True, and InsertCmd property is empty, an insert statement will be auto-generated.

Property Details
Read-write at design time and run time. 
Applies to CursorAdapter Class.

Remarks
Note that for automatic INSERT/UPDATE/DELETE generation the legacy 'Tables', 'KeyFieldlist', 'UpdatableFieldlist', and 'UpdateNameList' properties must be set via via the corresponding CursorAdapter property – i.e.; CursorAdapter.Tables, CursorAdapter.KeyFieldlist, CursorAdapter.UpdateNameList, etc..
1.2.2.11	AllowDelete Property
Boolean (.T.). Specifies whether delete operations will be allowed. When True, and DeleteCmd property is empty, a delete statement will be auto-generated.
Property Details
Read-write at design time and run time. 
Applies to CursorAdapter Class.

Remarks
Note that for automatic INSERT/UPDATE/DELETE generation the legacy 'Tables', 'KeyFieldlist', 'UpdatableFieldlist', and 'UpdateNameList' properties must be set via via the corresponding CursorAdapter property – i.e.; CursorAdapter.Tables, CursorAdapter.KeyFieldlist, CursorAdapter.UpdateNameList, etc.. 

1.2.2.12	InsertCmdDataSource Property
Specifies the data source used by the InsertCmd. 

Property Details
Read-write at design time and run time. 
Applies to Cursor Class.

Remarks 
•	If the InsertCmdDataSourceType  is empty, then the ADO Recordset update (Recordset.Update) functionality will be used.
•	If the InsertCmdDataSourceType  is ADO, then InsertCmdDataSource must be set to a valid ADO Command object. The ActiveConnection property must be set to a valid, open, ADO Connection object.
•	The CommandText property of the command object is set to the value of CursorAdapter.InsertCmd. The command is parsed for parameters, and if any are found, the parameter values are created and set in the command object.
•	If InsertCmd is empty, the insert statement is automatically generated.
•	Automatic updating functionality requires that the legacy 'Tables', 'KeyFieldlist', 'UpdatableFieldlist', and 'UpdateNameList' properties must be set via via the corresponding CursorAdapter property – i.e.; CursorAdapter.Tables, CursorAdapter.KeyFieldlist, CursorAdapter.UpdateNameList, etc..
•	Automatic updating via an ADO Recordset -
•	The ADODB.Recordset.BookMark property must be supported by the provider.
•	UpdateBatch is never called – CursorAdapter will always execute the Recordset.Update method.
•	The insert operation is carried out as:
•	CursorAdapter.InsertDataSource.Execute().
•	If the InsertCmdDataSourceType  is "XML", then DataSource is ignored.
If the property is not specified (null or empty), the CursorAdapter DataSource will be used.

1.2.2.13	InsertCmdDataSourceType  Property
Specifies the InsertCmd data source type. 

Property Details
Read-write at design time and run time. 
Applies to Cursor Class.

Remarks
•	If the property is not specified (null or empty), the Cursor data source type will be used, except as noted below.
•	To cause an XMLUpdateGram or DiffGram to be generated, the value of this property must be explicitly set to "XML".
•	Otherwise, Visual FoxPro disregards InsertCmdDataSourceType.
If the value of InsertCmdDataSource is empty – i.e.; use Cursor DataSource, then Cursor DataSourceType over-rides any value here.
1.2.2.14	UpdateCmdDataSource Property
Specifies the data source used by the UpdateCmd. 

Property Details
Read-write at design time and run time. 
Applies to Cursor Class.

UE Description
Property Sheet
Specifies the data source to be used for the UpdateCmd.
Remarks
•	If the UpdateCmdDataSourceType is empty, and cursor is based on ADO Recordset, then the ADO Recordset update (Recordset.Update) functionality will be used. 
•	If the UpdateCmdDataSourceType is ADO, then UpdateCmdDataSource must be set to a valid ADO Command object. The ActiveConnection property must be set to a valid, open, ADO Connection object.
•	The CommandText property of the command object is set to the value of CursorAdapter. UpdateCmd. The command is parsed for parameters, and if any are found, the parameter values are created and set in the command object.
•	If UpdateCmd is empty, the update statement is automatically generated.
•	Automatic updating functionality requires that the legacy 'Tables', 'KeyFieldlist', 'UpdatableFieldlist', and 'UpdateNameList' properties must be set via via the corresponding CursorAdapter property – i.e.; CursorAdapter.Tables, CursorAdapter.KeyFieldlist, CursorAdapter.UpdateNameList, etc..
•	Automatic updating via an ADO Recordset -
•	The ADODB.Recordset.BookMark property must be supported by the provider.
•	UpdateBatch is never called – CursorAdapter will always execute the Recordset.Update method.
•	The update operation is carried out as:
•	CursorAdapter.UpdateDataSource.Execute(). 
1.2.2.15	UpdateCmdDataSourceType Property
Specifies the data source type. 

Property Details
Read-write at design time and run time. 
Applies to Cursor Class.

Remarks
•	If the property is not specified (null or empty), the Cursor data source type will be used, except as noted below.
•	To cause an XMLUpdateGram or DiffGram to be generated, the value of this property must be explicitly set to "XML".
•	Otherwise, Visual FoxPro disregards UpdateCmdDataSource.
•	If the value of UpdateCmdDataSource is empty – i.e.; use Cursor DataSource, then Cursor DataSourceType over-rides any value here.
1.2.2.16	DeleteCmdDataSource Property
Specifies the data source used by the DeleteCmd. 

Property Details
Read-write at design time and run time. 
Applies to Cursor Class.

Remarks
•	If the DeleteCmdDataSourceType is empty, then the ADO Recordset update (Recordset.Update) functionality will be used.
•	If the DeleteCmdDataSourceType is ADO, then DeleteCmdDataSource must be set to a valid ADO Command object. The ActiveConnection property must be set to a valid, open, ADO Connection object.
•	The CommandText property of the command object is set to the value of CursorAdapter. DeleteCmd. The command is parsed for parameters, and if any are found, the parameter values are created and set in the command object.
•	If DeleteCmd is empty, the delete statement is automatically generated.
•	Automatic updating functionality requires that the legacy 'Tables', 'KeyFieldlist', 'UpdatableFieldlist', and 'UpdateNameList' properties must be set via via the corresponding CursorAdapter property – i.e.; CursorAdapter.Tables, CursorAdapter.KeyFieldlist, CursorAdapter.UpdateNameList, etc..
•	Automatic updating via an ADO Recordset -
•	The ADODB.Recordset.BookMark property must be supported by the provider.
•	UpdateBatch is never called – CursorAdapter will always execute the Recordset.Update method..
•	The delete operation is carried out as:
•	CursorAdapter. DeleteCmdDataSource.Execute(). 
If the property is not specified (null or empty), the Cursor data source will be used.
1.2.2.17	DeleteCmdDataSourceType Property
Specifies the data source type. 

Property Details
Read-write at design time and run time. 
Applies to Cursor Class.

Remarks
•	If the property is not specified (null or empty), the Cursor data source type will be used, except as noted below.
•	To cause an XMLUpdateGram or DiffGram to be generated, the value of this property must be explicitly set to "XML".
•	Otherwise, Visual FoxPro disregards DeleteCmdDataSource.
•	If the value of DeleteCmdDataSource is empty – i.e.; use Cursor DataSource, then Cursor DataSourceType over-rides any value here.
1.2.2.18	CursorSchema Property
Specifies the structure of the cursor associated with the CursorAdapter. 

Property Details
Read-write at design time and run time. 
Applies to Cursor Class.

Property Value
A character string or expression that specifies a valid list of field names and types, comma delimited. Used by CursorAdapter as a template for the cursor created by CursorFill() when lUseCursorSchema is True, or when adding an CursorAdapter based cursor to the DataEnvironment designer surface. For example: 

CursorAdapter.CursorSchema = ;
“col1 I, col2 c(25) NULL, col3 M NOCPTRANS, col4 Y, col5 n(12,3)” 

CursorSchema is the "between the parenthesis" portion of the CREATE TABLE/CREATE CURSOR command.

Remarks
If the property is not specified (null or empty), the Cursor’s data source will be used to determine the cursor structure.
Can also be used to define XML cursor structure.
The CursorSchema is used at design time to determine the cursor structure to place on the Data Environment Designer surface.
The CursorSchema is used at run-time if specified by the CursorFill method. It is the users responsibility to ensure that this value matches and/or maps data source columns in an acceptable manner.
1.2.2.19	DiffGram Property
Character (""). Contains the XMLUpdateGram or DiffGram that is generated when the Update/Insert/DeleteDataSourceType is set to XML and data is updated, added or deleted in the current cursor.

Property Details
Read-only at design time and run time. 
Applies to CursorAdapter Class.

Remarks
CursorAdapter generates the DiffGram/UpdateGram, but the actual update operation must be implemented by the user via whatever methodology is required – i.e.; SQLXML via HTTP or SQLXMLOLEDB, Web Service to .Net, etc.
1.2.2.20	Flags Property
Integer ( 0 ). Contains the nFlag settings, if any, that the user wishes to utilize.
When DataSourceType is "XML", CursorAdapter will use this to pass any flags desired by the user for constructing an  UpdateGram or DiffGram. Refer to the XMLUpdateGram() function documentation for the nFlags settings.

Property Details
Read-write at design time and run time. 
Applies to CursorAdapter Class.
Remarks
CursorAdapter will evaluate this property only once per update operation. 
1.2.2.21	CursorStatus Property
Specifies whether or not the cursor represented by the Alias property is attached, unattached, and how attached.. 

Property Details
Read-only. Applies to CursorAdapter Class.

Property Values – 
•	0 (default) – cursor <Alias> is not attached.
•	1 – Cursor <Alias> attached via CursorFill method.
•	2 - Cursor <Alias> attached via CursorAttach method.

Remarks
Needed to determine state of the current CursorAdapter instance, and where associated cursor came from.
1.2.2.22	ConversionFunc Property
When using automatic updating, provides the ability to specify a native VFP conversion function or custom function or method that will be applied to the field before it is sent to the data source by an update, insert, or delete operation. Contains a comma delimited list of field name/function name pairs separated by a space. 

For example, if the local cursor contains character fields that are being used as part of the WHERE clause for an automatically generated update command, the data source may require that the trailing spaces be removed to obtain a proper match at the data source. Thus:
	
CursorAdapter.ConversionFunc = "companyname TRIM, contactname TRIM"
	
•	The function or method specified must accept the associated fieldname as the sole parameter.
•	Do not include parentheses at the end of the function or method name.
•	The value returned by the function associated with the field is the value that will be sent to the data source.

Property Details
Read-write at design time and run time. 
Applies to CursorAdapter Class.

Errors 
	Invalid field name specified – Error 12 – Variable 'fieldname' is not found.
	Invalid function or procedure specified – 	Error 1  - File 'function name' does not exist.
Remarks. 
Property values not evaluated at design time. 
MSFT ONLY: Addresses issues raised by Bug 16120. 

1.2.3	CursorAdapter Class Methods
1.2.3.1	CursorFill Method
Executes SelectCmd against DataSource according to the DataSourceType property, creates cursor and retrieves data, performing any necessary data conversion according to the DataSourceType and CursorSchema.
Syntax
CursorFill( [ lUseCursorSchema [, lNoData [,  Options [,Source ] ] ] ] )

Returns  
Boolean. True if cursor successfully created, false if not. When .F. is returned, AERROR() must be called to retrieve the specific error messages that caused the fill to fail. In this scenario (when false (.F.) is returned), ON ERROR will not be triggered. 

When false (.f.) is returned, the cursor will be discarded. The BeforeCursorClose  and the AfterCursorClose events will not fire in this case.
Parameters
lUseCursorSchema (optional, .F.). T/F, or expression that returns Boolean. Specifies whether (.T.) or not (.F.) the schema contained in the CursorSchema property is used for cursor that is created. 
lNoData (optional, .F.). When set to true (.T.), the cursor is created, but not filled with data. 
•	When the DataSourceType property is set to "XML" and lNoData is set to True (.T.), the cursor will be created using the definition in the CursorSchema property regardless of the value of lUseCursorSchema. If the CursorSchema property is empty or invalid, VFP will goof. 
•	Note that if CursorFill() is executed with lNoData, and the SelectCmd is a SELECT SQL statement against a parameterized View, that you will still be prompted for parameter input. Doing this is the same as doing the following in VFP:
•	select * from "customers in specific country" where 1=0
Source (optional, ""). A reference to an ADO Command object. The object must have it's ActiveConnection property set to the appropriate ADO Connection object, which must be open. When DataSource is provided in this parameter, then DataSource.CommandText is set to the value of CursorAdapter.SelectCmd. The SelectCmd is parsed for parameters, and the parameter values are then set in the Command object. CursorFill is then executed in the following manner:
	CursorAdapter.DataSource.Open( Source,,,,Options ) 
where DataSource is the ADO Recordset object specified in CursorAdapter.DataSource, and Source is the ADO Command object specified in this parameter.
If Source is not provided, then CursorFill is executed in the following manner:
	CursorAdapter.DataSource.Open( CursorAdapter.SelectCmd,,,,Options )
where DataSource is the ADO Recordset object specified in CursorAdapter.DataSource as above. In this case, however, the Recordset executes the current CursorAdapter SelectCmd directly, and not via a command object as in the first case. The Recordset must have it's ActiveConnection set to a valid connection object.
Options 
•	ADO DataSourceType
•	(optional, -1) An int representing the Command type or Execution type Enum values that the user wishes to set for the ADO Recordset.Open method.
•	XML DataSourceType
•	Any combination of valid XMLToCursor() flags except flag 8192 which is automatically set when lUseCursorSchema is True.
Remarks

•	If DataSourceType is ODBC, SelectCmd is executed using the connection handle of the SelectCmd DataSource. Cursor returned via SQL PassThru as now. Name of cursor is This.Alias.
•	If DataSourceType is ADO, execution of the SelectCmd by CursorFill is carried out by calling the Open method of an ADO Recordset. Thus, the CursorAdapter.DataSource must be set to a valid ADO Recordset object. If the SelectCmd contains parameters, a valid ADO Command object must be specified in the Source parameter of the CursorFill method. See details in the parameter description. 
•	If DataSourceType is ADO, manually closing the cursor will also close the associated ADO Recordset. This does not apply if the cursor was obtained via CursorAttach.
•	If DataSourceType is XML, CursorFill() will utilize one of two methods to transform the XML to a cursor: 
•	If the SelectCmd evaluates to an XMLTable object – i.e. "oAdapter.Tables.Item(1)".
•	CursorFill() will call the specified XMLTable's ToCursor() method to obtain the cursor.
•	If the SelectCmd is an expression, command or function that returns an XML source.
•	The XML source requirements are the same as for the first parameter of the XMLToCursor() function, and are specified by passing the appropriate XMLToCursor() Flag via the CursorFill() Options parameter: 
•	XMLToCursor( XMLSource eExpression | cXMLFile [, cCursorName [,  nFlags ]] )
XMLSource
cXMLFile
Name/path of a physical XML file residing on the local machine or accessible via the network. If path is not specified, VFP will search the FoxPro path for the XML file.
eExpression
Any expression that returns XML in an accepted format. Accepts VFP memory variable, memo field contents, or the return from an HTTP request, return from a SOAP method call, XML from XMLDOM, ADO Stream, etc.
CursorFill() will determine which option to use based on the setting of the Options parameter. If Options is 512, CursorFill() will process XMLSource as cXMLFile, and expect a quoted literal, or a memory variable containing a valid file name.
If Options is 0, VFP will process XMLSource as eExpression and expect a memory variable containing a valid XML string, or an expression that results in an XML string. 
•	Additional XMLToCursor() flags may be specified and/or combined with the above, just as in XMLToCursor().
•	The cCursorName will always be the current CursorAdapter.Alias value. 
•	When lUseCursorSchema is True (.T.), CursorFill() will automatically add the 8192 flag to force use of existing cursor for the XML.
•	When CursorFill() is executed, the currently attached cursor, if any, is closed. If it is desirable to retain the current cursor for any reason, execute CursorDetach() before execution. Note further that the value of the Alias property must then also be changed to reflect the new cursor that will be created.
•	When a cursor is attached, the cursor (or table) may not be altered via ALTER TABLE or MODI STRU.
•	When CursorFill() is executed, the work area that the cursor is created in will become the selected work area.   
1.2.3.2	CursorRefresh Method
The cursor is refreshed with data from the data source from which the cursor was created. Essentially, the SelectCmd is re-executed in the context of the DataSourceType. Note that the user could change the SelectCmd in the BeforeCursorRefresh() event to change the scope of the rows returned vis-à-vis the original query. Not applicable to an ADO Based Cursor.
Syntax
CursorRefresh()

Returns
	Boolean – True if cursor successfully repopulated, false if not.
Parameters
None
1.2.3.3	CursorAttach Method
Associates an existing cursor with the current CursorAdapter instance.
Syntax
CursorAttach( [cAlias [, lInheritCursorProperties ]] )

Returns
	Boolean – True if cursor successfully associated, false if not.
Parameters
cAlias – optional (This.Alias). The Alias of the cursor or table to attach. If not supplied, the current value of the Alias property will be used. 
lInheritCursorProperties – optional (.F.). Allows users to keep or discard property values on the cursor that is being attached. This adds flexibility when users are attaching SQL Pass-Thru, Remote View, or other cursors.
•	If False, any existing properties that have been set on the cursor will not be copied into the corresponding properties of the CursorAdapter.
•	If True, any existing properties that have been set on the cursor will be inherited by the corresponding properties of  the CursorAdapter.
Remarks
•	Executing CursorAttach closes the currently associated cursor. To preserve the currently associated cursor, execute CursorDetach() first.
•	Executing CursorAttach populates the Alias property of the CursorAdapter instance with the alias of the attached cursor or table.
•	Executing CursorAttach sets BufferModeOverride (3) or BufferModeOverride (5), depending on BufferModeOverride property,  on for the attached cursor if not already set. If buffering is already on, but is less granular than Optimistic Row, buffering will be promoted to Optimistic Row.
•	When a cursor is attached, the cursor (or table) may not be altered via ALTER TABLE or MODI STRU.
•	Only one instance of an CursorAdapter may be attached to any given table or cursor at the same time.
•	If DataSourceType is ADO, manually closing the cursor will not close the associated ADO Recordset. This does not apply if the cursor was obtained via CursorFill.
1.2.3.4	CursorDetach Method
Frees an existing cursor from the current CursorAdapter instance.
Syntax
CursorDetach()

Returns
	Boolean – True if cursor successfully detached, false if not.
Parameters
None

Remarks
•	All settings available to a normal (non-CursorAdapter associated) cursor will be preserved, including Buffering, etc. in the cursor that is freed.
•	All uncommitted changes will be preserved – i.e.; it is not necessary to issue TableUpdate() prior to detaching a cursor.
1.2.3.5	AutoOpen Method
When called, this method calls the CursorFill() method with no parameters. When the CursorAdapter is contained in a DataEnvironment, this method will be called automatically in the following circumstances:
•	When the DataEnvironment.AutoOpenTables property is set to .T. (default).
•	When the DataEnvironment.AutoOpenTables property is set to .F., and the DataEnvironment.OpenTables method is executed by the user.
User may override by using NODEFAULT, and explicitly calling CursorFill (or not, as the case may be).
Syntax
AutoOpen()

Return Values
Boolean. .T. if successful. Otherwise, error will be raised.
Parameters
None

Remarks
The AutoOpenTables property only applies when the DataEnvironment is contained in a Form.
1.2.4	CursorAdapter Class Events
CursorAdapter class events are provided to allow the user to perform custom actions before and after CursorAdapter methods are executed. The following behavior will apply to events:
•	If a Before event returns false (.F.), the execution of the method will not occur.
•	If the After events for the individual U/I/D events return false, the field states in the cursor for that row will not be cleared, i.e.; GetFldState(-1) will still show all modifications.
•	In the After event of the TableUpdate event, the user can determine success of u/i/d operations, and take appropriate actions, committing/rolling back transactions, etc.
•	Others as specified.
1.2.4.1	BeforeCursorFill Event
Fired by CursorAdapter just before it attempts to attach new cursor. Currently attached cursor is not closed yet, operation will be cancelled if event returns .F. .   
Procedure object.BeforeCursorFill
LPARAMETERS lUseCursorSchema, lNoDataOnLoad, SelectCmd 

Returns
Boolean. If False is returned, CursorFill will not be executed.
Parameters
lUseCursorSchema – the value specified in the CursorFill()method.
NoDataOnLoad – the value specified in the CursorFill()method.
SelectCmd – The value of the SelectCmd property.
Remarks
•	Changes made to these parameters will take precedence over the original values stored in the respective properties, or passed to CursorFill..
•	Changes to the parameters will be stored internally, and will be used by the CursorFill method. The CursorRefresh() method will use the value of the SelectCmd parameter, not the value of the SelectCmd property. The original SelectCmd property value will not be changed. This will facilitate constructing custom queries "on the fly". A user could, for example, store the basic SQL SELECT statement in the property, and add the WHERE clause in this event without changing the underlying property value. The value of the SelectCmd parameter will be passed to the AfterCursorFill event.
•	If this event returns false (.f.), will be cancelled. 

1.2.4.2	AfterCursorFill Event
Fired by CursorAdapter just after attempt to attach new cursor.  Operation will be cancelled if event returns .F. .
Procedure object.AfterCursorFill
LPARAMETERS lUseCursorSchema, lnoDataOnLoad, SelectCmd, Result

Parameters
lUseCursorSchema – the value specified in the CursorFill()method.
NoDataOnLoad – the value specified in the CursorFill()method.
SelectCmd – The value of the SelectCmd used – i.e.; if changed in BeforeCursorFill, the changed value of SelectCmd.
Result – The return value (.T./.F.) from CursorFill.
Remarks
•	If any of the first three parameters were changed in the BeforeCursorFill event, the changed values will be passed to this event, instead of the values contained in the properties.
1.2.4.3	BeforeCursorRefresh Event
Fired by CursorAdapter just before attempt to refresh.
Procedure object.BeforeCursorRefresh
LPARAMETERS SelectCmd

Parameters
SelectCmd – The value of the SelectCmd used – i.e.; the value of the SelectCmd parameter as established in BeforeCursorFill, which may or may not be the same as the value in the SelectCmd property.

Remarks
•	If this event returns false (.f.), execution of CursorRefresh will not occur.
1.2.4.4	AfterCursorRefresh Event
Fired by CursorAdapter just after attempt to refresh.
Procedure object.AfterCursorRefresh
LPARAMETERS SelectCmd, Result

Parameters
SelectCmd – The value of the SelectCmd used – i.e.; the value of the SelectCmd parameter as established in BeforeCursorFill, which may or may not be the same as the value in the SelectCmd property.
Result – The return value (.T./.F.) from CursorRefresh().

Remarks
No action is taken if this event returns false (.F.).  User would typically return Result and take further action depending on it's value.
1.2.4.5	BeforeCursorUpdate Event
Occurs just before TableUpdate executes.
Procedure object.BeforeCursorUpdate
LPARAMETERS nRows, lForce

Parameters
nRows – the TableUpdate nRows setting.
lForce – the TableUpdate Force setting.

Remarks
•	BeforeCursorUpdate fires once at the beginning of the update process. If multiple rows are being updated, then the individual BeforeUpdate, BeforeInsert, and BeforeDelete events will fire for each updated row as appropriate.
•	If this event returns False (.F.), no update, insert, or delete operations will occur.
1.2.4.6	AfterCursorUpdate Event
Occurs immediately after TableUpdate executes.
Procedure object.AfterCursorUpdate
LPARAMETERS  nRows, TableUpdateResult

Parameters
nRows – the TableUpdate nRows setting.
TableUpdateResult – the TableUpdate return value.

Remarks
AfterCursorUpdate fires once at the completion of the update process. If multiple rows are being updated, then the individual AfterUpdate, AfterInsert, and AfterDelete events will fire for each updated row as appropriate..
1.2.4.7	BeforeUpdate Event
Occurs just before the TableUpdate function executes an update command.
Procedure object.BeforeUpdate
LPARAMETERS cFldState, lForce,  UpdateType, , UpdateInsertCmd, DeleteCmd

Parameters
lForce – the value of the Force parameter from TableUpdate.
cFldState – the field states of the row being processed. This is the same value that would be obtained by calling GetFldState(-1), i.e.; a character string consisting of deletion and edit status values for all fields in the table or cursor. For example, if a table has five fields and only the first field has been edited, GETFLDSTATE( ) returns the following: 121111. The 1 in the first position indicates the deletion status has not been changed.
UpdateType – Update or Delete/Insert. Value passed is the current value of this setting in the generated cursor – i.e.; CursorGetProp('UpdateType'). Numeric. 1 = update, 2 = insert/delete. Changing this will change the behavior for the current record, but will not change the value of the property. Needed only if Auto Updating.
UpdateInsertCmd – the value of the UpdateCmd or InsertCmd property, as appropriate. the value of this parameter is used as the Update/Insert command, and may be changed in this event. If changed, the value of the UpdateCmd or InsertCmd property is not changed.
DeleteCmd – empty if UpdateType = 1 (use UpdateCmd). Otherwise, value of DeleteCmd. the value of this parameter is used as the delete command, and may be changed in this event. If changed, the value of the DeleteCmd is not changed.

Remarks
If this event returns False (.F.), the update operation will not occur.
1.2.4.8	AfterUpdate Event
Occurs immediately after the TableUpdate function executes an update command.
Procedure object.AfterUpdate
LPARAMETERS cFldState, lForce, UpdateType, UpdateInsertCmd, DeleteCmd, Result

Parameters
cFldState – the field states of the row being processed as previous.
lForce – the value of the Force parameter from TableUpdate.
UpdateType – the value of the UpdateType parameter from BeforeUpdate event.
UpdateInsertCmd – the value of the UpdateInsertCmd parameter from BeforeUpdate event..
DeleteCmd – the value of the DeleteCmd parameter from BeforeUpdate event.
Result – the value returned by executing TableUpdate for this record. 

Remarks
•	Note that the user may change the value of Result and ErrorMsg to override the outcome of the operation as returned
•	If Result is false, field states are not cleared.
•	Conversely, if Result is true, field states will be cleared for this record.
1.2.4.9	BeforeInsert Event
Occurs just before the TableUpdate function executes an insert command.
Procedure object.BeforeInsert
LPARAMETERS cFldState, lForce, InsertCmd.

Parameters
cFldState – the field states of the row being processed. Details as above.
lForce – the value of the Force parameter from TableUpdate.
InsertCmd – the value of the InsertCmd property. The value of this parameter is used as the Insert command, and may be changed in this event. If changed, the value of the  InsertCmd property is not changed.

Remarks
If this event returns False (.F.), the insert operation will not occur.
1.2.4.10	AfterInsert Event
Occurs immediately after the TableUpdate function executes an insert command..
Procedure object.AfterInsert
LPARAMETERS cFldState, lForce, InsertCmd, Result

Parameters
cFldState – the field states of the row being processed as previous.
lForce – the value of the Force parameter from TableUpdate.
InsertCmd – the value of the InsertCmd parameter from BeforeInsert event..
Result – the value returned by executing TableUpdate for this record.
Error Messages:
None. User may call AERROR() function to retrieve errors from upstream data sources.
Remarks
•	Note that the user may change the value of Result and ErrorMsg to override the outcome of the operation as returned
•	If Result is false, field states are not cleared.
•	Conversely, if Result is true, field states will be cleared for this record.
1.2.4.11	BeforeDelete Event
Occurs just before the TableUpdate function executes an delete command.
Procedure object.BeforeDelete
LPARAMETERS cFldState, lForce, DeleteCmd

Parameters
cFldState – the field states of the row being processed. Details as above.
lForce – the value of the Force parameter from TableUpdate.
DeleteCmd – the value of the DeleteCmd property. the value of this parameter is used as the delete command, and may be changed in this event. If changed, the value of the DeleteCmd property is not changed.

Remarks
If this event returns False (.F.), the delete operation will not occur.
1.2.4.12	AfterDelete Event
Occurs immediately after the TableUpdate method executes an delete command.
Procedure object.AfterDelete
LPARAMETERS cFldState, lForce, DeleteCmd, Result
Parameters
cFldState – the field states of the row being processed as previous.
lForce – the value of the Force parameter from TableUpdate.
DeleteCmd – the value of the DeleteCmd parameter from BeforeDelete event..
Result – the value returned by executing TableUpdate for this record.
Error Messages:
None. User may call AERROR() function to retrieve errors from upstream data sources.
Remarks
•	Note that the user may change the value of Result to override the outcome of the operation as returned
•	If Result is false, field states are not cleared.
•	Conversely, if Result is true, field states will be cleared for this record.
1.2.4.13	BeforeCursorAttach Event
Fired by CursorAdapter just before attempt to Attach.
Procedure object.BeforeCursorAttach
LPARAMETERS cAlias

Returns
	Boolean. If False, CursorAttach will not be executed.
Parameters
cAlias – the alias of the cursor or table being associated. The value of this parameter may be changed in this event. The current value of this parameter will be used when CursorAttach executes. 

UE Localizable Text
Property Sheet Description:
Fired by CursorAdapter just before attempt to Detach.
Error Messages:
Error Number ???:
Remarks
•	Use this event to perform any necessary pre-attach operations, determine alias to attach, etc.
Example
For example, you can attach a View to an CursorAdapter in this event. Add code to open the data and the view:
PROCEDURE BeforeCursorAttach
OPEN DATABASE HOME(2)+"tastrade\data\tastrade.dbc"
USE 'product listing'
ENDPROC
Then call CursorAttach('product_listing').
1.2.4.14	AfterCursorAttach Event
Fired by CursorAdapter just after attempt to Attach..
Procedure object.AfterCursorAttach
LPARAMETERS cAlias, Result
Parameters
cAlias – the alias name of the cursor or table that was attached.
Result – the value returned by CursorAttach execution. True if successful, False if not.
Remarks
•	Use this event to customize cursor and CursorAdapter instance as appropriate, perform validations, etc.
1.2.4.15	BeforeCursorDetach Event
Occurs just before the CursorDetach method executes.
Procedure object.BeforeCursorAttach
LPARAMETERS cAlias

Returns
	Boolean. If False, CursorDetach will not be executed.
Parameters
cAlias – the alias of the cursor or table being freed. 

Remarks
•	Use this event to perform any necessary pre-detach operations, etc.
1.2.4.16	AfterCursorDetach Event
Occurs immediately after the CursorDetach method executes.
Procedure object.AfterCursorAttach
LPARAMETERS cAlias, Result
Parameters
cAlias – the alias name of the cursor or table that was freed.
Result – the value returned by CursorDetach execution. True if successful, False if not.

UE Localizable Text
Property Sheet Description:
Occurs after CursorDetach method is executed.
Error Messages:
Error Number ???:.
Remarks
•	Use this event to reset CursorAdapter properties, perform operations on the freed table, etc.
1.2.4.17	BeforeCursorClose Event
Occurs just before the cursor is closed. 
Procedure object.BeforeCursorClose
LPARAMETERS cAlias

Returns
Boolean. If False (.F.) is returned, cursor will not be closed (USE in cAlias will fail). Error 0 – "Execution was cancelled by the user" will be raised.
Parameters
cAlias – the alias of the cursor or table being closed. 

Remarks
•	Use this event to perform any necessary pre-close operations, etc. Note that returning False from this event will prevent the cursor from being closed explicitly. Returning False from this event will not, however, prevent the cursor from being closed as a result of the CursorAdapter object being released.
1.2.4.18	AfterCursorClose Event
Occurs immediately after the cursor is closed.
Procedure object.AfterCursorClose
LPARAMETERS cAlias, Result
Parameters
cAlias – the alias name of the cursor or table that was closed.
Result  - .T. if cursor successfully closed, .F. if not. (Cursor contained uncommitted changes, for example.)

UE Localizable Text
Property Sheet Description:
Occurs after cursor is closed.
Error Messages:
Error Number ???:.
Remarks
•	When an cursor adapter is closed, the CursorAdapter object instance is marked as not having an associated cursor. The alias property is not cleared, and must be cleared or reset by the developer if desired.
•	Use this event to reset CursorAdapter properties, etc.
1.2.5	CursorAdapter Class Designer and CREATE CLASS Support
To facilitate the use of builders, as well as general ease-of-use, VCX based and language design-time support will be added for CursorAdapter. This support will be the same as for other non-visual classes supported in VCX's and the Class Designer, i.e.; Custom, Project Hook, Hyperlink, etc. All CursorAdapter PEMS will be available as appropriate (R/W, R/O) via the Property Sheet when the Class Designer is invoked. The resulting Class definition will be stored in the specified VCX.
1.2.5.1	Class Designer - New Class Dialog
CursorAdapter class will be added to the "Based on:" drop-down list in the New Class dialog that is invoked when CREATE CLASS command is issued, File/New/Class is selected from the menu, or the Classes tab of the Project Manager is selected, and the New button is invoked. 
1.2.5.2	Language Support – CursorAdapter Class
Support for CursorAdapter will be added to the CREATE CLASS command:
	CREATE CLASS ClassName AS | ? [OF ClassLibraryName1 | ?]   
[AS CursorAdapter [FROM ClassLibraryName2]] [NOWAIT]
Support for CursorAdapter will be added to the MODIFY CLASS command as required.
1.2.5.3	 Language Support – Cursor Class
In addition to CursorAdapter, support for the current Cursor class will be added to the CREATE CLASS command:
	CREATE CLASS ClassName AS | ? [OF ClassLibraryName1 | ?]   
[AS Cursor [FROM ClassLibraryName2]] [NOWAIT]
Support for Cursor will be added to the MODIFY CLASS command as required.
1.2.5.4	Language Support – Relation Class
In addition to Cursor, support for the related Relation class will be added to the CREATE CLASS command:
	CREATE CLASS ClassName AS | ? [OF ClassLibraryName1 | ?]   
[AS Relation [FROM ClassLibraryName2]] [NOWAIT]
Support for Cursor will be added to the MODIFY CLASS command as required.

1.2.6	Other Considerations
1.2.6.1	Cursor adapter
"Cursor adapter" means a cursor that has been created using the CursorFill method of an CursorAdapter object, or by associating an existing cursor with an CursorAdapter object via CursorAttach..
1.2.6.2	Automatic Updating  
If UpdateCmd, InsertCmd, or DeleteCmd properties are empty, then the Update/Insert or Delete SQL command is auto-generated, as in current VFP remote/local views. It is the developers responsibility to determine if auto-generation is appropriate for the data source context being used. Note that for automatic INSERT/UPDATE/DELETE generation the legacy 'Tables', 'KeyFieldlist', 'UpdatableFieldlist', and 'UpdateNameList' properties must be set via via the corresponding CursorAdapter property – i.e.; CursorAdapter.Tables, CursorAdapter.KeyFieldlist, CursorAdapter.UpdateNameList, etc..
1.2.6.2.1	Automatic Updating and XML
CursorAdapter will have the ability to generate an XMLUpdateGram or DiffGram and assign same to the CursorAdapter.DiffGram property. Because there are numerous ways to update an XML source, CursorAdapter will not automatically generate an Update/Insert/Delete command as it does for other data source types. Thus the following rules for XML sources:
•	the UpdateCmd, InsertCmd, and DeleteCmd may not be empty, and must contain command text that can be executed for the appropriate Update, Insert or Delete operation.
•	To invoke automatic updating behavior, the Update/Insert/DeleteCmdDataSourceType must be set to XML.
•	If Row Buffering is in effect, an XMLUpdateGram will be created for each update/insert or delete operation.
•	If Table Buffering is in effect, an XMLUpdateGram will be created for the batch of changes.
•	In this buffering mode, TableUpdate() function must be called as expected to initiate the update process, and cause the XMLUpdateGram to be generated.
•	As with other data source types, the command text specified in the UpdateCmd, InsertCmd, or DeleteCmd will be executed as appropriate.
•	If the Update/Insert/DeleteCmdDataSourceType is not set to XML, the command text specified in the UpdateCmd, InsertCmd, or DeleteCmd will be executed, but no UpdateGram will be generated.
•	CursorAdapter utilizes the WhereType cursor property to generate the key field list for the updategram. Thus, when updating or deleting, the KeyFieldList cursor property must contain the appropriate key field(s).
•	CursorAdapter also requires that the Tables, UpdatableFieldList, and UpdateNameList cursor properties contain valid entries in order to construct the updategram correctly.
•	CursorAdapter can generate a multiple table update gram by specifying the tables and fields in the above properties as appropriate. Thus, a cursor that represents a multi-table join may be updated via the Updategram method.
Remarks
•	CursorAdapter generates the DiffGram/UpdateGram, but the actual update operation must be implemented by the user via whatever methodology is required – i.e.; SQLXML via HTTP or SQLXMLOLEDB, Web Service to .Net, etc.
1.2.6.3	TableUpdate Function
The current TableUpdate() function will be changed to recognize cursor adapters, and to execute commands in the context of the data source type, using the commands from UpdateCmd, InsertCmd, or DeleteCmd properties as appropriate, against the current cursor row or rows. 
The nRows, and lForce parameters are passed to the BeforeCursorUpdate event.
The Force parameter will have no effect when used with cursor adapters, but can be evaluated and acted upon in the appropriate cursor event if desired. 
TableUpdate() operates on the cursor associated with the CursorAdapter only – i.e.; it does not operate against the remote data. 
Syntax
TableUpdate( [ nRows [, lForce ] [, cTableAlias | nWorkArea ] [ cErrorArray] )

Parameters
nRows (optional). Specifies which changes made to the cursor adapter are committed. If nRows is 0 (or .F.) and row or table buffering is enabled, only changes made to the current row in the cursor are committed. 
If nRows is 1 and table buffering is enabled, changes made to all rows are committed to the cursor adapter. If nRows is 1 (or .T.) and row buffering is enabled, only changes made to the current record in the table or cursor are committed.
In the case when only the changes made to the current row in the cursor are committed, then the appropriate Update/Insert/DeleteCmd is fired for that row only. 
In the case when the changes made to all rows in the cursor are committed, then the appropriate Update/Insert/DeleteCmd is fired for each affected row.
Note that Optimistic Row Buffering, at least, will always be enabled for cursors associated with an CursorAdapter instance.
If nRows is 2, changes made to the table or cursor are committed in the same manner as when nRows is 1. However, an error doesn't occur when a change cannot be committed, and Visual FoxPro continues to process any remaining records in the table or cursor. If cErrorArray is included, an array containing error information is created when an error occurs. 
The default value for nRows is 0. 
lForce – used for informational purposes only with cursor adapters. Passed to the BeforeUpdateCursor, and Before/After Upd/Ins/Del events.
cErrorArray - used for informational purposes only with cursor adapters. Passed to the AfterUpdateCursor, and After Upd/Ins/Del events.
(optional). Specifies the name of an array created when nRows is 2 and changes to a record cannot be committed. The array contains a single column containing the record numbers of the records for which changes could not be committed. .

Remarks
Also passes value of GetFldState(-1) to the BeforeUpdate/Insert/Delete events of each row being acted upon.
Successful completion resets Field States, as per normal TableUpdate() behavior.
TableRevert() function will operate on cursor adapters the same as other buffered cursors.
Note that it is possible to move from the current record in the BeforeInsert, AfterInsert, BeforeDelete, AfterDelete, BeforeUpdate, AfterUpdate events. It is also possible to modify data in the cursor. This allows support for scenarios such as retrieving the auto increment value from the base table and putting it into the cursor. When this occurs, CursorAdapter should automatically return to the record, which is about to be committed, after event has been fired, and commit it. In case of TABLEUPDATE(1 or 2), all changes made to the cursor in BeforeInsert, AfterInsert, BeforeDelete, AfterDelete, BeforeUpdate, AfterUpdate must be committed during the same call to TABLEUPDATE(), unless an error occurs.
1.2.6.4	GetCursorAdapter Function
At run time, it may be necessary to obtain an object reference to an CursorAdapter object instance by referencing the cursor name. A new GetCursorAdapter function will be implemented for this purpose.
Syntax
GetCursorAdapter( [ cAlias ] )

Parameters
	cAlias – optional. The alias of cursor. If not supplied, the cursor in the current work area.
Returns 
	Object Reference. 
1.3	Examples
1.3.1	CursorAdapter Examples
1.3.1.1	CursorAdapter Using Native DBF table
*!* CursorAdapter using native table
CLEAR 
OPEN DATABASE HOME(2)+"data\testdata.dbc"
oXC = CREATEOBJECT('xcDBF')
BROWSE LAST
CLOSE DATABASES 

DEFINE CLASS xcDBF as CursorAdapter 
	DataSourceType="Native"
	Alias="xcCustomer"
	*!* CursorSchema required only if adding instance to DE.
	CursorSchema = ;
	"cust_id c(6), company c(40), contact c(30), "+;
	"title c(30), address c(60), city c(15), country c(15)"
	SelectCmd = ;
	"select cust_id, company, contact, title, address, city,"+;
	 "country from customer where cust_id LIKE 'C%'"
	PROCEDURE Init
		this.CursorFill()
	ENDPROC 	
ENDDEFINE
1.3.1.2	CursorAdapter Using ODBC Data Source
*!* CursorAdapter using ODBC
oXC = CREATEOBJECT('xcODBC')
BROWSE LAST

DEFINE CLASS xcODBC as CursorAdapter 
	DataSourceType="ODBC"
	Alias="sqlCustomers"
	*!* CursorSchema required only when adding instance to
	*!* DE at Design-time, or when over-riding source types 
	*!* via lUseCursorSchema in CursorFill() method.
CursorSchema = ;
	"customerid c(5),companyname c(30),contactname c(30),"+;
" contacttitle c(30), address c(30), city c(30), country"+;
" c(30)"
	SelectCmd = ;
	"select customerid, companyname, contactname, "+;
contacttitle, address, city, country from "+;
customers where customerid LIKE 'C%'"
	 PROCEDURE Init
		this.AddProperty('cn',;		
		SQLSTRINGCONNECT(;
		"Driver=SQL Server;Server=localserver;"+;
		"Description=Northwind, Int Security;"+;
		"DATABASE=Northwind;Trusted_Connection=Yes"))
		this.DataSource = this.cn
		this.CursorFill()
	 ENDPROC 	
ENDDEFINE
1.3.1.3	CursorAdapter Using XML Data Source
* XML CursorAdapter
oXC = CREATEOBJECT('xcXML')
BROWSE LAST

DEFINE CLASS xcXML as CursorAdapter 
	DataSourceType="XML"
	Alias="xmlCursor"
	PROCEDURE Init
		CLEAR
this.SelectCmd = "this.getXml()"
		this.CursorFill()
	ENDPROC
	PROCEDURE GetXml
		*!* For illustration purposes only.
		*!* XML Source would typically be XML File,
		*!* SQLXML via HTTP, Web Service, etc.
		CREATE CURSOR xmltest (col1 i, col2 c(25), col3 t)
		INSERT into xmltest values( 1, ;
'xmltest cursor',DATETIME())
		CURSORTOXML('xmltest','strXML',1,1,0,"1")
		RETURN strXML
	ENDPROC 
ENDDEFINE
1.3.1.4	CursorAdapter Using ADO Data Source
*!* ADO CursorAdapter
oXC = CREATEOBJECT('xcADO')
BROWSE LAST

DEFINE CLASS xcADO as CursorAdapter  
	PROCEDURE Init
		CLEAR
		this.AddProperty('oConn',;
		NEWOBJECT("ADODB.Connection"))
		this.AddProperty('oRS',;
		NEWOBJECT("ADODB.Recordset"))
		this.oConn.ConnectionString = ;
		"Provider=SQLOLEDB.1;Integrated Security=SSPI;"+;
"Persist Security Info=False;"+;
		"Initial Catalog=Northwind; Data Source=localserver"
		this.oConn.Open()
		this.oRS.ActiveConnection = this.oConn
		this.DataSourceType = "ADO"
		this.DataSource = this.oRS
		this.SelectCmd = ;
		"select customerid, companyname, contactname, "+;
		"contacttitle, address, city, country "+;
		"from customers where customerid LIKE 'C%'"	
		this.CursorFill()
	ENDPROC
ENDDEFINE