Extending ArcObjects  

PipeValidation Class Extension Example

This topic is relevant for the following:
Product(s): ArcGIS Desktop: All
Version(s): 9.0, 9.1, 9.2, 9.3
Language(s): VB6, VC++
Experience level(s): Intermediate to advanced

In this section:

  1. The case for a Pipe validation class extension
  2. Implementing a class extension

Pipe validation class extension example

Example Code Click here.

Description This project provides a custom validation of attributes, such that for any feature with a length greater than 10 meters, the valid values for MATERIAL are 'Coated Steel' or 'PVC'.

Design Subtype of FeatureClassExtension abstract class

License ArcEditor or above

Libraries  Geodatabase, Geometry, and System

Languages Visual Basic, Visual C++

Categories ESRI GeoObject ClassExtensions

Interfaces IClassExtension, IFeatureClassExtension, and IObjectClassValidation

How to use

    1. If using VB, register PipeValidationVB.dll, and double-click the PipeValidationVB.reg file to register to component categories.

      If using VC++, open and build the project PipeValidationVC.dsp to register the DLL and register to component categories.

    2. Open ArcMap. If using VB, add the PipesVB feature class from the Extending ArcObjects sample data. If using VC++, add the PipesVCpp feature class. These feature classes have been preconfigured with the example's class extension.
    3. Start editing and select all features in the class. Click the Editor menu and click Validate Features. Two features should be invalid. Select each feature individually and click Validate Features again. The reason for invalidity will be shown.

The case for a Pipe validation class extension

Imagine a network of water pipes. Pipes longer than 10 meters may only be made of PVC or coated steel, but shorter pipes may be made of many different materials. You would like to apply an attribute rule that ensures the material type as just described.

Pipes longer than 10 meters may only be made of coated steel or PVC.

The valid materials are dependent on the pipe length. The usual way to implement dependent validation is with subtypes, since each subtype within the object class can have a separate validation rule, and this can all be configured in ArcCatalog without any programming. However, in the example the dependency is on the pipe length, which is not a suitable attribute on which to base subtypes since there is no set of discrete values. A solution would be a custom attribute rule that validates objects on a combination of fields (for example, length and material) rather than just one field as normal. In the geodatabase the way to implement this behavior is with a class extension.

Implementing a class extension

Class extensions are the main way of providing custom geodatabase behavior. There are only two interfaces you must implement: IClassExtension and IObjectClassExtension. The latter interface is trivial, it just provides an identity for your extension. IFeatureClassExtension is a similar required identity interface if your extension applies to feature classes only. If you don't implement IObjectClassExtension, your extension will still work, but it won't conform to what is presented to developers on the ESRI object model diagrams.

Class extensions are not a way of making subclasses of the standard ObjectClass. Instead they provide an extension to the capabilities of ObjectClass.

The COM class that implements the class extension must be registered to the ESRI GeoObject Class Extensions component category. For details on how to apply a class extension to your data, see the next topic, 'Managing Class Extensions'.

Implementing IClassExtension

This is a simple interface, but possibly the most crucial. If there is an error in your code here, none of your users will be able to access the data in the object class.

The Init method is fired every time your object class is opened. If your object class is contained within a feature dataset, Init will fire as soon as any of the other feature classes are opened.

Within a feature dataset, if one feature class is opened, all the others are opened as well. This can cause your class extension's Init method to fire when you might not expect it.

You will typically use Init to initialize objects you want to store at the class level. The Pipe Validation example stores the index positions of the important fields to avoid recalculating them each time the field is used.

[Visual Basic 6]
Implements IClassExtension
Implements IObjectClassExtension
Implements IFeatureClassExtension
Implements IObjectClassValidation

Private m_iLengthField As Integer
Private m_iMaterialField As Integer
Private Const c_sMaterialField As String = "MATERIAL"
' HRESULT constant for returning errors
Private Const E_FAIL As Long = &H80004005

Private Sub IClassExtension_Init(ByVal pClassHelper _
  As esriGeoDatabase.IClassHelper, _
  ByVal pExtensionProperties As esriSystem.IPropertySet)
     ' Check that it is a linear feature class
     ' and that both length and material fields are present
     Dim pFeatureClass As IFeatureClass
     Set pFeatureClass = pClassHelper.Class
     If pFeatureClass.ShapeType <> esriGeometryPolyline Then
          Err.Raise E_FAIL, , "Not a linear feature class."
     End If
     Dim pLenField As IField
     Set pLenField = pFeatureClass.LengthField
     m_iLengthField = pFeatureClass.FindField(pLenField.Name)

     m_iMaterialField = pFeatureClass.FindField(c_sMaterialField)
     If m_iMaterialField = -1 Then
          Err.Raise E_FAIL, , "Required field not found: " & c_sMaterialField
     End If
End Sub

To run your class extension in the Visual Basic debugger, you will need a debug startup executable that registers your class to the correct component categories, then starts the appropriate application such as ArcMap.

For more details, see the description of the Compile and Register Add-In in the Component Categories topic.

There are two parameters to Init. The second, the class extension properties, is discussed in the next example. The first parameter, the class helper, is an intermediate object used to prevent circular references between an object class and a class extension. You should not keep a class-level variable referring to the object class; instead, keep a reference to this class helper object.

Note the error handling in the example—no message boxes are used to report the errors. You should avoid all user interface facilities in your class extension, since the geodatabase is independent of the user interface. Someone may want to use your object class extension from a nongraphical environment such as the command line, in which case message boxes would be inappropriate. It is better to pass the error back to the client application. In the example an HRESULT error number is used. This means that clients to the class extension will be able to handle the error appropriately whether they are written in Visual Basic or Visual C++.

Avoid unnecessary user interface functions in your class extension.

Implementing IObjectClassValidation

IObjectClassValidation provides custom validation of objects in addition to geodatabase validation of domains, relationship rules, and connectivity rules. After successfully completing all native validation within the geodatabase, the ValidateRow method is called. Effectively, this is the last type of validation performed when validating an object.

The ValidateRow method is called by an object's IValidate::Validate method and by the Validate methods on the IValidation interface of the associated object class. When implementing ValidateRow you will typically pass on the request to ValidateField, which provides the finer-grained validation.

[Visual Basic 6]
Private Function IObjectClassValidation_ValidateRow(ByVal Row As _
  esriGeodatabase.IRow) As String
     IObjectClassValidation_ValidateRow = _
       IObjectClassValidation_ValidateField(Row, c_sMaterialField)
End Function

The ValidateField method is called when IValidate::GetInvalidFields is called on an object of the associated object class. For both ValidateField and ValidateRow, if the field or row is invalid you should return an appropriate error string; otherwise, return a zero-length string.

[Visual Basic 6]
Private Function IObjectClassValidation_ValidateField(ByVal Row As _
  esriGeoDatabase.IRow, ByVal FieldName As String) As String
     Dim sError As String
     sError = ""

     If FieldName = c_sMaterialField Then
          Dim dLen As Double
          Dim sMaterial As String
          dLen = Row.Value(m_iLengthField)

          If IsNull(Row.Value(m_iMaterialField)) Then
               sMaterial = ""
               sMaterial = Row.Value(m_iMaterialField)
          End If

          If dLen > 10# _
          And (sMaterial <> "PVC" And sMaterial <> "Coated Steel") Then
               sError = "Value for " & c_sMaterialField & " is invalid." & _
                    vbNewLine & "If length is greater than 10m," & _
                    " only PVC and Coated Steel are valid."
          End If
     End If

     IObjectClassValidation_ValidateField = sError
End Function

Back to top

Go to the VB6 example code or the VC++ example code.

See Also About Class Extensions, Managing Class Extensions, Timestamper Class Extension Example, Class Extensions and Relationship Classes, and Class Extensions for Annotation and Dimensions.