copyright © 2002 esri. all rights reserved. advanced arcobjects component development ii (c++)...
TRANSCRIPT
Copyright © 2002 ESRI. All rights reserved. Advanced ArcObjects Component Development II (C++)
Advanced geodatabase customization
5-2Advanced ArcObjects Component Development II (C++)Copyright © 2002 ESRI. All rights reserved.
Lesson 5 overview
Accessing features with COM
Geodatabase customization directions
Class extensions
Custom features
Exercise 5: MudBank custom feature
5-3Advanced ArcObjects Component Development II (C++)Copyright © 2002 ESRI. All rights reserved.
Accessing features
COM
GeoDatabase API
Application
DatabaseCOM
Geodatabase API
Application
Database
5-4Advanced ArcObjects Component Development II (C++)Copyright © 2002 ESRI. All rights reserved.
Application ArcMap Editor
Database Class extensions
Custom features
Some application customizations can be accomplished at the database level Example: Event handling for feature creation
Levels of geodatabase customization
Application
COM
GDB API
Clients
Database
5-5Advanced ArcObjects Component Development II (C++)Copyright © 2002 ESRI. All rights reserved.
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
5-6Advanced ArcObjects Component Development II (C++)Copyright © 2002 ESRI. All rights reserved.
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
5-7Advanced ArcObjects Component Development II (C++)Copyright © 2002 ESRI. All rights reserved.
Relationship between a class and an extension
NameID
1 Laterals
CLSID{3070721…
EXTCLSID{0368CF51…Feature classes
ClassExtension
2 Mains {3070721…
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
5-8Advanced ArcObjects Component Development II (C++)Copyright © 2002 ESRI. All rights reserved.
ClassExtensionIClassExtension
0..1
IC lassE xtension : IUnknown
In it (in pC lassHelper: IC lassHelper, inpE xtens ionP roperties : IP ropertyS et)
S hutdow n
TableIClass IC lass : IUnknown
CLSID : IU IDE X TCLS ID : IU IDE xtension: IUnknownE xtens ionP roperties : IP ropertyS etFie lds : IF ie ldsHasOID : B ooleanIndexes: IIndexesOID Fie ldNam e: S tring
A ddFie ld (in Fie ld: IF ie ld)A ddIndex (in Index: IIndex)Dele teFie ld (in Fie ld: IF ie ld)Dele teIndex (in Index: IIndex)FindFie ld (in Nam e: S tring): Long
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;}
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;}
5-9Advanced ArcObjects Component Development II (C++)Copyright © 2002 ESRI. All rights reserved.
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 classIClassSchemaEdit::AlterClassExtensionProperties
STDMETHODIMP CMBClassExtension::Init(IClassHelper *pClassHelper,IPropertySet *pPropSet){
// Read stored symbol from property setCComVariant vSymbol;
pPropSet->GetProperty(CComBstr("symbol"), &vSymbol);
IUnknownPtr ipUnk(vSymbol.ppUnkVal);m_ipSymbolForFeatures = ipUnk;
}
STDMETHODIMP CMBClassExtension::Init(IClassHelper *pClassHelper,IPropertySet *pPropSet){
// Read stored symbol from property setCComVariant vSymbol;
pPropSet->GetProperty(CComBstr("symbol"), &vSymbol);
IUnknownPtr ipUnk(vSymbol.ppUnkVal);m_ipSymbolForFeatures = ipUnk;
}
5-10
Advanced ArcObjects Component Development II (C++)Copyright © 2002 ESRI. All rights reserved.
ClassExtensions and object events
Implement IObjectClassEvents
Not an outbound interface
Similar to Editor events
// TrackLifecycleExt.cpp// IClassExtensionSTDMETHODIMP TrackLifecycleExt::Init (IClassHelper * pClassHelper, IPropertySet * pExtensionProperties){… IClassPtr ipClass; ipFeatureClass->FindField (CComBSTR( L"CreationDate"),&m_creationDate)))…}// IObjectClassEventsSTDMETHODIMP 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)))}
// TrackLifecycleExt.cpp// IClassExtensionSTDMETHODIMP TrackLifecycleExt::Init (IClassHelper * pClassHelper, IPropertySet * pExtensionProperties){… IClassPtr ipClass; ipFeatureClass->FindField (CComBSTR( L"CreationDate"),&m_creationDate)))…}// IObjectClassEventsSTDMETHODIMP 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)))}
// TrackLifecycleExt.hclass ATL_NO_VTABLE CTrackLifecycleExt : public ITrackLifecycleExt, public IClassExtension, public IObjectClassEvents{…
// TrackLifecycleExt.hclass ATL_NO_VTABLE CTrackLifecycleExt : public ITrackLifecycleExt, public IClassExtension, public IObjectClassEvents{…
5-11
Advanced ArcObjects Component Development II (C++)Copyright © 2002 ESRI. All rights reserved.
ClassExtensions and custom drawing
Basic requirements
1. Implement IFeatureClassDraw
2. Renderer: esriCore or a custom renderer
3. Property page: esriCore or custom page
IFeatureClassDraw membersHasCustomRenderer – You have written your own renderer
CustomRenderer – Reference render to use always
ExclusiveCustomRenderer – User can change renderers via GUI
CustomRendererPropPageCLSID – Your custom property page
5-12
Advanced ArcObjects Component Development II (C++)Copyright © 2002 ESRI. All rights reserved.
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 defaultif (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)
}…}
STDMETHODIMP CMBRenderer::Draw(IFeatureCursor *pFeatureCursor,esriDrawPhase drawPhase,IDisplay *pDisplay,ITrackCancel *pTrackCancel){
// Just draw special if Geography, otherwise use defaultif (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)
}…}
5-13
Advanced ArcObjects Component Development II (C++)Copyright © 2002 ESRI. All rights reserved.
Methods of applying class extensions
During creation IFeatureWorkspace::CreateFeatureClass(pExtCLSID…)
After creation IClassSchemaEdit::AlterClassExtensionCLSID(pExtCLSID…)
After creation and class extension is missingIFeatureWorkspaceSchemaEdit::AlterClassExtensionCLSID
When modeling and designing in Visio Create class extension and make association via UML
Set the CLSID tagged value to the component
5-14
Advanced ArcObjects Component Development II (C++)Copyright © 2002 ESRI. All rights reserved.
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
5-15
Advanced ArcObjects Component Development II (C++)Copyright © 2002 ESRI. All rights reserved.
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)
esriCore.AddDataCommand
IUnknownMyAddDataCmd
ICommand
IUnknown (inner)
ICommand
IMyAddDataCmd
Instructor DemoInstructor Demo
5-16
Advanced ArcObjects Component Development II (C++)Copyright © 2002 ESRI. All rights reserved.
COM aggregation
Use to create custom features
Exposes interfaces of inner object directly
Provide implementation for contained interface/methods
ICustomFeature
Feature
IUnknown(controlling Unknown)CustomFeature
IFeature
IUnknown (inner)
IFeatureDraw
IFeatureBuffer
IFeatureDraw
IFeatureEdit
IFeatureEvents
5-17
Advanced ArcObjects Component Development II (C++)Copyright © 2002 ESRI. All rights reserved.
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 CMBFeaturepublic:
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 categoryIMPLEMENTED_CATEGORY(CATID_GeoObjects)
END_CATEGORY_MAP()};
// MBFeature.h : Declaration of the CMBFeaturepublic:
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 categoryIMPLEMENTED_CATEGORY(CATID_GeoObjects)
END_CATEGORY_MAP()};
5-18
Advanced ArcObjects Component Development II (C++)Copyright © 2002 ESRI. All rights reserved.
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.cppHRESULT 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;}
// MBFeature.cppHRESULT 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;}
5-19
Advanced ArcObjects Component Development II (C++)Copyright © 2002 ESRI. All rights reserved.
Custom drawing behavior
// MBFeature.cpp…// IFeatureDrawSTDMETHODIMP 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 IFeatureDrawSTDMETHODIMP 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;}
// MBFeature.cpp…// IFeatureDrawSTDMETHODIMP 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 IFeatureDrawSTDMETHODIMP 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;}
Call Draw() function in IFeature::Draw
5-20
Advanced ArcObjects Component Development II (C++)Copyright © 2002 ESRI. All rights reserved.
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
NameID
1 Laterals
CLSID{3070721…
EXTCLSID{0368CF51…Feature classes
CustomFeature
2 Mains {8881110…
5-21
Advanced ArcObjects Component Development II (C++)Copyright © 2002 ESRI. All rights reserved.
Use the selected object in ArcCatalog
Access IClassSchemaEdit, just like class extensions
Registering a custom feature
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
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
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
5-22
Advanced ArcObjects Component Development II (C++)Copyright © 2002 ESRI. All rights reserved.
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
5-23
Advanced ArcObjects Component Development II (C++)Copyright © 2002 ESRI. All rights reserved.
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
5-24
Advanced ArcObjects Component Development II (C++)Copyright © 2002 ESRI. All rights reserved.
Exercise 5 overview
Aggregate in an esriCore::Feature
Implement IFeatureDraw
Implement a class extension
Add a renderer and property page
Challenge: Persistence
CMBClassExtension
CMBRendererPropertyPage
IClassExtensionIFeatureClassExtension
IFeatureClassDraw
IPropertyPage
IPropertyPageContext
IUnknown
IFeatureDraw CMBFeature
IFeatureesriCore::Feature
IFeatureRenderer
CMBRendererILegendInfoIPersistStream
IMBClassExtension
5-25
Advanced ArcObjects Component Development II (C++)Copyright © 2002 ESRI. All rights reserved.
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?
5-26
Advanced ArcObjects Component Development II (C++)Copyright © 2002 ESRI. All rights reserved.