customizing arc gis desktop
TRANSCRIPT
forVisual Basic and Visual C++ Developers
Customizing ArcGIS Desktop Applications
Euan CameronAllan Laframboise
Agenda• 8:30 – 12:00 PM
– ArcGIS development• 1:00 – 5:30 PM
– Application extensions and advanced topics• Breaks
Introduction• Who are we?• Who are you?• What is this presentation about?• Who is this presentation for?
Questionnaire• How many people are:
– ArcObjects developers – Writing VBA macros, used OMDs…– Developing with COM - commands, tools or
extensions– ArcView Avenue developers – MapObjects developers– VB / C++ / VC++
Why are you here?• Learn how to extend ArcGIS desktop applications• Move VBA customizations to COM objects• Underlying architecture• Advanced customizations• Application extensions and when to use them
• Want to hang out in San Diego for the weekend…
What will be covered• ArcGIS product review• ArcObjects and COM backgrounder (short)• ArcObjects development strategies and
customization options• Application framework • Extensions • Document Persistence• Windows and property pages• Advanced development and integration
Code Samples and Prizes• Downloadable via
– www.esri.com\arcobjectsonline– All samples are written in VB and VC++
• Professionally printed ArcObjects OMDs (4x)– $50 value– Based on participation
• Exploring ArcObjects books (2x)– $100 value– Must complete presentation evaluation at end!
ArcGIS Product Review• Desktop Products
– ArcInfo– ArcEditor– ArcView– Core applications
– ArcMap, ArcCatalog and ArcToolBox– Extensions
• Server Products- ArcSDE- ArcIMS
ArcObjects: The Foundation• One object model, many products
– ArcInfo– ArcEditor – ArcView
• All share the following – DLLs, classes, interfaces and members
• Developers– Write multi-product ArcGIS components and
extensions
Product Licensing• License manager controls
– Which product can be installed and used– License file – floating and fixed seats
• Desktop Administrator– Enables a product and functionality based on
license available– E.g. ArcView vs ArcEditor
• Re-installation NOT required
Demo:Desktop Administrator
Licensing and Development• Clients must have a license to support the objects
that you develop with• All licensed interfaces are listed in Exploring
ArcObjects Appendix C• ArcMap and ArcCatalog detects license on startup• Using extensions requires checking out licenses• More on this later…
• Develop distributable components with the same license that your end-users will have
ArcObjects and COM for
VB and VC++ developersA 20 minute quiz…
ArcObjects• ArcObjects is a set of COM objects• Provides a framework and development platform
– Extend and customize ArcGIS applications– Develop custom tools and views– Enhance map presentation– Handle custom map and data processing – Manage data
• Help system, Object Model Diagrams
COM• Microsoft’s Component Object Model • Protocol that connects one software module to
another• Not a language• Binary specification• Language independent • Programming model that uses interfaces
• Why do most VBA, VB and pure C++ developers find it difficult to program with COM?
COM Classes and Interfaces
• Class encapsulates interface implementation• Working details of an interface are within a class
implementation, a so called CoClass• COM classes can support multiple interfaces• COM objects communicate via their interfaces• Acts as a contract• Access to COM objects is made via its interfaces
AO Interfaces and Classes• 1674 interfaces
– All custom interfaces– Early binding – Inbound and outbound
• 1192 coclasses – Support multiple interfaces
Default interfaces• AO classes have a default inbound and outbound• IUnknown is default for ArcObjects• VB programming considerations
– VB hides the default interface in Object Browser– VB returns default interface if none is specified– Forces you to declare all variable types
• Not an issue for VC++• Remember the “AlaMO”?
Demo:EOBrowser.exe
• How can you find the default interface for a class? • Where is IApplication in VB/VBA?
The well “known” Interface• IUnknown
– Maintains reference counting – QueryInterface (QI)
AddRef()Release()QueryInterface()
IUnknownIUnknown
• What interface do all other interfaces inherit from?
Querying for InterfacesPrivate Sub PopulateMap(pMap as IMap)AddLayers pMap
IActiveView pAVSet pAV = pMap ' QueryInterfacepAV.Refresh
End Sub
Private Sub PopulateMap(pMap as IMap)AddLayers pMap
IActiveView pAVSet pAV = pMap ' QueryInterfacepAV.Refresh
End Sub
void CMyClass::PopulateMap(IMap* pMap){::AddLayers (pMap);
IActiveView* pAV;pMap->QueryInterface(IID_IActiveView, (void **) &pAV); // QIpAV->Refresh();
pAV->Release();}
void CMyClass::PopulateMap(IMap* pMap){::AddLayers (pMap);
IActiveView* pAV;pMap->QueryInterface(IID_IActiveView, (void **) &pAV); // QIpAV->Refresh();
pAV->Release();}
ArcObjects Type Library• Main library is esriCore.olb
– Describes interfaces, coclasses, enums…– Written with IDL (Interface Definition Language)– Stores GUIDs – ProgID, CLSID, IID
• Clients and development environments use type library to discover available data “types” – VB -> References– VC++ -> #import and importlib
• What are three ways to explore esriCore library?
ArcObjects Help Tools• 1. ArcObjects Developer Help• 2. ArcObjects Developer Help (on-line)• 3. Exploring ArcObjects (PDF and hard copy)• 4. ArcObjects Object Model Diagrams (.pdfs and hard copy)• 5. In VBA\VB\VC++
– F1 -> ArcObjects Developer help (VB and VC++ version)– F2 -> Object Viewer
• 6. EOBrowser.exe• 7. OLEView.exe• 8. RegEdit.exe or RegEdit32.exe
ArcObjects Help in VC++• Additional resources• VC++ Add-In
– Arc8Help.dll• AoDevVC.chm
– AO C++ help
Demo:AoDevVC.chm
Review• Definitions – ArcObjects and COM• Interface programming• How to work with esriCore.olb• ArcObjects help tools
ArcObjects Development
The Application Framework
Before Starting…• Realize that ArcObjects are COM objects • Familiar with the ArcGIS customization framework• Understand what customizations are available • Know the recommended entry points• Familiar with the OMDs• Know help resources, tools and samples
Customization Directions1. Work inside of the ArcGIS applications
– Customize ArcMap and ArcCatalog with VBA – Macros, buttons, tools, menus– Build and deliver documents or templates
2. Create custom components with a COM-compliant language – Everything possible in VBA– Advanced functionality e.g. extensions– Deliver COM components
Development Triangle
Recommended progression
Start here
EmbeddingArcObjects
Stand-alone applications
Custom features and workspaces
Custom layers and renderers
Document persistence
Windows Property pagesViews
Application extensions Geodatabase class extensions
Editor tools
Commands Buttons Tools Menus Toolbars
VBA Macros and UIControls
Map Control
Difficulty of implementation
To VBA or not VBA• Advantages
– Free! No development platform required– Easy to write macros and scripts– Create buttons, tools and toolbars– Easy to debug– Distribute code as documents or templates
• Disadvantages– Can not write or compile COM components
– Many parts of the application framework require the creation of COM components
– Can not create application extensions
To VB or not VB• Advantages
– Fast and easy to write COM components– Manages interface pointer references– Hides much of the low level plumbing– Easy to design forms and complex GUIs
• Disadvantages– Debugging issues– Can’t implement all interfaces in esriCore – It’s VB…
To VC++ or not VC++• Advantages
– Can implement all esriCore interfaces– Complete control over internal COM plumbing – ATL wizards make life easer, but…
• Disadvantages– Requires C++ and COM programming experience– Must handle reference counting carefully– Sinking outbound interface is more complex– It’s VC++…
When to write components• Anytime you want to extend the architecture• Move away from VBA customizations• Advanced customizations such as extensions• Want to package functionality as a binary
General Approach• Create a COM object and implement one or more
interfacesIUnknown
IExtension
IUnknown
IExtension
Editor
yourExtension
esriCore
yourLibrary
Demo:Existing Extension
Levels of Implementation• Interface chosen depends on functionality desired• External GUI
– Framework components: ICommand, ITool, IToolbarDef and IMenuDef
– Editor: IEditSketch, IEditTask– Windows: IDockableWindowDef, IContentsView, IGxView,
ICOMPropertyPage
• Internal– Extensions: IExtension, IExtensionConfig, ICustomizationFilter
• Geodatabase– IClassExtension and IFeature
Component Categories• All advanced customizations must be registered in
the correct ESRI component category• Applications load components at runtime – faster!• Managing and exploring categories
– Customize Dialog, Categories.exe, .reg scripts– VB Add-ins – ESRI Compile and Register– VC++ Macros IMPLEMENTED_CATEGORY(UUID)– ArcCATIDs.h and CategoryIDs.cls
Demo:Categories.exe
• Name two ways to explore component categories?
Finding the right Interface - IPart I: Define the programming task
Part II: Find the OMD
Describe problem in AO terms
Subtask 1
Subtask 2…
ArcObjects Programming
Task
ArcObjects ArcObjects Programming Programming
TaskTask
COM DLL
COM COM DLLDLL
ExtractKeyWords
Search for the correct OMD(s)
Review allrelated
documentation
Trace the flow between classesand write code
Divide taskinto subtasks Decide where to
write code Search for a related sample
Review the Structure of the
OMD
Part III: Navigate the OMD
VBA MacroVBA VBA MacroMacro
Finding the right interface - II• Look at the existing coclasses e.g. TOC objects• Browse the component categories• Browse existing samples!
Creating AO Components• High-level steps
1. Create a COM project 2. Define library and class 3. Reference or import the esriCore.olb 4. Implement an esriCore interface5. Compile and register in component category
* General steps are the same regardless of the language and development environment
Writing your first Component• Choose a simple interface
– Framework ->ICommand, ITool…• Model code in VBA first
– UIButtonControl
Demo:VBA Zoom In UIButton
Writing your first Component• Easier to use VB than VC++ • Use the ESRI Add-ins
– ESRI Interface Implementer– ESRI Error Handler and Line Generator– Many more…
• Look at ESRI-provided code and samples
• Example: Use the ESRI Command Wizard
Demo:Command Wizard
AfCommands• Pre-written ESRI Commands• Work with ArcMap and the Map Control• Buttons and tools with lots of functionality
– Data access commands– File save and open commands– Map query and view tools– Editing commands and tools
• Source can be found in– \ArcObjects Developer Kit\Kits\ArcObjects Source\Commands\VB/VC++
• Just register and use….
Demo:CommandTestHarness
Components from ScratchVisual Basic:
1. Create a VB ActiveX DLL project 2. Define library and class 3. Reference or import the esriCore.olb 4. Implement an esriCore interface5. Compile and register in component category
Example: SelectLabel – ICommand and ITool
Demo:UCVBServer - SelectLabel
Components from ScratchVisual C++:
1. Create an ATL COM AppWizard project 2. Define library and class 3. Reference or import the esriCore.olb 4. Implement an esriCore interface5. Compile and register in component category
Example: UnSelectLabel - ICommand
Demo:UCVBServer - UnSelectLabel
Review• Development Triangle• Choosing a development language• Process for extending ArcObjects• Writing simple ArcObjects components
– Framework interfaces – commands and tools– VB and VC++
• VB Add-ins and ATL wizards
Application Extension Development
The Essentials
Topics• Application extensions• IExtension and IExtensionConfig• Event handling concepts• Locking application functionality• Writing custom interfaces• Persistence
Application Extensions• Mechanism to seamlessly integrate
customizations into ArcMap and ArcCatalog• Application-level customizations• Generally used to:
– Maintain “state” throughout an app session– Share information between tools– Persist information into a MXD file– Product license detection and tool management– Enable functionality via the Extensions window
• Example ESRI Editor, Spatial Analyst…
How Extensions Work
• User starts application• Application object is created• Document object is created• * Extensions are loaded• New or existing document is loaded• Application start-up is completed
General Implementation• Implement IExtension
– IExtension::Name– IExtension::StartUp()
– IMxApplication, IGxApplication – IEditor
– IExtension::ShutDown()
• Must register extension in component category– ESRI Mx Extension –> ArcMap– ESRI Gx Extension –> ArcCatalog– ESRI Editor Extensions -> ArcMap
Simple Extension - IImplements IExtension
Private Property Get IExtension_Name() As StringIExtension_Name = "ESRI UC 2001 Extension"
End Property
Private Sub IExtension_Startup(ByRef initializationData As Variant)If (TypeOf initializationData Is IApplication) ThenSet m_pApp = initializationDatam_frmSounds.play m_sSoundLoc & "\clap.wav"
End IfEnd Sub
Private Sub IExtension_Shutdown()Set m_pApp = Nothingm_frmSounds.play m_sSoundLoc & "\laser.wav"
End Sub
Implements IExtension
Private Property Get IExtension_Name() As StringIExtension_Name = "ESRI UC 2001 Extension"
End Property
Private Sub IExtension_Startup(ByRef initializationData As Variant)If (TypeOf initializationData Is IApplication) ThenSet m_pApp = initializationDatam_frmSounds.play m_sSoundLoc & "\clap.wav"
End IfEnd Sub
Private Sub IExtension_Shutdown()Set m_pApp = Nothingm_frmSounds.play m_sSoundLoc & "\laser.wav"
End Sub
Demo:Simple Extension - I
Using the Extension Window• Implement IExtensionConfig
– IExtensionConfig::Description– IExtensionConfig::State
• Looks like an ESRI extension
• You are responsible for:– What happens when checkbox is checked– Storing the “state” of the extension
– esriESEnabled, esriESDisabled or esriESUnavailable– State is stored in registry when application closes
Simple Extension - II
Demo:Simple Extension - II
Private m_esriExtensionState As esriExtensionState
Implements IExtensionImplements IExtensionConfig
Private Property Get IExtensionConfig_Description() As StringIExtensionConfig_Description = "Sounds Extension"
End Property
Private Property Get IExtensionConfig_State() As esriCore.esriExtensionStateIExtensionConfig_State = m_esriExtensionState
End Property
Private Property Let IExtensionConfig_State(ByVal ExtensionState AsesriCore.esriExtensionState)m_esriExtensionState = ExtensionStateSelect Case m_esriExtensionStateCase esriESEnabled: _ m_frmSounds.play m_sSoundLoc & "\cashreg.wav"Case esriESDisabled: _ m_frmSounds.play m_sSoundLoc & "\laser.wav"Case esriESUnavailable: _ m_frmSounds.play m_sSoundLoc & "\carbrake.wav"
End SelectEnd Property
Private m_esriExtensionState As esriExtensionState
Implements IExtensionImplements IExtensionConfig
Private Property Get IExtensionConfig_Description() As StringIExtensionConfig_Description = "Sounds Extension"
End Property
Private Property Get IExtensionConfig_State() As esriCore.esriExtensionStateIExtensionConfig_State = m_esriExtensionState
End Property
Private Property Let IExtensionConfig_State(ByVal ExtensionState AsesriCore.esriExtensionState)m_esriExtensionState = ExtensionStateSelect Case m_esriExtensionStateCase esriESEnabled: _ m_frmSounds.play m_sSoundLoc & "\cashreg.wav"Case esriESDisabled: _ m_frmSounds.play m_sSoundLoc & "\laser.wav"Case esriESUnavailable: _ m_frmSounds.play m_sSoundLoc & "\carbrake.wav"
End SelectEnd Property
Advanced Implementations• Extension acts as a central controlling object
– Maintains global data for tools, views, windows– Detect current product version and license– Check out appropriate extension license– Control enabled property of other tools– Lock down certain application functionality
• Requires– Application must listen for events– May need to make use of custom interfaces
Understanding Events • Application fires events to all components that
sink an outbound interface• Necessary to react to
– Documents opened, view changes, refresh…
MyToolMyCmdMyTOC
ArcMap
MyToolMyCommandMyExtension
ArcMap
Levels of Event Handling• Decide what level of event handling is
necessary– Document: IDocumentEvents– Map: IActiveViewEvents, IMapEvents,
ISelectionEvents…– Layout: IActiveViewEvents, IPageEvents,
ISelectionEvents…– Layer: ILayerEvents, ISelectionEvents…– Editor: IEditorEvents…– Workspace: IWorkspaceEditEvents
Sinking Events in VB• Use WithEvents • IExtension::Startup and Shutdown
Dim WithEvents m_pDocEvents As DocumentEvents
Private Sub IExtension_Startup(initializationData As Variant)Set m_ipApp = initializationData'connect to document eventsSet m_pDocEvents = m_ipApp.Document
End Sub
Private Sub IExtension_Shutdown()Set m_ipApp = Nothing'disconnect from document eventsSet m_pDocEvents = Nothing
End Sub
Dim WithEvents m_pDocEvents As DocumentEvents
Private Sub IExtension_Startup(initializationData As Variant)Set m_ipApp = initializationData'connect to document eventsSet m_pDocEvents = m_ipApp.Document
End Sub
Private Sub IExtension_Shutdown()Set m_ipApp = Nothing'disconnect from document eventsSet m_pDocEvents = Nothing
End Sub
Sinking Events in VC++ - I• Connect manually on IExtension::Startupvoid CPersistExt::ConnectToDocument(){
IDocumentPtr ipDoc;m_ipApp->get_Document(&ipDoc);if (ipDoc == NULL)return; // Nothing to connect to
m_ipMxDoc = ipDoc;if (m_ipMxDoc == NULL)return; // Nothing to connect to
//Avoid multiple connectsif (m_dwDocCookie)return;
//Connect to document eventsHRESULT hr = AtlAdvise(m_ipMxDoc, this->GetUnknown(),IID_IDocumentEvents, &m_dwDocCookie);
}
void CPersistExt::ConnectToDocument(){
IDocumentPtr ipDoc;m_ipApp->get_Document(&ipDoc);if (ipDoc == NULL)return; // Nothing to connect to
m_ipMxDoc = ipDoc;if (m_ipMxDoc == NULL)return; // Nothing to connect to
//Avoid multiple connectsif (m_dwDocCookie)return;
//Connect to document eventsHRESULT hr = AtlAdvise(m_ipMxDoc, this->GetUnknown(),IID_IDocumentEvents, &m_dwDocCookie);
}
Sinking Events in VC++ - II• Disconnect manually on IExtension::Shutdown
void CPersistExt::DisconnectFromDocument(){if (m_ipMxDoc == NULL)return;
//Disconnect from document eventsHRESULT hr = AtlUnadvise(m_ipMxDoc, IID_IDocumentEvents, m_dwDocCookie);if (FAILED(hr))MessageBoxW(::GetActiveWindow(),L"Could not disconnect from Documentevents",L"Persist Settings Extension", MB_OK);
m_ipMxDoc = 0;m_dwDocCookie = 0;
}
void CPersistExt::DisconnectFromDocument(){if (m_ipMxDoc == NULL)return;
//Disconnect from document eventsHRESULT hr = AtlUnadvise(m_ipMxDoc, IID_IDocumentEvents, m_dwDocCookie);if (FAILED(hr))MessageBoxW(::GetActiveWindow(),L"Could not disconnect from Documentevents",L"Persist Settings Extension", MB_OK);
m_ipMxDoc = 0;m_dwDocCookie = 0;
}
Extensions and Events• IDocumentEvents• Members
– ActiveViewChanged()– BeforeDocumentClosed()– CloseDocument()– MapsChanged()– NewDocument()– OnContextMenu()– OpenDocument()
Simple Extension - IIIPrivate WithEvents m_pMxDocumentEvents As MxDocument
Private Sub IExtension_Startup(ByRef initializationData As Variant)If (TypeOf initializationData Is IMxApplication) ThenSet m_pApp = initializationDataSet m_pMxDocumentEvents = m_pApp.Documentm_frmSounds.play m_sSoundLoc & "\clap.wav"
End IfEnd Sub
Private Function m_pMxDocumentEvents_CloseDocument() As Booleanm_frmSounds.play m_sSoundLoc & "\slidedwn.wav"
End Function
Private Function m_pMxDocumentEvents_NewDocument() As Booleanm_frmSounds.play m_sSoundLoc & "\slideup.wav"
End Function
Private Function m_pMxDocumentEvents_OpenDocument() As Booleanm_frmSounds.play m_sSoundLoc & "\pop.wav"
End Function
Private WithEvents m_pMxDocumentEvents As MxDocument
Private Sub IExtension_Startup(ByRef initializationData As Variant)If (TypeOf initializationData Is IMxApplication) ThenSet m_pApp = initializationDataSet m_pMxDocumentEvents = m_pApp.Documentm_frmSounds.play m_sSoundLoc & "\clap.wav"
End IfEnd Sub
Private Function m_pMxDocumentEvents_CloseDocument() As Booleanm_frmSounds.play m_sSoundLoc & "\slidedwn.wav"
End Function
Private Function m_pMxDocumentEvents_NewDocument() As Booleanm_frmSounds.play m_sSoundLoc & "\slideup.wav"
End Function
Private Function m_pMxDocumentEvents_OpenDocument() As Booleanm_frmSounds.play m_sSoundLoc & "\pop.wav"
End Function
Demo:Simple Extension - III
Locking Functionality• Implement ICustomizationFilter• Document-level scope• Use IDocumentEvents to apply filterDim m_pMyFilter as ICustomizationFilterSet m_pMyFilter = FilterExt.cMyCustomizationFilter
Private Sub IExtension_Startup(ByRef initializationData As Variant)Set m_pMyFilter = New ArcObjectsExtension.cExtensionFilter
End Sub
Private Function m_pDocumentEvents_NewDocument() As Booleanm_pApp.LockCustomization "password", m_pMyFilter
End Function
Private Function m_pDocumentEvents_OpenDocument() As Booleanm_pApp.LockCustomization "password", m_pMyFilter
End Function
Dim m_pMyFilter as ICustomizationFilterSet m_pMyFilter = FilterExt.cMyCustomizationFilter
Private Sub IExtension_Startup(ByRef initializationData As Variant)Set m_pMyFilter = New ArcObjectsExtension.cExtensionFilter
End Sub
Private Function m_pDocumentEvents_NewDocument() As Booleanm_pApp.LockCustomization "password", m_pMyFilter
End Function
Private Function m_pDocumentEvents_OpenDocument() As Booleanm_pApp.LockCustomization "password", m_pMyFilter
End Function
Simple Extension - IV
Implements ICustomizationFilter
Private Function ICustomizationFilter_OnCustomizationEvent(ByValcustEventType As esriCore.esriCustomizationEvent, ByVal eventCtx AsVariant) As Boolean
If (custEventType = esriCEShowCustDlg _Or custEventType = esriCEShowVBAIDE) Then
m_frmSounds.play "C:\WINNT\Media\Microsoft Office 2000\police.wav"ICustomizationFilter_OnCustomizationEvent = True ‘ Lock functionality
ElseICustomizationFilter_OnCustomizationEvent = False
End If
End Function
Implements ICustomizationFilter
Private Function ICustomizationFilter_OnCustomizationEvent(ByValcustEventType As esriCore.esriCustomizationEvent, ByVal eventCtx AsVariant) As Boolean
If (custEventType = esriCEShowCustDlg _Or custEventType = esriCEShowVBAIDE) Then
m_frmSounds.play "C:\WINNT\Media\Microsoft Office 2000\police.wav"ICustomizationFilter_OnCustomizationEvent = True ‘ Lock functionality
ElseICustomizationFilter_OnCustomizationEvent = False
End If
End Function
Demo:Simple Extension - IV
Extensions and Commands• Integrate to control
– Availability - ICommand::Enabled property– How they behave…
IUnknown
_cSelectLabelcSelectLabel
IUnknown
IUnSelectLabelCUnSelectLabel
IUnknown
IExtensioncMyExtension
ICommandITool
ICommand
Add a Custom Interface• To handle application-specific operations• VB and VC++ give you a default interface for free• Or create your own…
IUnknown
_cSelectLabelcSelectLabel
IUnknown
IUnSelectLabelCUnSelectLabel
IUnknown
IExtensioncMyExtensionIToolController
ICommandITool
ICommand
Creating Interfaces• VB
– Create a new class– Name it IWhatever– Add members* Implement new interface in a class
• VC++– Simply add interface to IDL– Add members using the wizard* Implement new interface in a class
Extension IToolController
Implements IExtensionImplements IToolController
Private Property Get IToolController_Enabled() As BooleanIf (m_iExtensionState = esriESEnabled) Then ' Other criteria...IToolController_Enabled = True
ElseIToolController_Enabled = False
End IfEnd Property
Implements IExtensionImplements IToolController
Private Property Get IToolController_Enabled() As BooleanIf (m_iExtensionState = esriESEnabled) Then ' Other criteria...IToolController_Enabled = True
ElseIToolController_Enabled = False
End IfEnd Property
' cSelectLabel.clsPrivate m_pToolController As IToolController
Private Property Get ICommand_Enabled() As BooleanICommand_Enabled = m_pToolController.Enabled
End Property
Private Sub ICommand_OnCreate(ByVal hook As Object)Set m_pApp = hookSet m_pToolController = m_pApp.FindExtensionByCLSID(pUID)
End Sub
' cSelectLabel.clsPrivate m_pToolController As IToolController
Private Property Get ICommand_Enabled() As BooleanICommand_Enabled = m_pToolController.Enabled
End Property
Private Sub ICommand_OnCreate(ByVal hook As Object)Set m_pApp = hookSet m_pToolController = m_pApp.FindExtensionByCLSID(pUID)
End Sub
Demo:UCVBServer
Enhancing IToolController…• Add more “state” management functionality• Checks for the following criteria:
– IExtensionConfig::State -> esriESEnabled (already done)– Correct product license -> esriProductCodeProfessional– Can check out an ArcPress license -> TRUE/FALSE
• If any of the above return FALSE, all tools are automatically disabled!
• Example: UCVBServer
Demo:UCVBServer
Extensions and Persistence• IPersistVariant or IPersistStream (VC++ only)
– Mechanism for storing your data in documents– Generally implemented with extensions– Maintain user customizations and state data
• Two types of data can be stored1. Standard types – strings, integers, longs…2. Objects that support IPersistStream
• Many ArcObjects can be persisted– Map, Graphic Elements, Renderer objects…
IPersistVariant - VBPrivate Sub IPersistVariant_Save(ByVal Stream As esriCore.IVariantStream)
'persist booleansStream.Write m_bDataViewStream.Write m_bDisplayView
'persist com objectStream.Write m_ipMapBackgroundColor
End Sub
Private Sub IPersistVariant_Save(ByVal Stream As esriCore.IVariantStream)'persist booleansStream.Write m_bDataViewStream.Write m_bDisplayView
'persist com objectStream.Write m_ipMapBackgroundColor
End Sub
• Sample: VBPersistVariant and VBPersistVariantCommand
Private Sub IPersistVariant_Load(ByVal Stream As esriCore.IVariantStream)'load booleansm_bDataView = Stream.Readm_bDisplayView = Stream.Read
'load com objectSet m_ipMapBackgroundColor = Stream.Read
End Sub
Private Sub IPersistVariant_Load(ByVal Stream As esriCore.IVariantStream)'load booleansm_bDataView = Stream.Readm_bDisplayView = Stream.Read
'load com objectSet m_ipMapBackgroundColor = Stream.Read
End Sub
IPersistVariant – VC++STDMETHODIMP CPersistExt::Save(IVariantStream * Stream){//persist booleansvSave.Clear();vSave.vt = VT_BOOL;vSave.boolVal = m_bDataView;hr = Stream->Write(vSave);if (FAILED(hr)) return hr;
vSave.Clear();vSave.vt = VT_BOOL;vSave.boolVal = m_bDisplayView;hr = Stream->Write(vSave);if (FAILED(hr)) return hr;
//persist com object
IObjectStreamPtr ipObjStream(CLSID_ObjectStream);ipObjStream->putref_Stream(Stream);ipObjStream->SaveObject(m_ipMapBackgroundColor);if (FAILED(hr)) return hr;
return S_OK;}
STDMETHODIMP CPersistExt::Save(IVariantStream * Stream){//persist booleansvSave.Clear();vSave.vt = VT_BOOL;vSave.boolVal = m_bDataView;hr = Stream->Write(vSave);if (FAILED(hr)) return hr;
vSave.Clear();vSave.vt = VT_BOOL;vSave.boolVal = m_bDisplayView;hr = Stream->Write(vSave);if (FAILED(hr)) return hr;
//persist com object
IObjectStreamPtr ipObjStream(CLSID_ObjectStream);ipObjStream->putref_Stream(Stream);ipObjStream->SaveObject(m_ipMapBackgroundColor);if (FAILED(hr)) return hr;
return S_OK;}
IPersistVariant::Load – VC++STDMETHODIMP CPersistExt::Load(IVariantStream * Stream){//load booleanshr = Stream->Read(&vLoad);if (FAILED(hr)) return hr;if (vLoad.vt == VT_BOOL)m_bDataView = vLoad.boolVal;vLoad.Clear();
hr = Stream->Read(&vLoad);if (FAILED(hr)) return hr;if (vLoad.vt == VT_BOOL)m_bDisplayView = vLoad.boolVal;vLoad.Clear();
//load com objectIObjectStreamPtr ipObjStream(CLSID_ObjectStream);ipObjStream->putref_Stream(Stream);ipObjStream->LoadObject(m_ipMapBackgroundColor);if (FAILED(hr)) return hr;
IUnknownPtr ipUnk;hr = ipObjectStream->LoadObject((GUID*) &IID_IUnknown, 0, &ipUnk);if (FAILED(hr) || (ipUnk == 0)) return E_FAIL;m_ipMapBackgroundColor = ipUnk;
return S_OK;}
STDMETHODIMP CPersistExt::Load(IVariantStream * Stream){//load booleanshr = Stream->Read(&vLoad);if (FAILED(hr)) return hr;if (vLoad.vt == VT_BOOL)m_bDataView = vLoad.boolVal;vLoad.Clear();
hr = Stream->Read(&vLoad);if (FAILED(hr)) return hr;if (vLoad.vt == VT_BOOL)m_bDisplayView = vLoad.boolVal;vLoad.Clear();
//load com objectIObjectStreamPtr ipObjStream(CLSID_ObjectStream);ipObjStream->putref_Stream(Stream);ipObjStream->LoadObject(m_ipMapBackgroundColor);if (FAILED(hr)) return hr;
IUnknownPtr ipUnk;hr = ipObjectStream->LoadObject((GUID*) &IID_IUnknown, 0, &ipUnk);if (FAILED(hr) || (ipUnk == 0)) return E_FAIL;m_ipMapBackgroundColor = ipUnk;
return S_OK;}
Review• Fundamentals of writing an application extension• Essentials of event handling (VB and VC++)• Lock application functionality• Custom interfaces (VB and VC++)• Extensions as a central “management” component• Integrate extensions and persistence
Extreme ArcObjects
The road less traveled…
Topics• Multi-component Development• Window development• Dockable Windows• Custom Views• Property Pages• Custom Layers• Custom Renderers
Multi-component Design• For complex integrations, it will necessary to
implement multiple interfaces and possibly multiple components
• Don’t forget about events• Examples:
– Command + Toolbar + Extension + Events…– Locking functionality + Extension + Events…– Window + Command + Extension + Events…– PropertyPage + Events +/- Command…– Renderers + PropertyPage + Class Extensions…
Dockable Windows• Embed new windows into the application itself• Similar to existing TOC window• Interface - IDockableWindowDef• Key members
– OnCreate, OnDestroy, ChildWindow• Integrated with ICommand• Component Category: ESRI Mx Dockable Windows
• Sample: OverviewDockableWindow
Demo:OverviewDockableWindow
Custom Tab Views• Generic application-level windows• Must handle events and all documents, data…• ArcMap – IContentsView, IActiveViewEvents• ArcCatalog – IGxView, IGxSelectionEvents
• Example: ArcMapContentsView • Sample: HTML View
Demo::ArcMapContentsView
Property Sheets and Pages• Windows with tabs (pages)• Generally activated on right-click or properties• CoClasses
– ComPropertySheet – Surrounding window• Interfaces
– IComPropertyPage (VB)– IPropertyPage– IComPropertyPageEvents
• There are tones of ESRI-property pages already!
On-The-Fly Property Sheets• Re-use and display ESRI property pages
1. Create ComPropertySheet2. Assign component Category3. Add an interface to QI for4. Call IComPropertySheet::EditProperties
• Should be called from a command or menu
• Sample: PropertySheet (Map pages)
Demo::PropertySheetOnTheFly
Implementing PropertyPages• You provide a window to display inside the page• IPropertyPage and IPropertyPageEvents• Must add your component to appropriate
component category for property pages• System automatically adds pages to the property
sheet when instantiated• In VC++, use ATL property page wizard
• Sample: PropertyPage (Custom Map Page)
Demo::PropertyPageCustom
Custom Layers• Possible to create custom layers – lots of work!• Implement ILayer• Need to manage and display elements somehow• What about selecting, persisting, editing, feature
management…• Should create a custom property page
• Example: LayerAndPropertyPage
Demo::PropertyPageCustom
Custom Renderers• Provide custom symbolization for features
– SimpleRenderer, UniqueValue, ClassBreaks…• Implement IFeatureRenderer• May want to implement custom symbols as well
• Sample: Sliver (small polygons)
Renderers and ClassExtensions
• ClassExtension extends a standard feature class• Implemented at the Geodatabase-level• Associates the render with the featureclass
– IClassExtension– IFeatureClassExtension– IFeatureClassDraw::CustomRenderer (doesn’t need to be
custom implementation!)• Data always draws the same way
Final Integration Sample• More complex integration
– Set of custom commands, tools and a toolbar– Custom extension– Persists user settings into MXD– Listens for ArcObjects events– Supports custom interface and custom events– Written in VC++ and VB
• Sample: DisplayToolbarExtension (Grand Finale)
Summary• Virtually everything is customizable by implementing
interfaces and plugging in new COM objects• Look at existing implementations (coclasses) and categories
to determine which direction to take• Extensions are simple to implement• Event handling is essential• Custom interfaces can be useful• Components often co-exist with others
• The new ArcObjects on-line help system and samples are invaluable!
The End