In this topic
- Custom features versus other solutions
- Solving feature symbology
- Handling data edit events
- Overriding standard interfaces
- Other reasons to use custom features
Custom features versus other solutions
As you can see from the Tree custom feature example, implementing a custom feature presents a significant technical challenge. Why would you choose the custom feature approach instead of other solutions? Typically the alternatives will be a class extension or a customization of the application, such as a tool, or an editor extension.
One of the most significant reasons not to implement custom features is that you must use a development environment that supports aggregation, for example, Visual C++. Unlike class extensions, custom features cannot be implemented with Visual Basic 6.
Another point to note is that there are no significant limitations of class extensions. All the functionality you would generally need to extend the behavior of a feature is available at the class extension level.
The following sections explore in more detail at why you should or shouldn't implement a custom feature. The table below shows a summary of the advantages and disadvantages. The reasons for customizing the geodatabase as opposed to the application were summarized in the section on class extensions, so they are not included here.
Custom features |
Other solutions | |
Advantages |
|
|
Disadvantages |
|
|
Solving feature symbology
A common reason to consider custom features is difficult symbology requirements. By implementing IFeatureDraw on your feature, you can control exactly how the feature is displayed. However, instead of a custom feature, it is normally preferable to implement a custom renderer.
Instead of solving symbology with a custom feature, it is normally preferable to implement a custom renderer.
As an example, consider a polygon feature class of buildings that, beyond a certain scale, you would like to display as points rather than polygons.
For a custom feature, the implementation of IFeatureDraw::Draw in this case is fairly simple, but note that code to get the current map scale needs to be reexecuted for each feature drawn. With a custom renderer you would only need to get the map scale just once at the start of the drawing loop. Although in this case the processing time difference is small, it demonstrates a common issue with custom features—a lot of redundant recalculation can be required for each feature. When drawing thousands of features this can result in custom features being significantly slower than custom renderers.
The custom feature solution is more object oriented and probably more elegant since it does not involve programming a loop. The custom renderer solution however, gives you complete control over the entire drawing process in one piece of code rather than implementing a routine that is called repeatedly. For more details about custom renderers, see the Extending the Display section.
Whether you implement a custom renderer or a custom feature, you will find IFeatureClassDraw useful. Custom renderers can be associated with feature classes in the geodatabase by implementing IFeatureClassDraw on the class extension. For custom features that implement IFeatureDraw, you should set the IFeatureClassDraw::DoesCustomDrawing property to true. For both custom features and custom renderers, RequiredFieldsForDraw should be used to define the fields that need to be fetched for display.
Another relative disadvantage of the custom feature approach to symbology is that the symbol in the ArcMap table of contents does not reflect the display. You can work around this by implementing IFeatureClassDraw to provide a default unchangeable renderer (set ExclusiveCustomRenderer to true) that also implements ILegendInfo to control the table of contents. You should also make a renderer property page to show the symbology on the layers property page.
Note that it is possible to mix custom renderers with custom features, if the renderer calls IFeatureDraw::Draw to display the feature rather than using the display to draw the shape directly. This can be powerful if the custom feature is limited to one aspect of the rendering and you require different options for the remainder.
Handling data edit events
Custom features can implement IRowEvents, IRelatedObjectEvents and IFeatureEvents to handle edit events. The following table shows the equivalence between the former two interfaces and those on class extensions, which provide equivalent functionality.
Custom feature interfaces |
Class extension interfaces |
IRowEvents |
IObjectClassEvents and IObjectClassValidation |
IRelatedObjectEvents |
IRelatedObjectClassEvents and IRelatedObjectClassEvents2 |
In general, it is preferable to use class extensions to handle these geodatabase events. A disadvantage of IRowEvents and IRelatedObjectEvents is that the events might occur before row state is fully determined, in other words, the events are not triggered at the end of all possible behavior execution. The comparable class extension events, however, are triggered last so they are more stable for this type of customization. This is especially important for rows that have related objects and also for network features.
It is normally better to use class extensions to handle geodatabase events.
IFeatureEvents provides events that are related to geometry changes. It does not have a class extension equivalent, but you should not normally implement it with custom features. The OnSplit event is not generally useful, since it does not provide access to the two new features (the normal way to handle split and merge policies is through Domain objects). The OnMerge event is currently reserved by ESRI for future use; it is currently not triggered by ArcGIS. The InitShape event is currently only triggered when a complex junction is added to a geometric network. Another similar interface, INetworkFeatureEvents, is currently reserved by ESRI, its methods are not consistently triggered by ArcGIS.
Note that the IRowChanges interface is commonly consumed by custom feature (and class extension) developers; however, it is never reimplemented.
Overriding standard interfaces
Custom features give you almost total control in a way that class extensions do not. This is particularly apparent with the capability of using containment to override the standard ESRI interfaces. COM containment (also known as delegation) cannot be used to implement an entire custom feature since there are some interfaces on the Feature coclass that cannot be contained, as they are internal to ArcGIS and hidden.
However, after aggregating a feature, you can contain the interfaces you want to customize. The individual methods on the contained interfaces can then either be implemented in the customized class or the method call can be passed on to the appropriate method on the contained interface.
As an example, consider the situation of a feature being rotated by an ArcMap user. You would like to intercept this event and adjust some attribute of the feature according to the amount of rotation. With a class extension you could place code in IObjectClassEvents::OnChange, but it would be hard to determine the amount of rotation that had taken place. With a custom feature you could override IFeatureEdit and place your custom code in the RotateSet method before delegating the call to the inner aggregated object.
You can use COM containment to override the standard interfaces of a feature. This kind of customization is not recommended in general.
It should be noted that this kind of customization is not recommended in general, for several reasons. First, nearly all scenarios can be handled by class extensions—the rotation example is slightly contrived and is one of the few exceptions. Second, you can also control functionality by customizing the application. As an example of this, consider the ArcMap Rotate tool, which handles the special case of point features whose symbols are rendered according to an angle attribute value of the feature. This attribute value is automatically updated when the feature is rotated with the tool. In this case it is the application object (the tool), rather than the database object (the feature), that is handling the special circumstance. Third, the custom feature solution is probably the least stable location for the customization. As has already been mentioned, your custom code is executed before the row state is fully determined; moreover, your own code may trigger further geodatabase behavior, possibly leading to complex scenarios.
There are no theoretical restrictions to you overriding some of the most basic feature interfaces such as IRow; however, this is not recommended—if you did this, your feature class may not integrate correctly with the rest of ArcGIS.
Other reasons to use custom features
Complex junctions can only be implemented as custom features. However, there is a lot of overhead (for example, building the network and complexities in coding) that makes implementing complex junctions prohibitively expensive.
By implementing IFeatureSnap, it is possible to provide custom snapping functionality for a feature class. This facility was designed specifically for connection points to complex junctions. Usually it is easier to implement a normal snap agent configured to use just your feature class.
Custom features may be a suitable solution if you want to persist objects with your features in the database. As an example, annotation features persist text elements. You should be aware that persisting objects in this way could dramatically increase the size of each row stored in the database, with severe performance results. Annotation feature classes work around this problem with the SymbolCollection object.
See Also:
About custom featuresTree custom feature example
Making a class extension with your custom feature
Managing custom features