260 likes | 501 Views
Advanced geodatabase customization. Lesson 5 overview. Accessing features with COM Geodatabase customization directions Class extensions Custom features Exercise 5: MudBank custom feature. Application. Application. GeoDatabase API. Geodatabase API. COM. COM. Database. Database.
E N D
Lesson 5 overview • Accessing features with COM • Geodatabase customization directions • Class extensions • Custom features • Exercise 5: MudBank custom feature
Application Application GeoDatabase API Geodatabase API COM COM Database Database Accessing features
Application Clients GDB API COM Database Levels of geodatabase customization • Application • ArcMap Editor • Database • Class extensions • Custom features • Some application customizations can be accomplished at the database level • Example: Event handling for feature creation
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
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
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 ID Name CLSID EXTCLSID Feature classes 1 Laterals {3070721… {0368CF51… 2 Mains {3070721…
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 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; }
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; }
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))) }
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 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) } … }
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
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 accomplishedwith class extensions • Do not want to compromise GDB integrity
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 esriCore. AddDataCommand ICommand ICommand Instructor Demo
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 IFeatureDraw IFeatureDraw IFeatureEdit IFeatureEvents
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() };
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; }
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; }
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 ID Name CLSID EXTCLSID Feature classes 1 Laterals {3070721… {0368CF51… 2 Mains {8881110…
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-11D4-9601-0080C7E61A96}" //pClassSchemaEdit.AlterClassExtensionCLSID pUid, Nothing pClassSchemaEdit.AlterInstanceCLSID pUid End If End Sub
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
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
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 esriCore::Feature IFeature CMBClassExtension IMBClassExtension IClassExtension IFeatureClassExtension IFeatureClassDraw IFeatureRenderer CMBRenderer ILegendInfo IPersistStream IPropertyPage CMBRendererPropertyPage IPropertyPageContext
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?