编写DLL扩展ArcMap功能
ArcGIS不仅仅是一个可直接使用的GIS程序集,也是一套完整的功能框架,它提供了多种方式可以让用户扩展功能,我们知道,这种扩展方式有三种,VBA,DLL和控件开发,前两者与框架是紧密相关的。对于VBA,许多书中已经介绍的非常清楚,而使用MapControl控件,也是我的书的重点内容。使用DLL来扩展功能,则是见于一些零散的文档中。
作为一个应用程序框架,以ArcMap为例,它必然提供了许多接口让用户可以实现,从而达到扩展的功能。以产生一个浮动窗口为例,即DockableWindow,我们知道,如果要产生这样一个对象,必须使用IDockableWindowDef接口;产生这样一个窗体后,我们希望是使用一个按钮的方式来打开这个窗口,这又必须使用ICommand接口才行,而这个按钮,还需要与已经存在的按钮在格调上一致。
下面是整个过程:
- 启动VB6,然后点击ActiveX dll选项,在工程中会出现一个class1.cls的类模块(我更喜欢使用VB6来写DLL,因为它对DLL的UID设置和Outbound Interface的实现都比较方便)
- 将工程名改为DockableWindow,它将来将作为一个类库名出现。
- 引入必须的类库,如ArcMapUI、FrameWork、System、SystemUI和Carto
- 引入资源文件,点选菜单“Add -Ins”下的“Add -in Manager”,在弹出的对话框的列表框中选择“VB Resource (RES) Editor”,单击“OK“后退出对话框。这样在“Tools”菜单栏中就有一项“Resource Editor”了。打开这个工具然后导入一个已经存在的位图文件,这个文件被加入后的编号为101,我们将在ICommand中使用它。
- 添加一个窗体,改名为frmDockWin,在窗体上添加一个listbox控件,改名为lstSelCount
- 将class1改名为cmdDockWin,然后键入如下代码:
- 添加一个类模块,改名为DockWin,键入如下代码
- 代码编写完毕,点击文件——生成DockableWindow.dll按钮,生成一个DLL文件(如果开始没有保存,此时将要求保存)
- 打开工程——DockableWindow属性——部件,将“版本兼容”设置为“二进制兼容”,保存工程。
- 打开ArcGIS的Component Category Manager,分别将dll中的两个类导入不同的组件目录,cmdDockWin导入ESRI Mx Comands目录,DockWin导入ESRI Mx Dockable Windows目录
- 打开ArcGIS程序,在Customize中查看是否存在“Developer Samples”目录,将目录中的命令拖到任何一个命令栏上,点击后,即可出现一个浮动窗体。
Implements ICommand '必须实现该接口才能成为一个按钮
Private m_pApp As IApplication
Private m_pDockWinMgr As IDockableWindowManager
Private m_pDockWin As IDockableWindow
Private m_pBitmap As IPictureDisp
Private Sub Class_Initialize()
'使用我们刚才新建的资源文件,0为位图类型
Set m_pBitmap = LoadResPicture(101, 0)
End Sub
Private Property Get ICommand_Bitmap() As esriSystem.OLE_HANDLE
'按钮的图标为m_pBitmap对象,即我们引入的那张位图
ICommand_Bitmap = m_pBitmap
End Property
Private Property Get ICommand_Caption() As String
ICommand_Caption = "SelCount Dockable Window"
End Property
Private Property Get ICommand_Category() As String
'该按钮会出现的目录是在Developer Samples,当我们打开Customize时,在Commands中会看到两栏,左边就是目录,右边是目录中可用的命令
ICommand_Category = "Developer Samples"
End Property
Private Property Get ICommand_Checked() As Boolean
ICommand_Checked = m_pDockWin.IsVisible
End Property
Private Property Get ICommand_Enabled() As Boolean
ICommand_Enabled = True
End Property
Private Property Get ICommand_HelpContextID() As Long
End Property
Private Property Get ICommand_HelpFile() As String
End Property
Private Property Get ICommand_Message() As String
ICommand_Message = "Display the selection count dockable window"
End Property
Private Property Get ICommand_Name() As String
ICommand_Name = "DeveloperSamples_SelCountDockableWindow"
End Property
Private Sub ICommand_OnClick()
m_pDockWin.Show Not m_pDockWin.IsVisible
End Sub
Private Sub ICommand_OnCreate(ByVal hook As Object)
'按钮初始化,从hook中引入Application对象,新建浮动窗体对象m_pDockWin
Set m_pApp = hook
Set m_pDockWinMgr = hook 'QI for IDockableWindowManager
' Get a reference to the selection count dockable window
Dim u As New UID
u.Value = "DockableWindow.DockWin"
Set m_pDockWin = m_pDockWinMgr.GetDockableWindow(u)
End Sub
Private Property Get ICommand_Tooltip() As String
ICommand_Tooltip = "Selection Count"
End Property
Implements IDockableWindowDef
Private m_pApp As IApplication
Private m_pMXDoc As IMxDocument
Private WithEvents m_pDocEvent As MxDocument ' Listen for the MxDocument events
Private WithEvents m_pMapEvent As Map ' Listen for the Map events
Private WithEvents m_pLayoutEvent As PageLayout ' Listen for the PageLayout events
Private Property Get IDockableWindowDef_Caption() As String
IDockableWindowDef_Caption = "Selected Features Count"
End Property
Private Property Get IDockableWindowDef_ChildHWND() As esriSystem.OLE_HANDLE
'它需要使用到frmdockwin窗体的listbox控件来显示数据
IDockableWindowDef_ChildHWND = frmDockWin.lstSelCount.hWnd
End Property
Private Property Get IDockableWindowDef_Name() As String
IDockableWindowDef_Name = "Selection Count"
End Property
Private Sub IDockableWindowDef_OnCreate(ByVal hook As Object)
Set m_pApp = hook
'Start listening to the MxDocument events
If Not m_pDocEvent Is m_pApp.Document Then
Set m_pDocEvent = m_pApp.Document
End If
Set m_pMXDoc = m_pApp.Document
If Not m_pMapEvent Is m_pMXDoc.FocusMap Then
Set m_pMapEvent = m_pMXDoc.FocusMap
End If
If Not m_pLayoutEvent Is m_pMXDoc.PageLayout Then
Set m_pLayoutEvent = m_pMXDoc.PageLayout
End If
End Sub
Private Sub IDockableWindowDef_OnDestroy()
Set m_pMapEvent = Nothing
Set m_pLayoutEvent = Nothing
Set m_pMXDoc = Nothing
Set m_pApp = Nothing
End Sub
Private Property Get IDockableWindowDef_UserData() As Variant
End Property
Private Function m_pDocEvent_MapsChanged() As Boolean
' Clear the list box and reset the reference to the map when a new dataframe
' is added to the document.
frmDockWin.lstSelCount.Clear
If Not m_pMapEvent Is m_pMXDoc.FocusMap Then
Set m_pMapEvent = m_pMXDoc.FocusMap
End If
End Function
Private Function m_pDocEvent_NewDocument() As Boolean
' Clear the list box and reset the reference to the map and pagelayout when
' a new document is created.
frmDockWin.lstSelCount.Clear
If Not m_pMapEvent Is m_pMXDoc.FocusMap Then
Set m_pMapEvent = m_pMXDoc.FocusMap
End If
If Not m_pLayoutEvent Is m_pMXDoc.PageLayout Then
Set m_pLayoutEvent = m_pMXDoc.PageLayout
End If
End Function
Private Function m_pDocEvent_OpenDocument() As Boolean
' Clear the list box and reset the reference to the map and pagelayout when
' another document is opened.
frmDockWin.lstSelCount.Clear
If Not m_pMapEvent Is m_pMXDoc.FocusMap Then
Set m_pMapEvent = m_pMXDoc.FocusMap
End If
If Not m_pLayoutEvent Is m_pMXDoc.PageLayout Then
Set m_pLayoutEvent = m_pMXDoc.PageLayout
End If
End Function
Private Sub m_pLayoutEvent_FocusMapChanged()
' Clear the list box and reset the reference to the map when
' a different map (dataframe) is made active.
frmDockWin.lstSelCount.Clear
If Not m_pMapEvent Is m_pMXDoc.FocusMap Then
Set m_pMapEvent = m_pMXDoc.FocusMap
End If
End Sub
Private Sub m_pMapEvent_SelectionChanged()
' When the selection changes is the map, update the contents of the listbox
Dim pMap As IMap
Dim i As Integer
Dim pFLayer As IFeatureLayer
Dim pSel As IFeatureSelection
Set pMap = m_pMapEvent
frmDockWin.lstSelCount.Clear
'Loop through the layers in the map and add the layer's name and
'selection count to the list box
For i = 0 To pMap.LayerCount - 1
If TypeOf pMap.Layer(i) Is IFeatureSelection Then
Set pFLayer = pMap.Layer(i)
Set pSel = pFLayer
frmDockWin.lstSelCount.AddItem pFLayer.Name & ": " & pSel.SelectionSet.Count
End If
Next
End Sub
Related Items
Comments
1.检查你的代码是否写的真是和书上一样
2.是否引入了messagebox类的类库
呵呵,还是不行,你说的问题我都检查了,代码是一样的,至于messagebox,你在书中特意强调要引入system.windows.forms,我注意到了。检查之后还是不行。我也不知道什么原因~~郁闷呀~~
最近又遇到新的问题了(我笨笨的),在map中加入新的元素这点我实现不了,不知道怎么办呵呵。我的问题真多~~
斑竹你好
你的网站不错。很高兴看到你的文章。若你想宣传你的网站,让更多的人访问你的网站,我建议你学习一些网站优化排名的知识。我推荐你到网站优化排名服务和搜索引擎优化排名的网站去看看。那里有上千篇有关网站优化排名的文章,一定对你有所帮助。
大哥你好!
小弟有个问题。。。。
我是用vc7.0+Ao做开发 ,现在正做一个点击地图查询属性的功能。
在vb中有 Dim pMxApplication As IMxApplication
Dim pMxDocument As IMxDocument
set pMxApplication = Application
set pMxDocument=Application.Document
set pMap=pMxDocument.FocusMap
.............
请问在vc中如何上面的语句,关键是第一个set语句。
我定义了IMxApplicationPtr pMxApplication;
后面如何让pMxApplication获得Application,实现vb中
set pMxApplication = Application 相同的操作呢!
有时间请帮助指点一下迷津。。。。。
![]()
![]()
![]()
![]()
除非是使用基于ArcMap的vba编程或dll编程,否则Application这个对象都是不可用的。你使用的AO+vc,肯定用了控件,是没有Application这个对象的。因此第一个set不能实现。
建议你看看我的书中写的东西。
是不是控件开发,IMxApplication和IMxDocument都用不了
对,这个原因在我的书中写的非常清楚。
我已经购买了arcobjcets开发与技巧,请问如何用c#编写的dll/tlb引用到令一台机器arcmap里,在本机加载没有问题,但到了令一台机器就不行了,请问采用什么措施才能完成。
请说的更清楚一点。
呵呵,您的blog写的真是好啊,对ao开放者来说简直是个宝
我最近也在做ao开发,刚开始弄
有个问题请教 "引入资源文件,点选菜单“Add -Ins”.."我在中文版的vb环境下怎么找不着啊
先谢了
我用c#2005写的程序编译后生成dll和tlb,把tlb加到本机器的arcmap里正常运行(可以加载进去),但我把这个dll和tlb文件拷贝到令一台机器上,就加载不上去,是不是在本机器编译时候注册了什么,所以导致直接拷贝的tlb在令一台机器上加不进去,请问如何解决?谢谢
在VB中是使用Project--Reference来引入ESRI的AO库的。
写DLL,还是用VB或VC比较好,.NET写DLL出的问题很多,我也是深受其难的。
找不到“Developer Samples”目录啊,能指导一下么?
在ArcGIS安装目录\DeveloperKit\samples中
该按钮会出现的目录是在Developer Samples,当我们打开Customize时,在Commands中会看到两栏,我中Customize根本找不到“Developer Samples”目录啊,能指导一下么?
你好,为什么在打开另一个mxd文件后,菜单栏上的按钮会消失呢?
我用c#2005写的程序编译后生成dll和tlb,把tlb加到本机器的arcmap里正常运行(可以加载进去),但我把这个dll和tlb文件拷贝到令一台机器上,就加载不上去,是不是在本机器编译时候注册了什么,所以导致直接拷贝的tlb在令一台机器上加不进去,请问如何解决?谢谢
这个问题有解决方法吗??
看了您的文章受益非浅,不过还是有一个问题想咨询一下,VBA写的程序与相同功能的vbDLL在速度上有多少优势,我现在写了一个VBA,需要处理24小时才能算完一个结果,我不知道改成DLL后会不会快一些,
会快一点,但不会高出一个数量级那么恐怖
你好,我按照您的做法在VB6 中文版里面进行了开发,前面几步骤都很顺利,但是到第10步的时候,提示“Can't Load Library”,非常奇怪。
代码都是从你的Blog上拷贝过去的,应该不会有问题。
补充一下,使用的环境是 VB 6 中文版,ArcGIS 9.2 安装了SDK for VB 。
SDK for .NET 装不上。
可能是版本不同,导致了库的不同,你可能没有引入相关库
Leave a comment
Or, take a look at Archives and Categories
嗯,在看你的书,写的很好。是学习AO的好帮手。谢谢你为大家提供了这么好的东西~~
我最近遇到一个问题,自己解决不了,想问问你和各位大佬。也是关于dll方式扩展ArcMap功能的。在你的书里的第二章最后讲述了一个使用dll为ArcMap做扩展的例子。我安装例子完成后也注册完成了。使用ArcGis的Component Category Manager看到在Mx Extension中也确实存在了我自己的扩展了,但是实现不了预期的想像工作。即打开文档和新建文档时都有提供框出现。请问可能是什么原因呢?需要怎么解决呢?