In this section:
- The case for a Timestamper class extension
- Implementing a class extension with extension properties
- Implementing a feature class property page
Timestamper class extension example
Object Model Diagram Click here.
Example Code Click here.
Description This extension writes date and time information whenever an object is created or changed. The field names that are used to store the information are kept as properties of the extension, configurable via a property page. A new feature class or table with this extension can be created with the ArcCatalog user interface.
Design Subtype of ObjectClassExtension abstract class
License required ArcEditor
Libraries System, Geodatabase, and Framework
Languages Visual Basic, Visual C++
Categories ESRI GeoObject ClassExtensions, ESRI GeoObject ClassDescriptions, ESRI Table Property Pages, and ESRI Feature Class Property Pages
Interfaces IClassExtension, IObjectClassExtension, IObjectClassEvents, IObjectClassDescription, IFeatureClassDescription, and IComPropertyPage
How to use
- If using VB, register TimestamperVB.dll, and
double-click the TimestamperVB.reg file to register to component categories.
If using VC++, open and build the project TimestamperVC.dsp, to register the DLL and register to component categories.
- Using ArcCatalog, create a new feature class in a geodatabase of your choice. On the first page of the New Feature Class dialog box, there is a combo box for the type of custom object you will store in the feature class, choose 'Timestamped Feature Class'.
- After your feature class is created, right-click on it and choose Properties. Go to the Timestamping tab of the Properties dialog box and inspect the settings.
- In ArcMap, start editing the feature class and digitize some new features. Inspect the attributes of the features to see the timestamp details.
Imagine that you would like to keep records on who has been editing your data. For your most important feature classes and tables, you would like to know who created or modified any feature, and at what time the edit was made. In summary, you would like to timestamp data changes.
Whenever a table is edited, you would like to record the date, the time, and the person who made the change.
Timestamping can be achieved by customizing the editor to handle creation and modification events, but there may be several different applications editing the dataeach would need to be customized. It may also be awkward to add new feature classes to the scheme.
It is often preferable to implement this kind of business rule as close to the data as possible, to guarantee the rule is enforced. Class extensions offer a way of implementing this kind of rule in the geodatabase. In this example, a class extension solution is presented which uses IObjectClassEvents to trap creation and modification events. The solution also presents the use of class extension properties and a property page to administrate the timestamping. The IObjectClassDescription and IFeatureClassDescription interfaces are used so that a new timestamped table or feature class can easily be created.
It is recommended that you run the Timestamper sample before reading the rest of the text, particularly to see the property page in ArcCatalog.
You would like each timestamped object class to be able to use differently named fields to store the information. For example, in one feature class, a field called CREATION_DATE may be specified, whereas in another the field may be named CREATED. Additionally, you would like to be able to configure whether all the information is recorded. For example, you may want to record the creation date, but not the modification date or username. This configuration information belongs with the object class to which the extension is applied, so you need to store it within the geodatabase in the same way as the object class's metadataextension properties are provided for this purpose. The extension properties are stored within the geodatabase in the GDB_OBJECTCLASSES table.
A class extension may be applied to many different object classes. The extension properties allow each of those object classes to be configured differently.
In the example's Init routine, the extension properties are copied into class-level variables so they can be manipulated easily.
Private SubIClassExtension_Init( _
Setm_pClassHelper = pClassHelper m_sUsrName = Environ("USERNAME")
' If object class has been just created then,
' if the default fields are present, use them
Is Nothing Then CallTryDefaultProperties
' Load extension properties into the module variables
' Turn off errors so that if the property is not present
' the module variables will remain as a null string
On Error Resume Nextm_sCreFieldName = _ pExtensionProperties.GetProperty(c_sCreFieldPropName) m_sModFieldName = _ pExtensionProperties.GetProperty(c_sModFieldPropName) m_sUsrFieldName = _ pExtensionProperties.GetProperty(c_sUsrFieldPropName)
On Error GoTo0
The example uses a convention that an empty string for any of the class-level variables indicates that the timestamp field is not in use (for example, information about username is not being collected).
Extension properties can be used to persist almost any kind of data. For example, you may want to store a symbol, perhaps for custom rendering. The symbol would be kept in the database with the feature class and could be modified by the owner of the feature class.
Implementing your own custom interface
You can implement your own interfaces to provide functionality that is particular to your class extension. In the example, a new non-ESRI interface has been created, ITimestampClassExtension, which provides facilities to manage which timestamp fields are in use. This interface has been modelled after the IAnnoClassAdmin and IDimensionClassExtension interfaces. It has various read and write properties to alter the configuration of the timestamp fields and an UpdateProperties method to apply the changes to the geodatabase.
' Note that user should have an exclusive schema lock
' before calling this method
' Check if the specified fields exist
' Make the property set
NewesriSystem.PropertySet pPropSet.SetProperty c_sCreFieldPropName, m_sCreFieldName pPropSet.SetProperty c_sModFieldPropName, m_sModFieldName pPropSet.SetProperty c_sUsrFieldPropName, m_sUsrFieldName
' Update the schema
SetpClassSchemaEdit2 = m_pClassHelper.Class pClassSchemaEdit2.AlterClassExtensionProperties pPropSet
A developer using the class extension could choose to ignore this interface and make changes to the extension properties directly, but the methods and properties on ITimestampClassExtension make it easier. The developer should use ISchemaLock to gain an exclusive lock before changing the extension properties, since effectively the structure of the object class is being changed.
Implementing the IObjectClassEvents interface
IObjectClassEvents lets you catch the creation, modification, and deletion of objects. In the example, this is where the timestamps are made. The coding is simple, as is shown by the OnCreate event:
' Set the creation date and user name
' For Enterprise geodatabases, it is preferable to use the database
' date and username, but for simplicity this sample will just use the
' client OS date and username.
SetpRow = obj
IfLen(m_sCreFieldName) > 0
ThenpRow.Value(m_lCreField) = Now
End If IfLen(m_sUsrFieldName) > 0
ThenpRow.Value(m_lUsrField) = m_sUsrName
End If End Sub
The methods on IObjectClassEvents will be called by an object class before notifying other related and external objects.
Note that there is no need to call IRow::Store after making changes to the object within any of these methods. Indeed, it is Store or Delete that actually causes these events to fire. If, however, you make changes to objects other than the passed one, you will need to call Store as usual.
Note also that the field positions and username have been precalculated in global variables. If the user were to update a few thousand rows at once, this could save significant processor time.
For best performance, minimize the amount of processing in IObjectClassEvents.
The editor's undo and redo events do not cause object class events to fire. Any data edits made by the class extension will be undone or redone automatically, since they form part of the edit operation which is in progress when the object class event fires.
If you are using the OnCreate, OnDelete or OnChange methods to validate edit operations, do not call AbortEditOperation on the workspace if your logic indicates that the edit operation is invalid. Instead, raise an HRESULT error, which will be propagated to the application that is performing the edit on the class. It is the responsibility of the editing application that receives the error to abort the edit operation. This is especially true when editing with ArcMap. If you call AbortEditOperation from within the class extension, the ArcMap undo/redo edit stack will become unsynchronized.
Even if your class extension is concerned with data edits, you should not make any reference to the editor or any other application objects in your code, since they are not guaranteed to be present when your object class is opened.
Implementing the IObjectClassDescription and IFeatureClassDescription interfaces
An ObjectClassDescription provides information for ArcCatalog to use when creating a new object class or feature class. In the New Table or New Feature Class wizards, the ObjectClassDescription results in an entry in the custom object type combo box.
ArcCatalog's New Feature Class wizard uses feature class descriptions.
For a new table you implement just IObjectClassDescription. For feature classes you implement both IObjectClassDescription and IFeatureClassDescription. These interfaces can be implemented on a class extension or on a separate coclass.
In the example, you want to let ArcCatalog users create both new timestamped tables and new timestamped feature classes.
Two new coclasses have been made: on the first, just IObjectClassDescription is implemented; on the second, both IObjectClassDescription and IFeatureClassDescription.
In this example it would not have been possible to implement both interfaces on the class extension, since what is returned by the IObjectClassDescription::RequiredFields property is different for new tables than it is for new feature classes, as feature classes need a geometry field. Moreover, the InstanceCLSID property is different in each case.
Private Property GetIObjectClassDescription_RequiredFields() _
' Get the required fields for a feature class
SetpFieldsEdit = pOCDescription.RequiredFields
' Now add the timestamp fields
SetIObjectClassDescription_RequiredFields = pFieldsEdit
The code excerpt above makes use of the standard FeatureClassDescription coclass to provide the default ObjectID and geometry fields.
IObjectClassDescription::InstanceCLSID should return the UID of a geodatabase Object, Feature, network feature, custom object or custom feature, as appropriate.
IObjectClassDescription::ClassExtensionCLSID should return the UID of your class extension.
Private Property GetIObjectClassDescription_ClassExtensionCLSID() _
NewUID pUID.Value = "Timestamper.TimestampClassExtension"
SetIObjectClassDescription_ClassExtensionCLSID = pUID
The class that implements the description interfaces (whether it is the class extension or a separate coclass) must be registered to the ESRI GeoObject Class Descriptions component category.
Note the ModelName and ModelNameUnique properties on IObjectClassDescription. In fact model names for object classes must be always be unique within the geodatabase. Because of this fact, they are unsuitable for this kind of ObjectClassDescription, which can be applied to more than one object class. In the example the model name has been set to an empty string. It does not matter what value is returned for ModelNameUnique, as this property is now deprecated. The main use of model names is for UML modelling. Actually, if you create your object classes from a UML model with the Schema Wizard, ObjectClassDescriptions are unnecessary.
The ModelName property can normally be set to an empty string.
It is not possible to further customize either the New Table or New Feature Class wizard; for example, by adding a new page to configure your class extension. The timestamp class extension provides default configuration inside its Init routine, which is guaranteed to run after the object class is created.
Setm_pClassHelper = pClassHelper
' If object class has been just created then,
' if the default fields are present, use them
Is Nothing Then CallTryDefaultProperties
The timestamper example has a property page that can be used to configure which fields are being used for timestamping. This property page is a client to the custom interface implemented on the class extension, ITimestampClassExtension.
The vital code is in the IComPropertyPage_Applies function. This ensures that the property page is only displayed for object classes that have the timestamper class extension. See 'Developing Objects' for advice on the standard implementation of a property page class.
As BooleanIComPropertyPage_Applies =
'Apply if object class has a timestamp extension
If(Objects.Count < 1)
Then Exit Function End IfObjects.Reset
SetpObject = Objects.Next
Is Nothing If(
SetpObjectClass = pObject
Is Nothing Then If TypeOfpObjectClass.Extension
True Exit Function End If End If End If SetpObject = Objects.Next
Loop End Function
Your property page should be registered to the ESRI Feature Class Property Pages component category and, if appropriate, to nonspatial data as well such as ESRI Table Property Pages.
The timestamping property page appears when you view the properties of the feature class in ArcCatalog.