Presentation is loading. Please wait.

Presentation is loading. Please wait.

Advanced geodatabase customization

Similar presentations

Presentation on theme: "Advanced geodatabase customization"— Presentation transcript:

1 Advanced geodatabase customization
Advanced ArcObjects Component Development II (C++)

2 Lesson 5 overview Accessing features with COM
Geodatabase customization directions Class extensions Custom features Exercise 5: MudBank custom feature Advanced ArcObjects Component Development II (C++)

3 Accessing features Application Application COM Geodatabase API COM
All applications communicate with the geodatabase API to access features. Retrieving a feature is a multi-stage process. Initially, a client accesses the database and a COM feature is created. Next, the geometry information is passed back from the database and a geometry object is created. Both the feature and geometry objects are passed back to the client. If the client does not have the COM class registered that supports the feature, then the feature class cannot be accessed. Advanced ArcObjects Component Development II (C++)

4 Levels of geodatabase customization
Application COM GDB API Clients Database Application ArcMap Editor Database Class extensions Custom features Some application customizations can be accomplished at the database level Example: Event handling for feature creation There are two levels of geodatabase customization: application and database. Customizations tied into applications such as ArcMap or ArcCatalog™ are application-level customizations. Customizations written as class extensions or custom features are independent of client applications and are considered database-level customizations. It should be recognized that some application-level customizations can be accomplished at the database-level. Moving your application code to the database prevents developers from having to maintain application code for each client that can access the database (e.g., ArcMap and ArcCatalog). When a database extension is created, it forces all clients to have the database extension DLL registered on their system before the data can be accessed. This mechanism is how database integrity is preserved. Ensure your database extensions do not adversely affect the performance of the database itself. Advanced ArcObjects Component Development II (C++)

5 Database-level customizations
Change the default behavior of a feature class (e.g., new validation rules, custom database updates) Two ways to implement behavior Class extensions: Only loaded once when class is created Custom features/objects: Applies to feature when created Only applies to feature classes in a Personal or ArcSDE geodatabase Custom behavior can be broken down into customizations involving class extensions and custom objects. The main difference between a class extension and custom object is the fact that class extensions are only loaded once when the class is created, whereas custom features are created each time a feature is accessed or created. Advanced ArcObjects Component Development II (C++)

6 Class extensions Adds class-level behavior to object classes
Higher-level customization than custom features Object is created when data is accessed Common uses Custom editing behavior Custom rules and validation Custom drawing: Renderers Custom property inspector Related object notification Custom split policies Class extensions provide a mechanism for developers to extend the behavior of object classes. Custom rules, renderers, and inspectors are some examples of the types of customizations possible. These extensions apply to every client accessing the data. Advanced ArcObjects Component Development II (C++)

7 Relationship between a class and an extension
Only one extension can be associated with a class Component requires registration On each client system In categories: ESRI GeoObject Class Extension Must associate class with new class extension Insert GUID with UML or ArcObjects code ClassExtension Although all custom behavior resides at the geodatabase level, the implementation code actually resides in a component on a client machine; therefore, when you create new behavior, such as a new class extension, the GUID of the component must be associated with the appropriate class. This is accomplished either through UML modeling or with ArcObjects code. ID Name CLSID EXTCLSID Feature classes 1 Laterals { … {0368CF51… 2 Mains { … Advanced ArcObjects Component Development II (C++)

8 Accessing the class from the extension
IClassExtension::Init() IClassHelper – Reference is passed in IClassHelper::Class – Acquire a reference to the class (e.g., m_ipClass) IClassExtension::Shutdown() Release m_ipClass reference When a class extension is initialized, a reference to the class itself is passed in. You can use this reference to access the class that supports the class extension. Once you have the class reference, you can acquire a number of properties associated with the class itself, including the CLSID. In the extension implementation, be sure to remove the reference to the class when the extension is shut down in order to avoid circular references. A ClassHelper object is passed into the extension upon initialization. This has one property with a reference back to the class itself. This class ensures that there are no cyclical references to the object class when the extension attempts to unload. STDMETHODIMP CMBClassExtension::Init(IClassHelper *pClassHelper,IPropertySet *pPropSet) { if (!pClassHelper) return E_POINTER; HRESULT hr; IClassPtr ipClass; hr = pClassHelper->get_Class(&m_ipClass); return S_OK; } Advanced ArcObjects Component Development II (C++)

9 Accessing data with a class extension
Data can be stored with a feature class Clients can access data via extension PropertySet is passed in upon initialization Can also write data (property set) to the class IClassSchemaEdit::AlterClassExtensionProperties STDMETHODIMP CMBClassExtension::Init(IClassHelper *pClassHelper,IPropertySet *pPropSet) { // Read stored symbol from property set CComVariant vSymbol; pPropSet->GetProperty(CComBstr("symbol"), &vSymbol); IUnknownPtr ipUnk(vSymbol.ppUnkVal); m_ipSymbolForFeatures = ipUnk; } Class extensions allow you to persist data with a feature class. The data is stored in the database as a property set. When the class extension initializes, a reference is passed into the property set from which it can extract values. A client can use these values to control certain program flow. It is possible to update the property set through IClassSchemaEdit::AlterClassExtensionProperties if you are the only user accessing the class. Care should be taken however, because if every client has the ability to update a class extension’s property set, then the last user to access he data will have the ability to change data and overwrite everyone else’s customizations. This is not the proper usage. The property set should be used as a read-only property by all clients except for one. One administrative client should have access to the property set so the data can be updated, if need be. This way all clients accessing the data will be affected equally. One example of this might be the global path to some data that the extension requires (i.e., \\Server\ServerData). Advanced ArcObjects Component Development II (C++)

10 ClassExtensions and object events
Implement IObjectClassEvents Not an outbound interface Similar to Editor events // TrackLifecycleExt.h class ATL_NO_VTABLE CTrackLifecycleExt : public ITrackLifecycleExt, public IClassExtension, public IObjectClassEvents {… // TrackLifecycleExt.cpp // IClassExtension STDMETHODIMP TrackLifecycleExt::Init (IClassHelper * pClassHelper, IPropertySet * pExtensionProperties) {… IClassPtr ipClass; ipFeatureClass->FindField (CComBSTR( L"CreationDate"),&m_creationDate)))… } // IObjectClassEvents STDMETHODIMP CTrackLifecycleExt::OnCreate (IObject * obj) SYSTEMTIME systemTime; DOUBLE dTime; ::GetLocalTime(&systemTime); ::SystemTimeToVariantTime(&systemTime, &dTime); CComVariant vTime(dTime); if (FAILED(hr = obj->put_Value (m_creationDate, vTime))) Feature class extensions can listen for the creation, modification, and deletion of features. This same functionality is available via IEditorEvents, but as mentioned before, it is often more desirable to move this code down to the database level. Advanced ArcObjects Component Development II (C++)

11 ClassExtensions and custom drawing
Basic requirements 1. Implement IFeatureClassDraw 2. Renderer: esriCore or a custom renderer 3. Property page: esriCore or custom page IFeatureClassDraw members HasCustomRenderer – You have written your own renderer CustomRenderer – Reference render to use always ExclusiveCustomRenderer – User can change renderers via GUI CustomRendererPropPageCLSID – Your custom property page Custom drawing can be accomplished easily with feature class extensions by implementing IFeatureClassDraw. The implementation also requires the usage of a renderer to perform the drawing. The advantage here is that regardless of what client is accessing the data (e.g., ArcMap or ArcCatalog), the same renderer will be used to draw the features. A custom renderer can also be used in place of the default esriCore renderers if desired. Advanced ArcObjects Component Development II (C++)

12 Custom renderers Implement IFeatureRenderer
Draw, PrepareFilter, ExclusionSet ILegendInfo – Create symbols for the TOC IPersistStream – Store LegendGroup and symbols STDMETHODIMP CMBRenderer::Draw(IFeatureCursor *pFeatureCursor,esriDrawPhase drawPhase,IDisplay *pDisplay,ITrackCancel *pTrackCancel) { // Just draw special if Geography, otherwise use default if (drawPhase != esriDPGeography) return E_NOTIMPL; // Loop through all the MudBanks, unioning all the Mudbanks into one polygon IGeometryPtr ipMudBankGeom(CLSID_Polygon); IFeaturePtr ipFeature; IMBClassExtensionPtr ipClassExtension; while (pFeatureCursor->NextFeature(&ipFeature) == S_OK) // Do specialized drawing here… ipFeature->get_Shape(&ipFeatureGeom); ::Draw(pDisplay, ipSym, ipLandGeom) } Custom renderers are fairly simple to implement. This customization requires that you handle the actual drawing of the features on a feature-by-feature basis. You have the option of preparing a queryfilter and an exclusion set so only a subset of the features are drawn. It is common to implement ILegendInfo and IPersistStream in a renderer so the LegendInfo object and symbols can be stored in an .mxd. Advanced ArcObjects Component Development II (C++)

13 Methods of applying class extensions
During creation IFeatureWorkspace::CreateFeatureClass(pExtCLSID…) After creation IClassSchemaEdit::AlterClassExtensionCLSID(pExtCLSID…) After creation and class extension is missing IFeatureWorkspaceSchemaEdit::AlterClassExtensionCLSID When modeling and designing in Visio Create class extension and make association via UML Set the CLSID tagged value to the component There are four ways to apply class extensions. The methodology you choose depends on how far you are into the database and application development process. If you are using Visio and are designing a new database and application, then you should use Visio to lay out the interfaces and members for the extension. If you are not using Visio but are creating the class programmatically, then you have the option of using the default interface assigned to the object, or creating your own. Advanced ArcObjects Component Development II (C++)

14 Custom objects Extends row-level behavior for a class
Requires aggregation of an esriCore::Feature Why create custom objects? Complex network features: Switch gear Custom features: Drawing, row updating ESRI: Feature-link annotation and dimension The reality… Most customizations can be accomplished with class extensions Do not want to compromise GDB integrity The lowest level of behavior that can be implemented is by creating custom objects. This type of customization requires a COM-development language that supports aggregation in order to reuse functionality in the esriCore::Feature object. You might choose this development direction when the functionality you desire cannot be accomplished through feature class extensions and other objects. The key issue to be aware of is not to compromise the integrity of the system by poorly writing your custom object. For example, writing custom editing or drawing behavior can adversely affect the performance of the system. Advanced ArcObjects Component Development II (C++)

15 COM containment Simplest form of binary reuse
Delegate requests to inner object Provide custom behavior for selected members Can wrap any coclass (e.g., esriCore commands) IUnknown MyAddDataCmd IUnknown (inner) IMyAddDataCmd The simplest form of binary reuse is containment. With containment, the contained object (inner) has no knowledge that it is contained within another object (outer). The outer must implement all the interfaces supported by the inner. When requests are made on these interfaces the outer simply delegates them to the inner. To support new functionality the outer can either implement one of the interfaces without passing the calls on, or implement an entirely new interface in addition to those interfaces from the inner object. In this example, esriCore AddDataCommand command is contained by MyAddDataCmd object. All of the requests to MyAddDataCmd are forwarded to the esriCore AddDataCommand object. When MyAddDataCmd::OnClick() executes, it performs the password validation and then forwards the request on to the inner object. If the password the user enters is incorrect, then the inner object never receives the call. This is one way to reuse a component’s functionality and alter the behavior of an object. esriCore. AddDataCommand ICommand ICommand Instructor Demo Advanced ArcObjects Component Development II (C++)

16 COM aggregation Use to create custom features
Exposes interfaces of inner object directly Provide implementation for contained interface/methods IUnknown (controlling Unknown) CustomFeature ICustomFeature IUnknown (inner) Feature IFeature IFeatureBuffer COM aggregation involves the parent object exposing its interfaces to clients of the child object. Using this technique, the child does not need to implement any interfaces of the parent. The parent object is aware that it is aggregated into another object, and can access that other object if required. The parent object must access the child object if an interface of the child object is requested through a query interface operation performed on the parent object. The parent accesses the child object via a pointer to the child objects IUnknown interface, this pointer is known as the controlling unknown. Custom features make use of both containment and aggregation. A developer aggregates the interfaces where no customizations are required, and contains those that are to be customized. The individual methods on the contained interfaces can then either be implemented in the customized class, thus providing custom functionality, or the method call can be passed to the appropriate method on the contained interface. IFeatureDraw IFeatureDraw IFeatureEdit IFeatureEvents Advanced ArcObjects Component Development II (C++)

17 ATL macros for aggregation
One macro exposes controlling IUnknown (outer) Another allows the class to be aggregated Implement the interfaces you wish to contain // MBFeature.h : Declaration of the CMBFeature public: CMBFeature() : m_pInnerUnk(NULL) { } DECLARE_GET_CONTROLLING_UNKNOWN() BEGIN_COM_MAP(CMBFeature) COM_INTERFACE_ENTRY(IMBFeature) COM_INTERFACE_ENTRY(ISupportErrorInfo) COM_INTERFACE_ENTRY(IFeatureDraw) COM_INTERFACE_ENTRY_AGGREGATE_BLIND(m_pInnerUnk) END_COM_MAP() BEGIN_CATEGORY_MAP(CMBFeature) // Register in this component category IMPLEMENTED_CATEGORY(CATID_GeoObjects) END_CATEGORY_MAP() }; Aggregation is made simple in ATL through a number of macros. DECLARE_GET_CONTROLLING_UNKNOWN allows your class to access the controlling unknown at any time. COM_INTERFACE_ENTRY_AGGREGATE_BLIND tells the system to aggregate the object and expose this interface. You will write code to override the behavior of selected members for this interface. Lastly, the category map macro registers the custom feature with component categories. Advanced ArcObjects Component Development II (C++)

18 Aggregating an esriCore::Feature in ATL
Cocreate esriCore object in FinalConstruct() Hook inner and outer objects together via IUnknown Get a reference to inner interface for containment // MBFeature.cpp HRESULT CMBFeature::FinalConstruct() { // Get outer object since this object may be aggregated in as well. IUnknown * pOuter = GetControllingUnknown(); // Aggregate in ESRI's simple Feature object if (FAILED (CoCreateInstance(CLSID_Feature, pOuter, CLSCTX_INPROC_SERVER, IID_IUnknown, (void**) &m_pInnerUnk))) return E_FAIL; // Get reference to inner feature IFeatureDraw if (FAILED(m_pInnerUnk->QueryInterface(IID_IFeatureDraw, (void**)&m_pFeatureDraw))) return E_FAIL; return S_OK; } This example illustrates the correct way to aggregate in an esriCore::Feature object. Notice that the outer unknown reference is passed in to the CoCreateInstance() function. Lastly, since we will be overriding some of the behavior of IFeatureDraw, we hold on to a reference to the inner object’s IFeatureDraw interface. This way we can control what is delegated to the inner object and what is contained. Advanced ArcObjects Component Development II (C++)

19 Custom drawing behavior
Call Draw() function in IFeature::Draw // MBFeature.cpp // IFeatureDraw STDMETHODIMP CMBFeature::get_InvalidArea(IInvalidArea** ppInvalidArea) { return m_pFeatureDraw->get_InvalidArea(ppInvalidArea); // Use containment } STDMETHODIMP CMBFeature::putref_InvalidArea(IInvalidArea* pInvalidArea) return m_pFeatureDraw->putref_InvalidArea(pInvalidArea); // Use containment // Override the Draw method of IFeatureDraw STDMETHODIMP CMBFeature::Draw(esriDrawPhase drawPhase, IDisplay* pDisplay, ISymbol* pSymbol, VARIANT_BOOL SymbolInstalled, IGeometry* pGeometry, esriDrawStyle drawStyle) // Get the ClassExtension because it has the symbols and methods ipClassExtension->get_LandSymbol(&ipSym); ipClassExtension->get_IntersectingLandGeometry(ipMudBankGeom,&ipLandGeom); ::Draw(pDisplay, ipSym, ipLandGeom); return S_OK; This example illustrates how to delegate requests to the inner (aggregated) object and how to contain other members. The get_InvalidArea and putref_InvalidArea members are simply contained and delegated to the inner object. The Draw member is overridden and custom drawing behavior is provided. Advanced ArcObjects Component Development II (C++)

20 Relationship between a feature and a class
One feature object can be associated with a class Component requires registration On each client system In categories: ESRI GeoObjects Must associate feature with class Insert GUID with UML or ArcObjects code CustomFeature Although all custom behavior resides at the geodatabase level, the implementation code actually resides in a DLL on the client machine; therefore, when you create new behavior, such as a new custom feature, the GUID of the component must be associated with the appropriate class. This is accomplished either through UML modeling or with ArcObjects code. ID Name CLSID EXTCLSID Feature classes 1 Laterals { … {0368CF51… 2 Mains { … Advanced ArcObjects Component Development II (C++)

21 Registering a custom feature
Use the selected object in ArcCatalog Access IClassSchemaEdit, just like class extensions Public Sub LoadClassExtension() Dim pGxApp As IGxApplication Dim pGxCatalog As IGxCatalog Dim pGxSelection As IGxSelection Set pGxApp = Application Set pGxCatalog = pGxApp.Catalog Set pGxSelection = pGxCatalog.Selection Dim pGxEnum As IEnumGxObject Set pGxEnum = pGxSelection.SelectedObjects pGxEnum.Reset Dim pGxObject As IGxObject Set pGxObject = pGxEnum.Next If (pGxObject Is Nothing) Then Set pGxObject = pGxCatalog.SelectedObject If (TypeOf pGxObject Is IGxDataset) Then Dim pGxDataset As IGxDataset Dim pObjectClass As IObjectClass Dim pClassSchemaEdit As IClassSchemaEdit Set pGxDataset = pGxObject ' QI Set pObjectClass = pGxDataset.Dataset Set pClassSchemaEdit = pObjectClass Dim pUid As New UID pUid.Value = "{82F517DC-C600-11D C7E61A96}" //pClassSchemaEdit.AlterClassExtensionCLSID pUid, Nothing pClassSchemaEdit.AlterInstanceCLSID pUid End If End Sub This example illustrates how to update the feature CLSID for a feature class. IClassSchemaEdit::AlterInstanceCLSID is the member that assigns the new custom feature GUID to the class itself. After assigning the GUID, the feature class will exercise the behavior provided by the custom feature. Advanced ArcObjects Component Development II (C++)

22 Modeling custom features in Visio
Inherit from esriCore::Feature Assign custom feature via tagged values (CLSID) Define other public members if necessary Export to repository Custom features and feature classes can also be modeled in Visio. In this case, a simple custom feature called Building is associated with a CLSID that points to the custom feature GUID. A CLSID tagged value should be used to associate the feature with the custom feature object. Also note that the Building class also supports a custom interface named IBuilding, which has two members. Advanced ArcObjects Component Development II (C++)

23 Building a Visual Studio workspace
Use ESRI Code Generation add-in to import repository Creates an ATL project Aggregates an esriCore::Feature object Choose interface and members to contain Adds public members modeled in Visio Example: IBuilding::Age() and IBuilding::Owner() Create the schema Import repository to a database in ArcCatalog Automatically applies GUIDs modeled See Case Tools tutorial for more details After a UML model is imported into ArcCatalog and the database is created, the next step is to import the repository into Visual Studio. ESRI provides an ESRI Code Generation add-in that can be used for this task. During the import process, the wizard will ask you to identify the interfaces and members you wish to contain and delegate to. Upon completion, it will create the project workspace and create all of the class files necessary to support your custom feature. It is your job at the end, to write the custom implementation of the feature for those interfaces and members to override. Advanced ArcObjects Component Development II (C++)

24 CMBRendererPropertyPage
Exercise 5 overview IUnknown Aggregate in an esriCore::Feature Implement IFeatureDraw Implement a class extension Add a renderer and property page Challenge: Persistence IFeatureDraw CMBFeature IFeature esriCore::Feature IMBClassExtension CMBClassExtension IClassExtension IFeatureClassExtension IFeatureClassDraw IFeatureRenderer CMBRenderer ILegendInfo IPersistStream IPropertyPage CMBRendererPropertyPage IPropertyPageContext Advanced ArcObjects Component Development II (C++)

25 Review When should you write a custom feature?
How do you register a custom feature with a feature class? What other objects are often created with custom features or extensions? When should you use IEditorEvents versus IObjectClassEvents? What happens when a client application accesses a feature class, but does not have the custom feature DLL registered on their system? Advanced ArcObjects Component Development II (C++)

26 Advanced ArcObjects Component Development II (C++)

Download ppt "Advanced geodatabase customization"

Similar presentations

Ads by Google