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
Microsofts 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
Cant implement all interfaces in esriCore
Its 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
Its 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
Dont 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 (doesnt 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

You might also like