You are on page 1of 0

for

Visual Basic and Visual C++ Developers
Customizing ArcGIS
Desktop Applications
Euan Cameron
Allan 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++ developers
A 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()
IUnknown
IUnknown
• What interface do all other interfaces inherit from?
Querying for Interfaces
Private Sub PopulateMap(pMap as IMap)
AddLayers pMap
IActiveView pAV
Set pAV = pMap ' QueryInterface
pAV.Refresh
End Sub
Private Sub PopulateMap(pMap as IMap)
AddLayers pMap
IActiveView pAV
Set pAV = pMap ' QueryInterface
pAV.Refresh
End Sub
void CMyClass::PopulateMap(IMap* pMap)
{
::AddLayers (pMap);
IActiveView* pAV;
pMap->QueryInterface(IID_IActiveView, (void **) &pAV); // QI
pAV->Refresh();
pAV->Release();
}
void CMyClass::PopulateMap(IMap* pMap)
{
::AddLayers (pMap);
IActiveView* pAV;
pMap->QueryInterface(IID_IActiveView, (void **) &pAV); // QI
pAV->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 Directions
1. 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
Embedding
ArcObjects
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
interfaces
IUnknown
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 - I
Part 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
Task Task
COM
DLL
COM COM
DLL DLL
Extract
KeyWords
Search for the
correct OMD(s)
Review all
related
documentation
Trace the flow
between classes
and write code
Divide task
into subtasks
Decide where to
write code
Search for a
related sample
Review the
Structure of the
OMD
Part III: Navigate the OMD
VBA
Macro
VBA VBA
Macro Macro
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 interface
5. 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 Scratch
Visual Basic:
1. Create a VB ActiveX DLL project
2. Define library and class
3. Reference or import the esriCore.olb
4. Implement an esriCore interface
5. Compile and register in component category
Example: SelectLabel – ICommand and ITool
Demo:
UCVBServer - SelectLabel
Components from Scratch
Visual C++:
1. Create an ATL COM AppWizard project
2. Define library and class
3. Reference or import the esriCore.olb
4. Implement an esriCore interface
5. 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 - I
Implements IExtension
Private Property Get IExtension_Name() As String
IExtension_Name = "ESRI UC 2001 Extension"
End Property
Private Sub IExtension_Startup(ByRef initializationData As Variant)
If (TypeOf initializationData Is IApplication) Then
Set m_pApp = initializationData
m_frmSounds.play m_sSoundLoc & "\clap.wav"
End If
End Sub
Private Sub IExtension_Shutdown()
Set m_pApp = Nothing
m_frmSounds.play m_sSoundLoc & "\laser.wav"
End Sub
Implements IExtension
Private Property Get IExtension_Name() As String
IExtension_Name = "ESRI UC 2001 Extension"
End Property
Private Sub IExtension_Startup(ByRef initializationData As Variant)
If (TypeOf initializationData Is IApplication) Then
Set m_pApp = initializationData
m_frmSounds.play m_sSoundLoc & "\clap.wav"
End If
End Sub
Private Sub IExtension_Shutdown()
Set m_pApp = Nothing
m_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 IExtension
Implements IExtensionConfig
Private Property Get IExtensionConfig_Description() As String
IExtensionConfig_Description = "Sounds Extension"
End Property
Private Property Get IExtensionConfig_State() As esriCore.esriExtensionState
IExtensionConfig_State = m_esriExtensionState
End Property
Private Property Let IExtensionConfig_State(ByVal ExtensionState As
esriCore.esriExtensionState)
m_esriExtensionState = ExtensionState
Select Case m_esriExtensionState
Case 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 Select
End Property
Private m_esriExtensionState As esriExtensionState
Implements IExtension
Implements IExtensionConfig
Private Property Get IExtensionConfig_Description() As String
IExtensionConfig_Description = "Sounds Extension"
End Property
Private Property Get IExtensionConfig_State() As esriCore.esriExtensionState
IExtensionConfig_State = m_esriExtensionState
End Property
Private Property Let IExtensionConfig_State(ByVal ExtensionState As
esriCore.esriExtensionState)
m_esriExtensionState = ExtensionState
Select Case m_esriExtensionState
Case 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 Select
End 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…
MyTool
MyCmd
MyTOC
ArcMap
MyTool
MyCommand
MyExtension
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 events
Set m_pDocEvents = m_ipApp.Document
End Sub
Private Sub IExtension_Shutdown()
Set m_ipApp = Nothing
'disconnect from document events
Set 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 events
Set m_pDocEvents = m_ipApp.Document
End Sub
Private Sub IExtension_Shutdown()
Set m_ipApp = Nothing
'disconnect from document events
Set m_pDocEvents = Nothing
End Sub
Sinking Events in VC++ - I
• Connect manually on IExtension::Startup
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 connects
if (m_dwDocCookie)
return;
//Connect to document events
HRESULT 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 connects
if (m_dwDocCookie)
return;
//Connect to document events
HRESULT 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 events
HRESULT hr = AtlUnadvise(m_ipMxDoc, IID_IDocumentEvents, m_dwDocCookie);
if (FAILED(hr))
MessageBoxW(::GetActiveWindow(),L"Could not disconnect from Document
events",L"Persist Settings Extension", MB_OK);
m_ipMxDoc = 0;
m_dwDocCookie = 0;
}
void CPersistExt::DisconnectFromDocument()
{
if (m_ipMxDoc == NULL)
return;
//Disconnect from document events
HRESULT hr = AtlUnadvise(m_ipMxDoc, IID_IDocumentEvents, m_dwDocCookie);
if (FAILED(hr))
MessageBoxW(::GetActiveWindow(),L"Could not disconnect from Document
events",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 - III
Private WithEvents m_pMxDocumentEvents As MxDocument
Private Sub IExtension_Startup(ByRef initializationData As Variant)
If (TypeOf initializationData Is IMxApplication) Then
Set m_pApp = initializationData
Set m_pMxDocumentEvents = m_pApp.Document
m_frmSounds.play m_sSoundLoc & "\clap.wav"
End If
End Sub
Private Function m_pMxDocumentEvents_CloseDocument() As Boolean
m_frmSounds.play m_sSoundLoc & "\slidedwn.wav"
End Function
Private Function m_pMxDocumentEvents_NewDocument() As Boolean
m_frmSounds.play m_sSoundLoc & "\slideup.wav"
End Function
Private Function m_pMxDocumentEvents_OpenDocument() As Boolean
m_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) Then
Set m_pApp = initializationData
Set m_pMxDocumentEvents = m_pApp.Document
m_frmSounds.play m_sSoundLoc & "\clap.wav"
End If
End Sub
Private Function m_pMxDocumentEvents_CloseDocument() As Boolean
m_frmSounds.play m_sSoundLoc & "\slidedwn.wav"
End Function
Private Function m_pMxDocumentEvents_NewDocument() As Boolean
m_frmSounds.play m_sSoundLoc & "\slideup.wav"
End Function
Private Function m_pMxDocumentEvents_OpenDocument() As Boolean
m_frmSounds.play m_sSoundLoc & "\pop.wav"
End Function
Demo:
Simple Extension - III
Locking Functionality
• Implement ICustomizationFilter
• Document-level scope
• Use IDocumentEvents to apply filter
Dim m_pMyFilter as ICustomizationFilter
Set 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 Boolean
m_pApp.LockCustomization "password", m_pMyFilter
End Function
Private Function m_pDocumentEvents_OpenDocument() As Boolean
m_pApp.LockCustomization "password", m_pMyFilter
End Function
Dim m_pMyFilter as ICustomizationFilter
Set 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 Boolean
m_pApp.LockCustomization "password", m_pMyFilter
End Function
Private Function m_pDocumentEvents_OpenDocument() As Boolean
m_pApp.LockCustomization "password", m_pMyFilter
End Function
Simple Extension - IV
Implements ICustomizationFilter
Private Function ICustomizationFilter_OnCustomizationEvent(ByVal
custEventType As esriCore.esriCustomizationEvent, ByVal eventCtx As
Variant) 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
Else
ICustomizationFilter_OnCustomizationEvent = False
End If
End Function
Implements ICustomizationFilter
Private Function ICustomizationFilter_OnCustomizationEvent(ByVal
custEventType As esriCore.esriCustomizationEvent, ByVal eventCtx As
Variant) 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
Else
ICustomizationFilter_OnCustomizationEvent = False
End If
End Function
Demo:
Simple Extension - IV
Extensions and Commands
• Integrate to control
– Availability - ICommand::Enabled property
– How they behave…
IUnknown
_cSelectLabel
cSelectLabel
IUnknown
IUnSelectLabel
CUnSelectLabel
IUnknown
IExtension
cMyExtension
ICommand
ITool
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
_cSelectLabel
cSelectLabel
IUnknown
IUnSelectLabel
CUnSelectLabel
IUnknown
IExtension
cMyExtension
IToolController
ICommand
ITool
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 IExtension
Implements IToolController
Private Property Get IToolController_Enabled() As Boolean
If (m_iExtensionState = esriESEnabled) Then ' Other criteria...
IToolController_Enabled = True
Else
IToolController_Enabled = False
End If
End Property
Implements IExtension
Implements IToolController
Private Property Get IToolController_Enabled() As Boolean
If (m_iExtensionState = esriESEnabled) Then ' Other criteria...
IToolController_Enabled = True
Else
IToolController_Enabled = False
End If
End Property
' cSelectLabel.cls
Private m_pToolController As IToolController
Private Property Get ICommand_Enabled() As Boolean
ICommand_Enabled = m_pToolController.Enabled
End Property
Private Sub ICommand_OnCreate(ByVal hook As Object)
Set m_pApp = hook
Set m_pToolController = m_pApp.FindExtensionByCLSID(pUID)
End Sub
' cSelectLabel.cls
Private m_pToolController As IToolController
Private Property Get ICommand_Enabled() As Boolean
ICommand_Enabled = m_pToolController.Enabled
End Property
Private Sub ICommand_OnCreate(ByVal hook As Object)
Set m_pApp = hook
Set 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 stored
1. Standard types – strings, integers, longs…
2. Objects that support IPersistStream
• Many ArcObjects can be persisted
– Map, Graphic Elements, Renderer objects…
IPersistVariant - VB
Private Sub IPersistVariant_Save(ByVal Stream As esriCore.IVariantStream)
'persist booleans
Stream.Write m_bDataView
Stream.Write m_bDisplayView
'persist com object
Stream.Write m_ipMapBackgroundColor
End Sub
Private Sub IPersistVariant_Save(ByVal Stream As esriCore.IVariantStream)
'persist booleans
Stream.Write m_bDataView
Stream.Write m_bDisplayView
'persist com object
Stream.Write m_ipMapBackgroundColor
End Sub
• Sample: VBPersistVariant and VBPersistVariantCommand
Private Sub IPersistVariant_Load(ByVal Stream As esriCore.IVariantStream)
'load booleans
m_bDataView = Stream.Read
m_bDisplayView = Stream.Read
'load com object
Set m_ipMapBackgroundColor = Stream.Read
End Sub
Private Sub IPersistVariant_Load(ByVal Stream As esriCore.IVariantStream)
'load booleans
m_bDataView = Stream.Read
m_bDisplayView = Stream.Read
'load com object
Set m_ipMapBackgroundColor = Stream.Read
End Sub
IPersistVariant – VC++
STDMETHODIMP CPersistExt::Save(IVariantStream * Stream)
{
//persist booleans
vSave.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 booleans
vSave.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 booleans
hr = 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 object
IObjectStreamPtr 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 booleans
hr = 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 object
IObjectStreamPtr 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 ComPropertySheet
2. Assign component Category
3. Add an interface to QI for
4. 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