ArcGIS Engine控件编程

更新时间:2024-06-13 21:49:01 阅读量: 综合文库 文档下载

说明:文章内容仅供预览,部分内容可能不全。下载后的文档,内容与下面显示的完全一致。下载之前请确认下面内容是否您想要的,是否完整无缺。

2 使用ArcGIS Engine控件编程

在ESRI.ArcGIS.Controls命名空间中包含了可用于快速构建GIS独立应用程序的控件,如MapControl、PageLayoutControl、TOCControl和ToolbarControl等。其中MapControl和PageLayoutControl对应ArcMap桌面应用程序的“数据”和“布局”视图。前者封装了Map对象,用于地图数据的显示和分析,后者封装了PageLayout对象,用于地图的修饰和整理,以便生成一幅成品地图。这两个控件均实现了IMxdContents接口,可以读取和写入地图文档(MapDocument)。

TOCControl和ToolbarControl控件分别对应ArcMap中的“Table of Contents”和各种工具条。这两个控件都需要一个“伙伴控件”协同工作,而伙伴控件则可以是MapControl、PageLayoutControl、SceneControl或GlobeControl。TOCControl用交互树视图的方式来显示伙伴控件的地图、图层和符号体系内容,并保持其内容与伙伴控件同步。而ToolbarControl则可以驻留操作其伙伴控件的命令、工具和菜单。

在Visual Studio.NET环境中使用ArcGIS Engine开发GIS应用程序,需要使用ESRI interop程序集(Interop Assemblies),它为ArcGIS控件提供了能够位于.NET窗体上的控件,这些控件当被拖放在窗体上,生成具体的实例对象时,均有带有前缀“ax”。如axMapControl1,axPageLayoutControl1等。

本章将详细介绍MapControl、PageLayoutControl、TOCControl和ToolbarControl四个常用控件,并在C#.NET开发平台上,结合GIS软件开发的基本需求进行讲述。

2.1 MapControl控件

MapControl控件对应于ArcMap中的数据视图,它封装了Map对象,并提供了相应的属性、方法、事件,可以实现: (1)地图显示;

(2)地图的放大、缩小和漫游; (3)生成点、线、面等图形元素;

(4)识别地图上选中的元素,进行属性查询; (5)标注地图元素等。

该控件能够实现ArcMap能够完成的绝大部分任务。该控件实现的主要接口包括IMapControlDefault、IMapControl2、IMapControl3、IMapControl4和事件接口IMapControlEvents2、IMxdContents、ITOCBuddyEvents。

2.1.1 主要接口

?IMapControlDefault接口

IMapControlDefault接口是MapControl的缺省接口,是代表控件最新版本的接口。一般的开发环境自动使用这个接口定义的属性和方法。在C#开发平台上,当用户将该控件拖放到窗体上时,会自动生成一个名为axMapControl1的对象,该对象可以直接使用缺省接口对应的属性和方法。该控件当前最新版本的接口为IMapControl4。定义该接口的变量,并进行实例化的方法如下:

IMapControlDefault pMapcontrol;

pMapcontrol = axMapControl1.Object as IMapControlDefault;

?IMapControl2接口

该接口是所有与MapControl相关任务的出发点,包括设置控件外观、设置Map对象及控件的显示属性,添加、管理数据层,在控件上绘制图形并返回Geometry等。该接口定义

了控制控件外观界面的各种属性,如Apperaance、BorderStyle和BackColor。它定义了AddLayer、AddLayerFromFile、AddshapeFile、CenterAt、ClearLayers、DeleteLayer、LoadMxFile、MoveLayerTo、Pan、ReadMxMaps等添加、移动、删除、清楚图层的方法,TrackLine、TrackPolygon、TrackRectangle和TrackCircle等让程序员可以直接在控件上获得Geometry对象的方法,外还提供了DrawShape和DrawText让程序员可以在控件上绘制图形的方法。

?IMapControl3接口

该接口在继承IMapControl2的基础上,增加了以下属性和方法: 1)CustomProperty:设置自定义控件属性;

2)DoucumentFilename:返回加入MapControl中地图文档的文件名; 3)DocumentMap:返回MapControl最后装入Map的名称;

4)KeyIntercept:返回或设置MapControl截取的键盘按键信息,按键信息类型如表2.1所示。

5)Object:返回潜在的MapControl控件。 6)ShowMap Tips、TipDelay、TipStyle

7)SuppressResizeDrawing():控件尺寸发生变化时,阻止数据实时重绘。

表2.1 esriKeyIntercept可能取值

枚举值 EsriKeyInterceptNone EsriKeyInterceptArrowKeys EsriKeyInterceptAlt EsriKeyInterceptTab EsriKeyInterceptEnter 0 1 2 4 8 Value 功能描述 没有截取鼠标按键 截取了箭头按键 截取了Alt按键 截取了Tab按键 截取了回车按键 ?IMapControl4接口

该接口在IMapControl接口的基础上,增加了两个属性,分别是AutoKeyboardScrolling和AutoMouseWheel。

?IMapControlEvents2 接口

该接口为事件接口,它定义了MapControl能够处理的全部事件。 如OnMouseDown、OnMouseMove、OnAfterDraw、OnExtentUpdated等。这些事件,在实际GIS软件开发中经常用到,如AfterScreenDraw是绘屏结束后触发的事件,OnExtentUpdate是地图的Extent属性发生变化时触发的事件等。

2.1.2 用MapControl控件加载地理数据

?用MapControl加载shape格式文件

用MapControl控件的AddShapeFile方法可以加载shape格式文件,该方法包括两个参数,分别是文件路径和不带扩展名的文件名。调用格式如下:

axMapControl1.AddShapeFile(strFilePath, strFilename);

也可以利用工作空间工厂打开Shape文件。基本思路是,利用工作空间工厂抽象类的IWorkspaceFactory接口定义接口变量pWorkspaceFactory,再利用其子类

ShapefileWorkspaceFactory对应的组件类ShapefileWorkspaceFactoryClass对

pWorkspaceFactory进行实例化。利用IWorkspace接口定义工作空间变量pWorkspace,利用pWorkspaceFactory的OpenFromFile方法打开工作空间,对其进行实例化。该方法包含两个参数,第一参数为需要打开工作空间(数据库,即shape格式的文件)的路径。接下来利用IFeatureWorkspace接口定义接口变量pFeatureWorkspace,对该变量利用接口跳转的方式进

行实例化:

pFeatureWorkspace=pWorkspace as IFeatureWorkspace;

利用IFeatureClass接口定义变量pFeatureClass,并对该变量利用pFeatureWorkspace的OpneFeatureClass方法进行实例化,该方法包含的参数是需要打开的shape文件名。定义数据集对象、特征图层对象,定义特征图层的特征数据类及特征图层的名称,再定义图层,最后将图层添加到MapControl的Map对象上,就能显示所选择的shape格式文件,具体代码如下所示。

string WorkSpacePath =System.IO.Path.GetDirectoryName(strFileName); strShapeFileName=System.IO.Path.GetFileName(strFileName);

IWorkspaceFactory pWorkspaceFactory=new ShapefileWorkspaceFactoryClass(); IWorkspace pWorkspace=pWorkspaceFactory.OpenFromFile(WorkSpacePath,0); IFeatureWorkspace pFeatureWorkspace=pWorkspace as IFeatureWorkspace;

IFeatureClass pFeatureClass=pFeatureWorkspace.OpenFeatureClass(strShapeFileName); IDataset pDataset=pFeatureClass as IDataset; IFeatureLayer pFeatureLayer=new FeatureLayerClass(); pFeatureLayer.FeatureClass=pFeatureClass; pFeatureLayer.Name=pDataset.Name; ILayer pLayer=pFeatureLayer as ILayer; axMapControl1.Map.AddLayer(pLayer);

在上述两种方式中,到底哪种方式显示图形的效率高,需要进行测试。

?用MapControl加载MXD格式文件

MapControl控件可以“链接”或“包含”地图文档,若为链接文档,控件将保存对地图文档的引用,以后任何对文档的修改都会出现在控件上,若为“包含文档”,则控件会复制当前的文档状态,而不会受到后来修改的影响。 在AE编程中,可以直接使用MapControl的LoadMxFile方法载入地图文档。在进行加载时,一般需通过MapControl提供的方法CheckMxFile()检查所选文件是否是合法的MXD文档,若合法则调用MapControl提供的LoadMxFile方法打开文档。

if (axMapControl1.CheckMxFile(strFileName))//strFileName是用户所选地图文档文件 {

//加载MXD文档

axMapControl1.MousePointer = esriControlsMousePointer.esriPointerHourglass; axMapControl1.LoadMxFile(strFileName, 0, Type.Missing);

axMapControl1.MousePointer = esriControlsMousePointer.esriPointerDefault; } else {

MessageBox.Show(\所选文件不是地图文档文件!\信息提示\

return;

}

若要加载某个地图文档中的特定地图,则需要先进行判断满足特定条件的地图,然后才加载地图文档文件。如下面的实例代码所示:

if (axMapControl1.CheckMxFile(strFileName))//strFileName为地图文档文件 {

//是Mxd文档文件 IArray pArray;

pArray = axMapControl1.ReadMxMaps(strFileName, Type.Missing); IMap pMap;

for (int i = 0; i < pArray.Count; i++) {

//QI接口跳转

pMap = pArray.get_Element(i) as IMap; if (pMap.Name == \ {

//加载文档对象中特定的Map对象

axMapControl1.MousePointer = esriControlsMousePointer.esriPointerHourglass; axMapControl1.LoadMxFile(strFileName, 0, Type.Missing);

axMapControl1.MousePointer = esriControlsMousePointer.esriPointerDefault; } } } else {

MessageBox.Show(\所选文件不是地图文档文件!\信息提示\

return;

}

?用MapControl加载栅格格式文件

利用MapControl控件也可以加载诸如*.bmp、*.tif、*.jpg等各种栅格格式的文件,加载栅格格式文件,需要引入ESRI.ArcGIS.DataSourcesRaster命名空间。具体思路如下:利用工作空间工厂抽象类定义接口变量,利用工作空间工厂的子类RasterWorkspaceFactory对用的组件类RasterWorkspaceFactoryClass实例化工作空间工厂变量。接下来定义工作空间变量,并进行实例化,再定义栅格工作空间,并进行实例化,接下来定义栅格数据集,利用所选栅格数据文件进行实例化,并判断该文件是否具有金字塔,若没有则创建金字塔,以提高大数据量图像的显示效率。在此基础上定义栅格数据、栅格数据层和数据层,并完成向MapControl空间中添加数据层数据,具体实现代码参与以下实例:

string pathName = System.IO.Path.GetDirectoryName(strFileName);//strFileName是栅格数据文件

string fileName = System.IO.Path.GetFileName(strFileName);

IWorkspaceFactory pWSF;

pWSF = new RasterWorkspaceFactoryClass(); IWorkspace pWS;

pWS = pWSF.OpenFromFile(pathName, 0); IRasterWorkspace pRWS;

pRWS = pWS as IRasterWorkspace; IRasterDataset pRasterDataset;

pRasterDataset = pRWS.OpenRasterDataset(fileName ); //影像金字塔判断与创建

IRasterPyramid pRasPyrmid;

pRasPyrmid = pRasterDataset as IRasterPyramid; if (pRasPyrmid != null) {

if (!(pRasPyrmid.Present))

{

pRasPyrmid.Create();

//在进度条中说明正在创建金字塔 }

}

IRaster pRaster;

pRaster = pRasterDataset.CreateDefaultRaster(); IRasterLayer pRasterLayer;

pRasterLayer = new RasterLayerClass(); pRasterLayer.CreateFromRaster(pRaster); ILayer pLayer = pRasterLayer as ILayer; //向axMapControl1控件中添加栅格图层 axMapControl1.AddLayer(pLayer, 0);

通过上面的讲述,可以体会MapControl控件的AddLayer方法、AddShapeFile方法、AddLayerFromFile方法等。另外,该控件还包括DeleteLayer和MoveLayerTo等方法。其它方法请查看MapControl的对象模型图,各种方法的具体使用请参阅ESRI ArcGIS Engine开发帮助文档。

IMapDocument接口定义了操作和管理文档对象的方法和属性。MapDocument类能够封装地图文档文件,如mxd、mxt和pmf等,它也可以封装一个图层文件(*.lyr)。使用这个对象可以获取和更新一个文档的内容,设置文档文件的属性以及读、写和保存一个文档文件。下面举例说明打开、保存、另存一个文档文件,主要代码如下:

//打开文档文件

IMapDocument pMapDocument;//定义接口变量

pMapDocument = new MapDocumentClass();//实例化地图文档对象 //将数据加载入pMapDocument并与map控件联系起来

pMapDocument.Open(strFileName, \);//strFileName是用户选择的文档文件 for (int i = 0; i < pMapDocument.MapCount; i++) {

//遍历所有可能的Map对象

axMapControl1.Map = pMapDocument.get_Map(i); }

//刷新地图

axMapControl1.Refresh();

//保存文档文件

//判断文档是否为只读文档

if (pMapDocument.get_IsReadOnly(pMapDocument.DocumentFilename) == true) {

MessageBox.Show(\此地图文档为只读文档!\, \信息提示\); return;

axMapControl1.Extent = axMapControl1.TrackRectangle(); //刷新地图

axMapControl1.ActiveView.PartialRefresh(esriViewDrawPhase.esriViewGeography, null);

null,

?移动地图

在MapControl中可以利用pan()方法来移动其中的地图。在MapControl控件的OnMouseDown事件中,添加如下代码,可实现地图移动。

axMapControl1.Pan();

?在MapControl控件中绘制图形

绘制图形包括绘制点、线、面和标注等。下面举例说明绘制点、线、面图形和标注的方法,别的图形的绘制将在后续章节中介绍。

(1)绘制点

IMap pMap;

IActiveView pActiveView; pMap = axMapControl1.Map;

pActiveView = pMap as IActiveView; //新建点对象 IPoint pPt;

pPt = new PointClass(); pPt.PutCoords(e.mapX, e.mapY); //产生一个Marker元素

IMarkerElement pMarkerElement;

pMarkerElement = new MarkerElementClass(); //产生修饰Marker元素的symbol ISimpleMarkerSymbol pMarkerSymbol;

pMarkerSymbol = new SimpleMarkerSymbolClass(); //设置符号颜色 IRgbColor pColor;

pColor = new RgbColorClass(); pColor.Red = 220; pColor.Green = 112; pColor.Blue = 60;

pMarkerSymbol.Color = pColor;//需要由用户动态设定 //设置符号大小

pMarkerSymbol.Size = 2; //设置符号类型

pMarkerSymbol.Style = esriSimpleMarkerStyle.esriSMSDiamond;//点符号也应该由用户动态选择

IElement pElement;

pElement = pMarkerElement as IElement;

//得到Element的接口对象,用于设置元素的Geometry pElement.Geometry = pPt;

pMarkerElement.Symbol = pMarkerSymbol; IGraphicsContainer pGraphicsContainer;

pGraphicsContainer = pMap as IGraphicsContainer; //将元素添加到Map中

pGraphicsContainer.AddElement(pMarkerElement as IElement, 0);

pActiveView.PartialRefresh(esriViewDrawPhase.esriViewGraphics, null, null);

(2)绘制线

IMap pMap;

IActiveView pActiveView; pMap = axMapControl1.Map;

pActiveView = pMap as IActiveView; IPolyline pPolyline;

pPolyline = axMapControl1.TrackLine() as IPolyline; //产生一个SimpleLineSymbol符号 ISimpleLineSymbol pSimpleLineSym;

pSimpleLineSym = new SimpleLineSymbolClass();

pSimpleLineSym.Style = esriSimpleLineStyle.esriSLSSolid;//需要用户动态选择 //设置符号颜色 IRgbColor pColor;

pColor = new RgbColorClass(); pColor.Red = 220; pColor.Green = 112; pColor.Blue = 60;

pSimpleLineSym.Color = pColor;//需要用户动态选择 pSimpleLineSym.Width = 1; //产生一个PolylineElement对象 ILineElement pLineEle;

pLineEle = new LineElementClass(); IElement pEle;

pEle = pLineEle as IElement; pEle.Geometry = pPolyline; //将元素添加到Map对象之中

IGraphicsContainer pGraphicsContainer;

pGraphicsContainer = pMap as IGraphicsContainer; pGraphicsContainer.AddElement(pEle, 0);

pActiveView.PartialRefresh(esriViewDrawPhase.esriViewGraphics, null, null);

(3)绘制面

IMap pMap;

IActiveView pActiveView; pMap = axMapControl1.Map;

pActiveView = pMap as IActiveView; IPolygon pPolygion;

pPolygion = axMapControl1.TrackPolygon() as IPolygon; //产生一个SimpleFillSymbol符号

ISimpleFillSymbol pSimpleFillSym;

pSimpleFillSym = new SimpleFillSymbolClass();

pSimpleFillSym.Style = esriSimpleFillStyle.esriSFSDiagonalCross;//需要用户动态选择 //设置符号颜色 IRgbColor pColor;

pColor = new RgbColorClass(); pColor.Red = 220; pColor.Green = 112; pColor.Blue = 60;

pSimpleFillSym.Color = pColor;//需要用户动态选择 //产生一个PolygonElement对象 IFillShapeElement pPolygonEle;

pPolygonEle = new PolygonElementClass(); pPolygonEle.Symbol = pSimpleFillSym; IElement pEle;

pEle = pPolygonEle as IElement; pEle.Geometry = pPolygion; //将元素添加到Map对象之中

IGraphicsContainer pGraphicsContainer;

pGraphicsContainer = pMap as IGraphicsContainer; pGraphicsContainer.AddElement(pEle, 0);

pActiveView.PartialRefresh(esriViewDrawPhase.esriViewGraphics, null, null);

(4)地图标注

IMap pMap;

IActiveView pActiveView; pMap = axMapControl1.Map;

pActiveView = pMap as IActiveView; ITextElement pTextEle; IElement pEles;

//建立文字符号对象,并设置相应的属性 pTextEle = new TextElementClass();

pTextEle.Text = \西安科技大学\;//需要动态设定 pEles = pTextEle as IElement; //设置文字字符的几何形体属性 IPoint pPoint;

pPoint = new PointClass(); pPoint.PutCoords(e.mapX, e.mapY); pEles.Geometry = pPoint; //添加到Map对象中,并刷新显示

IGraphicsContainer pGraphicsContainer;

pGraphicsContainer = pMap as IGraphicsContainer; pGraphicsContainer.AddElement(pEles, 0);

pActiveView.PartialRefresh(esriViewDrawPhase.esriViewGraphics, null, null);

2.1.4 数据选择

在MapControl控件中,可以使用SearchByshape方法来构造一个基于Map的选择集。它可以选择控件中所有处于选择范围内的要素。在地图控件的OnMouseDown事件中添加下列代码,可以实现数据选择:

//得到一个Envelope对象 IEnvelope pEnv;

pEnv = axMapControl1.TrackRectangle(); //新建选择集环境对象

ISelectionEnvironment pSelectionEnv;

pSelectionEnv = new SelectionEnvironmentClass(); //改变选择集的默认颜色 //设置符号颜色 IRgbColor pColor;

pColor = new RgbColorClass(); pColor.Red = 220; pColor.Green = 112; pColor.Blue = 60;

pSelectionEnv.DefaultColor = pColor; //选择要素,并将其放入选择集

axMapControl1.Map.SelectByShape(pEnv, pSelectionEnv, false);

axMapControl1.ActiveView.PartialRefresh(esriViewDrawPhase.esriViewGeoSelection, null, null);

利用上述方法选择的选择集,可以采用如下方法进行清除:

axMapControl1.Map.Clearselection(); axMapControl.ActiveView.Refresh();

上述多种与地图控件交互的操作,可以封装在类的公共静态方法之中。在定义公共方法时,需要设置的参数包括地图控件的对象,IMapControlEvents2_OnMouseDownEvent的对象,以及用户选择的操作类型。在主窗体地图控件的OnMouseDown事件中,调用该方法,并传入相应的参数即可。

2.1.5 实现鹰眼功能

绝大部分的GIS软件都能看到鹰眼窗口,利用鹰眼窗口,用户可以非常很直观地看到主视图中的地图范围在整个地图范围内的位置。下面将讲述如何实现鹰眼功能,其基本思路如下:

(1)窗体控件布局,需要两个MapControl控件,一个作为主视图,一个作为鹰眼视图。 (2)让主视图和鹰眼视图两个控件中显示的数据保持一致。

(3)主视图中当前显示的地图范围,需用一个红色矩形框在鹰眼视图中标绘出来。当主视图显示的地图范围发生变化,红色矩形框的位置也要发生变化。

(4)当用户用鼠标在鹰眼视图中移动或改变标识主视图中地图范围的红色矩形框的位置和大小时,主视图的地图范围也要发生相应变化。

当主视图中的地图发生变换化时,鹰眼视图中的地图也要发生变化。为此,在axMapControl1的OnMapReplaced事件中添加下列代码,使主视图和鹰眼视图中的数据保持一致。

IMap pMap;

pMap=axMapControl1.Map;

for(int i=0;i

axMapControl2.Map.AddLayer(pMap.get_Layer(i)); }

//使鹰眼视图中显示加载地图的全图

axMapControl2.Extent=axMapControl2.FullExtent; //使鹰眼视图中显示加载地图的全图

在鹰眼视图中移动红色矩形框时,主视图中的地图范围要发生相应的变换,包括范围变化和位置移动。需要鹰眼视图的OnMouseDown事件中添加下列代码:

if (e.button == 1)//探测鼠标左键 {

IPoint pPt = new PointClass(); pPt.X = e.mapX; pPt.Y = e.mapY;

IEnvelope pEnvelope = axMapControl1.Extent as IEnvelope; pEnvelope.CenterAt(pPt);

axMapControl1.Extent = pEnvelope;

axMapControl1.ActiveView.PartialRefresh(esriViewDrawPhase.esriViewGeography, null,

null); }

else if (e.button == 2)//鼠标右键按下 {

IEnvelope pEnvelope = axMapControl2.TrackRectangle(); axMapControl1.Extent = pEnvelope;

axMapControl1.ActiveView.PartialRefresh(esriViewDrawPhase.esriViewGeography, null,

null); }

上述代码的功能是,利用鼠标左键移动鹰眼视图中的红色矩形框,主视图中显示的范围要进行刷新。用鼠标右键在鹰眼视图中改变红色矩形框的大小,主视图中显示的地图数据也要进行刷新。

在axMapControl2的OnMouseMove事件中添加如下代码:

if (e.button != 1)

return;

IPoint pPt = new PointClass(); pPt.X = e.mapX; pPt.Y = e.mapY;

axMapControl1.CenterAt(pPt);

axMapControl2.ActiveView.PartialRefresh(esriViewDrawPhase.esriViewGraphics, null);

null,

主视图的范围发生变化时,会触发控件的OnExtentUpdated事件,绘制鹰眼图中的红色方框应放在主视图的OnExtentUpdated事件中:

//控制鹰眼的代码

IGraphicsContainer pGraphicsContainer = axMapControl2.Map as IGraphicsContainer;//以mapControl2为图形容器

IActiveView pAv = pGraphicsContainer as IActiveView;

// 在绘制前,清除axMapControl2 中的任何图形元素 pGraphicsContainer.DeleteAllElements();

IRectangleElement pRecElement = new RectangleElementClass(); IElement pEle = pRecElement as IElement; IEnvelope pEnv;

pEnv = e.newEnvelope as IEnvelope; pEle.Geometry = pEnv; //设置颜色

IRgbColor pColor = new RgbColorClass(); pColor.Red = 200; pColor.Green = 0; pColor.Blue = 0;

pColor.Transparency = 255; //产生一个线符号对象

ILineSymbol pLineSymbol = new SimpleLineSymbolClass(); pLineSymbol.Width = 2; pLineSymbol.Color = pColor; //设置填充符号的属性

IFillSymbol pFillSymbol = new SimpleFillSymbolClass(); //设置透明颜色

pColor.Transparency = 0; pFillSymbol.Color = pColor; pFillSymbol.Outline = pLineSymbol;

IFillShapeElement pFillShapeElement = pRecElement as IFillShapeElement; pFillShapeElement.Symbol = pFillSymbol; pGraphicsContainer.AddElement(pEle, 0);

axMapControl2.ActiveView.PartialRefresh(esriViewDrawPhase.esriViewGraphics, null, null);

2.2 PageLayoutControl控件

PageLayoutControl控件主要用于制图,利用该控件可以方便地操作各种元素对象,以便产生制作精美的地图对象。在PageLayoutControl中封装了一个名为PageLayout的组件类,它提供了在布局视图中控制元素的属性和方法。

除PageLayout对象外,PageLayoutControl控件还拥有许多附加的事件、属性和方法。例如,Printer属性用于处理地图打印时的一系列设定、Page属性用于处理控件的页面效果、Element属性则用于管理控件中的地图元素。因地图总是包含在Map对象中,而Map对象是由一个MapFrame对象所持有,因此PageLayout对象至少拥有一个MapFrame对象。

PageLayoutControl控件实现了多个接口,如IPageLayoutControlDefault、

IPageLayoutControl、IPageLayoutControl2和IPageLayoutControlEvents等。这些接口定义了该控件对象可以使用的属性、方法和事件。

利用PageLayoutControl控件,可以检查和加载MXD文档文件、实现元素的添加和移动板式页面等在ArcMap程序中布局视图可以实现的功能。

2.2.1 用PageLayoutControl操作MXD文件

使用该控件操作Mxd文档,需要使用Carto库中的IMapDocument接口定义的属性和方法,

这些属性和方法可以读取地图文档文件(.Mxd、.Mxt、.Pmf)和保存Mxd文件。IMapDocument接口提供了多种属性和方法,可以方便用户获取一个Mxd文件的内容。如Map属性使用一个索引号返回文档中的一个Map对象;MapCount则用于返回文档中包含的Map对象的数目;Pagelayout属性则可以返回一个页面视图Pagelayout,这个对象包含了Map和MapSurround集合。DocumentType用于返回当前载入地图文档的类型,如果是Mxd文档,返回值为0;若为Pmf文档,返回值为1;图层文件,返回值为2;未知类型则返回3。

利用下面这段代码,可以选择Mxd文档,并将选择的文件在PageLayout控件上打开。

OpenFileDialog openFileDlg = new OpenFileDialog(); openFileDlg.Title = \选择需要打开的地图文档\; openFileDlg.Filter = \地图文档(*.mxd)|*.mxd\; openFileDlg.ShowDialog(); string strFileN = string.Empty; strFileN = openFileDlg.FileName; if (strFileN == string.Empty) return;

IMapDocument pMapDocument;

pMapDocument = new MapDocumentClass(); //将数据载入到pMapDocument并与Map控件联系起来 pMapDocument.Open(strFileN, \); //将IMapDocument的数据传给控件

axPagelayoutControl1.PageLayout = pMapDocument.PageLayout; axPagelayoutControl1.Refresh();

2.2.2 PageLayout与MapControl联动

对操作过ArcMap的使用者应该知道,数据视图和布局视图中的数据是实时联动的,因为它们本来是在处理同一份数据。在ArcGIS Engine开发中,因MapControl控件和

PageLayoutControl控件并不能共享一个文档文件,要实现联动,一般需通过数据拷贝的方法传递两个控件中的数据内容。基本思路如下:

(1)编写数据拷贝方法,将axMapControl1控件中的地理数据复制到

axPageLayoutControl1控件中。在该方法中需要利用IObjectCopy接口,以便将axMapControl1中的Map对象拷贝到axPageLayoutControl1控件的ActiveView对象的FocusMap中。具体代码如下:

public static void CopyAndOverwriteMap(AxMapControl axMapControl1, AxPageLayoutControl axPageLayoutControl1) {

//获取对象拷贝接口

IObjectCopy objectCopy = new ObjectCopyClass(); object toCopyMap = axMapControl1.Map;

object copiedMap = objectCopy.Copy(toCopyMap);//复制地图到copiedMap中

object toOverwiteMap = axPageLayoutControl1.ActiveView.FocusMap;//获取视图控件的焦点地图

objectCopy.Overwrite(copiedMap, ref toOverwiteMap);//复制地图

}

在下面的示例代码中,该方法被封装在GeoMapLoad类中。

(2)当axMapControl1控件中加载的地理数据的发生重绘时,与其联动的

axPageLayoutControl1控件中的地理数据也要发生相应变化,因此在axMapControl1控件的OnAfterScreenDraw事件中应该添加获取axMapControl1控件中当前所显示地理数据范围的代码,并将当前显示范围传到axPageLayoutControl1控件的ActiveView对象的FocusMap中,同时要调用数据拷贝方法。

IActiveView pActiveView = (IActiveView)axPageLayoutControl1.ActiveView.FocusMap; IDisplayTransformation displayTransformation = pActiveView.ScreenDisplay.DisplayTransformation;

displayTransformation.VisibleBounds = axMapControl1.Extent;//设置焦点地图的可视范围 axPageLayoutControl1.ActiveView.Refresh();

GeoMapLoad.CopyAndOverwriteMap(axMapControl1, axPageLayoutControl1);

(3)当axMapControl1控件中加载的地理数据发生变化时,axPageLayoutCOntrol1控件中的地理数据也要发生相应变化。因此在axMapControl1控件的OnMapReplaced事件中应调用数据拷贝的方法。

GeoMapLoad.CopyAndOverwriteMap(axMapControl1, axPageLayoutControl1);

(4)当MapControl控件中的地理数据显示状况发生变化时,与其联动的PageLayoutControl中的地理数据也要发生变化。因此在OnViewRefreshed事件中应该添加调用数据拷贝的代码和使图层控件刷新的代码。

axTOCControl1.Update();

GeoMapLoad.CopyAndOverwriteMap(axMapControl1, axPageLayoutControl1);

2.2.3 在Pagelayout里面绘制元素

本节主要讨论使用样式对话框的方式来设置PageLayoutControl控件的各种属性。包括控件的边框(border)、阴影(shadow)、背景(background)和设置MapFrame的Mapgrid属性的方法等。

2.3 TOCControl控件

2.3.1 TOCControl控件概述

TOCControl要与一个伙伴控件或实现了IActiveView接口的对象协同工作。伙伴控件可以是MapControl、PageLayoutControl、ReaderControl、SceneControl或GlobeControl。伙伴控件可以利用TOCControl控件的属性页设置,也可以通过编程设置。该控件的伙伴控件都实现了ITOCBuddy接口。TOCControl控件用来显示其伙伴控件里的地图、图层和符号体系等内容,并保持其内容与伙伴控件同步。例如,若TOCControl控件的伙伴控件是MapControl,若从MapControl中删除一个图层,则该图层也会从TOCControl中删除。若用户与TOCControl交互,取消了某个图层的可见(Visibility)复选框,则该图层在MapControl中不再可见。

TOCControl以树形结构显示其“伙伴控件”的地图、图层和符号体系,该空间通过ITOCBuddy接口来访问其伙伴控件。TOCControl管理图层的可见性和标签的编辑。该控件的主要接口包括:

?ITOCControl:IDispatch

?ITOCControlEvents:IDispatch

(1)ITOCControl

ITOCControl接口是任何与TOCControl有关任务的出发点,如设置控件的外观、设置伙

伴控件、管理图层的可见和标签的编辑等。可通过如下方式定义该接口的变量,并进行实例化:

ITOCControl pTOCControl;

pTOCControl = axTOCControl1.Object as ITOCControl; 或pTOCControl = axTOCControl1.GetOcx() as ITOCControl;

(2)ITOCControlEvents接口

ITOCControlEvents接口是一个事件接口,它定义了TOCControl能够处理的全部事件,如OnMouseDown、OnMouseMove、OnMouseUp、OnBeginLabelEdit、OnEndLabelEdit等。在实际GIS软件系统开发中,会经常用到这些事件。如OnBeginLabelEdit和OnEndLabelEdit分别为当TOCCOntrol中的标签(地图、图层或图例)开始编辑和结束编辑时触发的事件。这两个事件的参数e都有一个名为canEdit的成员参数,可将这个参数设置为true或false,以便控制标签是否可编辑。

2.3.2 TOCCOntrol控件应用开发实例

(1)调整TOCControl控件中图层的显示顺序

在ArcMap中,用户只需通过鼠标拖动,就能调整“Table of Contents”中图层显示的顺序。但TOCControl控件本身并没有实现此功能,需要开发人员编程实现。利用鼠标拖放调整图层的显示顺序的基本思路如下:

1)利用鼠标将需要调整显示顺序的图层拖放到目标位置,需要利用TOCControl控件的OnMouseDown和OnMouseUp两个事件,以及HitTest()和Update()方法。

2)利用OnMouseDown事件获取需要调整显示顺序的图层,利用OnMouseUp事件,获得目标图层及其索引号,利用IMap提供的MoveLayer(源图层,目标图层索引)方法,将需要调整显示顺序的图层移到目标图层的下方。使用TOCControl控件提供的Update()方法更新TOCControl控件中显示的内容。

3)需要声明的全局变量包括:

ITOCControl mTOCControl;//需要在窗体层对其进行实例化 ILayer pMovelayer;//需要调整显示顺序的图层 int toIndex;//存放目标图层索引

并在窗体的Form_Load事件中对mTOCControl进行实例化 mTOCControl = axTOCControl1.Object as ITOCControl;

4)在TOCControl的OnMouseDown事件中添加下列代码

private void axTOCControl1_OnMouseDown(object sender, ITOCControlEvents_OnMouseDownEvent e) {

esriTOCControlItem item = esriTOCControlItem.esriTOCControlItemNone; if (e.button == 1) {

IBasicMap map = null; ILayer layer = null; object other = null; object index = null;

mTOCControl.HitTest(e.x, e.y, ref item, ref map, ref layer, ref other, ref index);

if (item == esriTOCControlItem.esriTOCControlItemLayer)

{

if (layer is IAnnotationSublayer) return; else {

pMoveLayer = layer; } } } }

6)在TOCControl的OnMouseUp事件中添加代码

private void axTOCControl1_OnMouseUp(object sender, ITOCControlEvents_OnMouseUpEvent e) {

if (e.button == 1) {

esriTOCControlItem item = esriTOCControlItem.esriTOCControlItemNone; IBasicMap map = null; ILayer layer = null; object other = null; object index = null;

mTOCControl.HitTest(e.x, e.y, ref item, ref map, ref layer, ref other, ref index);

IMap pMap = axMapControl1.ActiveView.FocusMap;

if (item == esriTOCControlItem.esriTOCControlItemLayer || layer != null) {

if (pMoveLayer != layer) {

ILayer pTempLayer;

for (int i = 0; i < pMap.LayerCount; i++) {

pTempLayer = pMap.get_Layer(i); if (pTempLayer == layer) {

toIndex = i; } }

pMap.MoveLayer(pMoveLayer, toIndex); axMapControl1.ActiveView.Refresh(); mTOCControl.Update(); } } }

}

至此,已经可以利用鼠标调整TOCControl中图层的显示顺序,而位于主视图中的地图显示顺序也会进行相应调整。

(2)点击鼠标右键显示所在图层的属性表内容

在ArcMap中,“Table of Contents”中右键菜单功能非常丰富,在利用ArcGIS Engine进行GIS软件开发时,TOCControl控件本身并没有提供右键菜单功能,需要通过自己编程来实现。本实例实现了在TOCControl控件中点击鼠标右键,选择图层,弹出所选图层属性表的功能,软件开发的基本思路如下:

A. 利用鼠标点击,选择需要打开属性表的图层; B. 根据所选择的图层,创建数据表; C. 新建一个窗体对象,在其上放置DataGridView控件,利用它打开创建的属性表。 ?利用鼠标右键选择需要打开属性表的图层

在axTOCControl1的OnMouseDown事件中,添加下列代码,以便选择需要打开其属性表的图层。

else if (e.button == 2)//探测鼠标右键按下 {

//鼠标右键按下

if (axMapControl1.LayerCount > 0)//主视图中要有地理数据 {

esriTOCControlItem mItem = new esriTOCControlItem(); IBasicMap pMap = new MapClass(); ILayer pLayer = new FeatureLayerClass(); object pOther = new object(); object pIndex = new object();

axTOCControl1.HitTest(e.x, e.y, ref mItem, ref pMap, ref pLayer, ref pOther, ref pIndex);

IGeoFeatureLayer pGeoFeatureLayer;

pGeoFeatureLayer = (IGeoFeatureLayer)pLayer; //添加显示属性表的代码 }

}

?根据所选图层创建数据表

public static System.Data.DataTable CreateDataTable(ILayer pLayer, string tableName) {

//根据选择的图层创建空DataTable

System.Data.DataTable pDataTable = CreateDataTableByLayer(pLayer, tableName); pDataTable.TableName = pLayer.Name; //取得图层类型

string shapeType = getShapeType(pLayer); //创建DataTable的行对象 DataRow pDataRow = null; //从ILayer查询到ITable

ITable pTable = pLayer as ITable;

ICursor pCursor = pTable.Search(null, false); //取得ITable中的行信息

IRow pRow = pCursor.NextRow(); int n = 0;

while (pRow != null) {

//新建DataTable的行对象

pDataRow = pDataTable.NewRow();

for (int i = 0; i < pRow.Fields.FieldCount; i++) {

//如果字段类型为esriFieldTypeGeometry,则根据图层类型设置字段值

if (pRow.Fields.get_Field(i).Type == esriFieldType.esriFieldTypeGeometry) {

pDataRow[i] = shapeType; }

//当图层类型为Anotation时,要素类中会有esriFieldTypeBlob类型的数据, //其存储的是标注内容,如此情况需将对应的字段值设置为Element

else if (pRow.Fields.get_Field(i).Type == esriFieldType.esriFieldTypeBlob) {

pDataRow[i] = \; } else {

pDataRow[i] = pRow.get_Value(i); } }

//添加DataRow到DataTable pDataTable.Rows.Add(pDataRow); pDataRow = null; n++;

pRow = pCursor.NextRow(); }

return pDataTable;

}

应该将上述方法封装在一个类里面。假设封装在GeoMapLoad类里。该方法还调用了一个方法getShapeType,用于确定图层的类型,即判断图层是属于点图层、线图层,还是面图层。

public static string getShapeType(ILayer pLayer) {

IFeatureLayer pFeatLyr = (IFeatureLayer)pLayer; switch (pFeatLyr.FeatureClass.ShapeType) {

case esriGeometryType.esriGeometryPoint: return \;

case esriGeometryType.esriGeometryPolyline: return \;

case esriGeometryType.esriGeometryPolygon: return \; default: return \; }

}

创建空表的方法如下:

private static System.Data.DataTable CreateDataTableByLayer(ILayer pLayer, string tableName) {

//创建一个DataTable表

System.Data.DataTable pDataTable = new System.Data.DataTable(tableName); //取得ITable接口

ITable pTable = pLayer as ITable; IField pField = null; DataColumn pDataColumn;

//根据每个字段的属性建立DataColumn对象

for (int i = 0; i < pTable.Fields.FieldCount; i++) {

pField = pTable.Fields.get_Field(i); //新建一个DataColumn并设置其属性

pDataColumn = new DataColumn(pField.Name); if (pField.Name == pTable.OIDFieldName) {

pDataColumn.Unique = true;//字段值是否唯一 }

//字段值是否允许为空

pDataColumn.AllowDBNull = pField.IsNullable; //字段别名

pDataColumn.Caption = pField.AliasName; //字段数据类型

pDataColumn.DataType = System.Type.GetType(ParseFieldType(pField.Type)); //字段默认值

pDataColumn.DefaultValue = pField.DefaultValue; //当字段为String类型是设置字段长度 if (pField.VarType == 8) {

pDataColumn.MaxLength = pField.Length; }

//字段添加到表中

pDataTable.Columns.Add(pDataColumn); pField = null; pDataColumn = null; }

return pDataTable;

}

public static string ParseFieldType(esriFieldType fieldType) {

switch (fieldType) {

case esriFieldType.esriFieldTypeBlob: return \;

case esriFieldType.esriFieldTypeDate: return \;

case esriFieldType.esriFieldTypeDouble: return \;

case esriFieldType.esriFieldTypeGeometry: return \;

case esriFieldType.esriFieldTypeGlobalID: return \;

case esriFieldType.esriFieldTypeGUID: return \;

case esriFieldType.esriFieldTypeInteger: return \;

case esriFieldType.esriFieldTypeOID: return \;

case esriFieldType.esriFieldTypeRaster: return \;

case esriFieldType.esriFieldTypeSingle: return \;

case esriFieldType.esriFieldTypeSmallInteger: return \;

case esriFieldType.esriFieldTypeString: return \; default:

return \; }

}

//因为DataTable的表名不允许含有“.”,因此我们用“_”替换。函数如下: public static string getValidFeatureClassName(string FCname) {

int dot = FCname.IndexOf(\); if (dot != -1) {

return FCname.Replace(\, \); }

return FCname;

}

?显示选择图层的属性表。需要新添加一个窗体,在本例中为GeoMapAttribute,在窗体上添加DataGridView控件,并将其Modifiers属性改为Public。利用下面的代码,就能将所选图层的属性表显示出来。这部分代码应放在第一步选择目标图层的代码后面。

string layerPath; string layerDatafileName; IWorkspaceName pWorkspaceName; IDatasetName pDatasetName; IDataLayer pDataLayer;

pDataLayer = pGeoFeatureLayer as IDataLayer;

pDatasetName = pDataLayer.DataSourceName as IDatasetName; pWorkspaceName = pDatasetName.WorkspaceName; layerPath = pWorkspaceName.PathName;

layerDatafileName = pLayer.Name.Trim() + \; string dbfFilePath = layerPath; ; string dbfFileName = layerDatafileName; ;

IWorkspaceFactory pWorkspaceFactory = new ShapefileWorkspaceFactoryClass(); IWorkspace pWorkspace = pWorkspaceFactory.OpenFromFile(dbfFilePath, 0); IFeatureWorkspace pFeatureWorkspace = pWorkspace as IFeatureWorkspace; if (pFeatureWorkspace != null) {

IFeatureClass pFeatureClass = pFeatureWorkspace.OpenFeatureClass(dbfFileName); if (pFeatureClass != null) {

DataTable dt = new DataTable(); DataColumn dc;

for (int i = 0; i <= pFeatureClass.Fields.FieldCount - 1; i++) {

dc = new DataColumn(pFeatureClass.Fields.get_Field(i).Name); dt.Columns.Add(dc); }

IFeatureCursor pFeatureCursor = pFeatureClass.Search(null, false); IFeature pFeature = pFeatureCursor.NextFeature(); DataRow dr;

while (pFeature != null) {

dr = dt.NewRow();

for (int j = 0; j <= pFeatureClass.Fields.FieldCount - 1; j++) {

if (pFeatureClass.FindField(pFeatureClass.ShapeFieldName) == j) {

dr[j] = pFeatureClass.ShapeType.ToString(); } else {

dr[j] = pFeature.get_Value(j).ToString(); } }

dt.Rows.Add(dr);

pFeature = pFeatureCursor.NextFeature(); }

GeoMapAttribute frmTable = new GeoMapAttribute(); frmTable.Show();

frmTable.dataGridView1.DataSource = dt; } }

上面的所有代码,还可以进行优化,先应该利用鼠标右键点击目标图层,并弹出列表菜单,其中应该包含打开属性表。当用户用鼠标选择弹出菜单中的打开属性表时,系统才显示所选图层的属性表。这样利用后面的ToolbarControl和有关菜单定制方面的知识。随后再讲。

2.4 ToolbarControl及相关对象

2.4.1 概述

ToolbarControl命名空间包含以下6个对象及相关接口:ToolbarControl、ToolbarItem、ToolbarMenu、CommandPool、CustomizeDialog、MissingCommand。这些对象之间的结构关系,如下图所示。

ToolbarControl ToolbarItem CustomizeDialog ToolbarMenu CommandPool MissingComman

图 ToolbarControl及相关对象之间的关系

ToolbarControl要与一个伙伴控件协同工作。伙伴控件可以是MapControl、

PageLayoutControl、ReaderControl、SceneControl或GlobeControl。伙伴控件可以利用ToolbarControl控件的属性页设置,也可以通过编程设置。该控件的伙伴控件都实现了IToolbarBuddy接口,这个接口用于设置伙伴控件的CurrenTool属性。ToolbarControl不仅提供了部分用户界面,而且还提供了部分应用程序框架。ArcGIS Desktop应用程序,如ArcMap、ArcGlobe和ArcScene等具有强大而灵活的框架,包括诸如工具条、命令、菜单、泊靠窗口和状态条等用户界面组件。这种框架是终端用户可以通过改变位置、添加和删除这些用户界面组件来定制应用程序。

2.4.2 ToolbarControl控件

ToolbarControl使用钩子(hook)来联系命令对象和MapControl或PageLayoutControl控件,并提供属性、方法和事件用于:

?管理控件外观 ?设置伙伴控件 ?添加、删除命令项 ?设置当前工具 ?定制工具

该控件的主要接口包括: ?IToolbarControl ?IToolbarControl2

?IToolbarControlEvents

(1)IToolbarControl

该接口是任何与ToolbarControl有关任务的出发点,如设置控件外观、设置伙伴控件、添加或取出命令、工具、菜单、定制ToolbarControl的内容等。

IToolbarControl接口提供的主要属性包括:Buddy、CommandPool、CurrentTool、Customize、CustomProperty、Enabled、Object、OptionStack、ToolTips、TextAlignment、UpdateInterval等。

IToolbarControl接口提供的主要方法包括:AddItem、AddMenuItem、AddToolbarDef、Find、GetItemRect、HitTest、MoveItem、Remove、RemoveAll、SetBuddyControl、Update等。

(2)IToolbarControl2

该接口在继承IToolbarControl接口的基础上,又添加了一些新的方法和属性。代表目前版本的最新接口。

(3)IToolbarControlEvents

该接口是一个事件接口,它定义了ToolbarControl能够处理的全部事件。如

OnDoubleClick、OnItemClick、OnKeyDown、OnKeyUp、OnMouseDown、OnMouseUp和OnMouseMove等。

(4)ToolbarControl上的命令

在ToolbarControl上可以以下驻留三类命令:

1)实现了相应单击事件的ICommand接口的单击命令(简单命令)。用户单击事件会导致对ICommand:OnClick方法的调用,并执行某种操作。通过改变ICommand:Checked属性的值,简单命令项的行为就像开关那样,单击命令是可以驻留在菜单中的唯一命令类型。

2)实现了ICommand接口和ITool接口、需要终端用户与伙伴控件的显示进行交互的工具。ToolbarControl维护着一个CurrentTool属性,当终端用户单击ToolbarControl上的工具时,该工具就成为CurrentTool,而前一个工具就处于非活动状态。ToolbarControl会设置伙伴控件的CurrentTool属性,当某个工具为CurrentTool时,该工具会从伙伴控件收到鼠标和键盘事件。

3)实现了ICommand接口和IToolControl接口的工具控件。这通常为用户界面组件,象ToolbarControl上的列表框和组合框。ToolbarControl驻留了来自IToolControl:hWnd属性窗口句柄提供的一个小窗口。只能向ToolbarControl添加特定工具控件的一个例程。

有三种方法可以将命令添加到ToolbarControl中。第一种方法是指定唯一识别命令的一个UID对象(使用GUID),使用示例如下:

UID uID = new UIDClass();

uID.Value = \; axToolbarControl1.AddItem(uID, -1, -1, false, 0,

esriCommandStyles.esriCommandStyleIconOnly);

第二种方法指定一个progID,使用示例如下:

string progID = \; axToolbarControl1.AddItem(progID, -1, -1, false, 0, esriCommandStyles.esriCommandStyleIconOnly);

第三种方法是提供某个现有命令对象的一个例程,使用示例如下:

ICommand command = new ControlsMapFullExtentCommandClass(); axToolbarControl1.AddItem(command, -1, -1, false, 0, esriCommandStyles.esriCommandStyleIconOnly);

上述三种方法将都是调用AddItem方法将命令加入ToolbarControl中,该方法参数的具体含义请查阅相应的帮助文档。

(5)更新命令

默认情况下,ToolbarControl每半秒自动更新其自身一次,以确保驻留在ToolbarControl上的每个工具条命令项的外观与其底层命令的Enabled、Bitmap和Caption属性同步。改变UpdateInterval属性可以更改更新的频率。在应用程序首次调用Update方法时,ToolbarControl会检查每个工具条命令项底层命令的ICommand:OnCreate方法来检查是否已经被调用过。若还没有调用过该方法,则ToolbarControl将作为钩子(hook)被自动传递给ICommand:OnCreate方法。

(6)定制

利用鼠标选择窗体上的ToolbarControl空间实例对象,在单击鼠标右键,将弹出该

axToolbarControl实例对象的属性对话框,选择属性对话框面板上的Items页面,利用其中的Add和Remove All命令按钮,就可以定制ToolbarControl控件上的命令、工具、菜单等。

2.4.3 ToolbarItem

ToolbarItem就是驻留在Toolbarcontrol或工具条菜单上的单个Command、Tool、

ToolControl或Menu Item菜单。ToolbarItem接口的属性决定工具条命令项的外观。例如,工具条命令项是否在其左侧有一条垂直线表示是否开始一个命令组(Group)、命令项的样式是否有一个位图、标题或两者都有。

ToolBarItem是一个不可创建的对象,引用不可创建的对象必须通过其它对象获得。ToolbarItem的主要接口是IToolbarItem,该接口下面包含的属性如下图所示。

图 IToolbarItem接口的属性

2.3.4 ToobarMenu组件类

ToolbarControl可以驻留下拉菜单。工具条菜单(ToolbarMenu)表示单击命令项的一个垂直列表。用户必须选择工具条菜单上的一个命令项,或单击工具条菜单之外的地方使其消失。工具条菜单只能驻留命令项(不允许驻留工具或工具控件),工具条菜单本身可以驻留在ToolbarControl上,作为子菜单驻留在另一个工具条菜单上,或者作为右键单击弹出式菜单。

每个Toolbarcontrol和工具条菜单都有一个命令池(CommandPool),用于管理其使用的命令对象集。命令池中的对象可以重复添加,软件开发人员可以通过编程探测命令池中的对象是否已经添加过,在实际GIS软件开发中,相同的命令对象在命令池中只添加一次。

ToolbarMenu的主要接口是IToolbarMenu,该接口包含的主要成员如下图所示。

图 ToolbarMenu接口的主要成员

2.5 ControlCommands

ControlCommands命名空间中提供了大量的命令、工具条及工具、工具控件和菜单。利用ArcGIS Engine进行GIS软件开发,可以直接使用这些对象,使用这些对象包括两种方式,一是通过Toolbarcontrol控件使用,另外一种是不通过Toolbarcontrol控件使用。开发人员在实际软件开发过程中,可以创建自己的自定义命令、工具和菜单来扩展ArcGIS Engine提供的命令集。HookHelper、GlobeHookHelper和SceneHookHelper对象可以简化这种开发。

2.5.1 通过ToolbarControl控件使用ControlCommands

通过Toolbarcontrol控件使用ControlCommands有三条途径:

(1)在程序设计阶段,利用ToolbarControl控件的属性页设置;

(2)在程序代码中,使用AddItem、AddMenuItem或AddToolbarDef方法将要用到的ControlCommands的实例对象添加到ToolbarControl控件中。命令可以使用AddItem或AddSubMenu方法添加到ToolbarMenu上。

(3)最终用户可以通过Customize对话框进行设置。

2.5.2 不通过ToolbarControl控件使用ControlCommands

利用ToolbarControl控件建立GIS应用程序可快速构建类似ArcGIS DeskTop应用程序样的框架组成部分,但在下列情况下应用程序可能并不需要ToolbarControl:

?ToolbarControl的可视化外观可能不符合应用程序需要。 ?不需要实现ToolbarControl的命令对象。 ?应用程序中已有一个现有的应用程序框架。

?ToolbarControl及其驻留的命令不易于跨多个伙伴控件使用。

在上述情况下,开发人员必须直接操作MapControl、PageLayoutControl、SceneControl、GlobeControl和ReaderControl。应用程序可能需要诸如命令按钮、状态条和列表框等用户界面组件,这些组件可由开发环境提供。

作为选择,AE提供的控件命令(ControlCommands)或者使用HookHelper、GlobeHookHelper或SceneHookHelper直接操作单个的AE控件。但开发人员只需在适当的时候调用ICommand:OnCreate和ICommand:OnClick方法,以读取ICommand接口上的属性,以便建立用户界面。

通过编程创建命令的一个新例程,并将单个AE控件传递给OnCreate事件。如要用3D的放大到全图“ZoomToFullExtent”命令操作GlobeControl,则GlobeControl必须作为“钩子(hook)”传递给OnCreate方法。

开发人员可以使用脱离ToolbarControl的命令池(CommandPool)对象,以便管理应用程序所使用的命令。命令池支持基于命令的“钩子(hook)”属性,调用各个命令的OnCreate方法。

如果命令只实现了ICommand接口,开发人员可以在适当的时候调用OnClick方法以执行特定行为。如果命令为实现了ICommand接口和ITool接口的工具,开发人员必须将该工具设置为AE控件中的CurrentTool,AE控件会将任何键盘和鼠标事件传送给该工具。如下面的示例所示:

ICommand pCmd;

pCmd = new ControlsSelectFeaturesToolClass(); pCmd.OnCreate(axMapControl1.Object);

axMapControl1.CurrentTool = pCmd as ITool;//将pCmd定义为操控当前axMapControl1的工具

2.6 应用开发实例

2.6.1 概述

命令和工具是ToolbarControl控件上最常用的两种对象。当命令对象被驻留到

ToolbarControl控件上后,就会立即调用ICommand:OnCreate方法,将一个句柄(handle)或钩子(hook)传递给该命令操作的应用程序。在命令实现时,先要查看被传递的钩子是否是命令可以操作的对象,若被传递的钩子不被支持,命令将自动失效。若钩子被支持,命令将存储该钩子,以便以后使用。如“打开地图文档”命令要操作MapControl或PageLayoutControl,则MapControl或PageLayoutControl将作为钩子被传递给OnCreate方法,该命令就会存储该钩子,以便后续使用。

若ToolBarControl被作为钩子传递给OnCreate事件,则命令一般会通过Buddy属性检查与该工具条协同使用的伙伴控件类型。当驻留在ToolbarControl上的一个命令只能操作

ReaderControl,而该ToolbarControl控件的伙伴控件却是MapControl,则该命令将自动失效。

HookHelper、GlobeHookHelper和SceneHookHelper可以帮助开发人员创建自定义命令,以操作ArcGIS控件。

?HookHelper用于帮助开发人员创建能操作MapControl、PageLayoutControl和

ToolbarControl桌面应用程序的自定义命令。

?SecneHookHelper用于帮助开发人员创建能操作SceneControl和ToolbarControl桌面应用程序的自定义命令。

?GlobeHookHelper用于帮助开发人员创建操作GlobeControl和ToobarControl桌面应用程序的自定义命令。

并不是由开发人员向命令的OnCreate方法中添加代码以确定传递给该命令的钩子的类型,而是由helper对象来处理这个任务。Helper对象用于控制钩子,并返回ActiveView对象、PageLayout对象、Map对象、Globe对象和Scene对象,而不管被传递的是何种类型的钩子。当终端用户单击工具条上的某个命令项时,ICommand:OnClick方法被调用。根据钩子类型,利用钩子访问来自伙伴控件的所需对象来完成某项工作。IHookHelper是HookHelper的主接口,其属性如下表所示。

表 IHookHelper接口的主要属性

属性类型 属性名称 ActiveView FocusMap Hook OperationStack PageLayout 功能描述 被关联控件或应用程序的活动视图。 被关联控件或应用程序的焦点地图。 关联多项,该对象与ICommand:OnCreate中被传递的对象一致。 被关联控件或应用程序的操作栈,用于取消与恢复操作。 被关联控件或应用程序的PageLayout。 一般情况下所有的命令对象都要实现ICommand接口的所有成员,所有的工具对象都要

实现ICommand和ITool两个接口的所有成员。为简化自定义命令和工具的开发,ESRI提供了BaseCommand和BaseTool两个抽象基类。这两个类都是抽象类,不能直接被实例化,只能被其它类继承使用。这两个基类被定义在ESRI.ArcGIS.Utility程序集中,属于ESRI.ArcGIS.Utility.BaseClasses命名空间。

这两个基类为ICommand和ITool每个成员提供了缺省实现,这样就简化了创建自定义命令和工具的过程。开发者不用为每个成员提供实现代码,只需重载自定义命名或工具所需的成员,如ICommand:OnCreate,该成员在初始化的类中,必须重载,以便完成用户希望的操作。

在VS.NET开发环境中,为ArcGIS应用程序创建命令和工具,推荐使用这些基类。以便能够更快速、更简单、更少出错地创建命令和工具。BaseClasses有两种:BaseCommand和BaseTool,它们都有重载的构造函数,使得用户可以快速地通过构造函数参数设置命令和工具的许多属性。

重载的BaseCommand构造函数有以下签名:

public BaseCommand(System.Drawing.Bitmap bitmap, string caption, string category, int helpContextId, string helpFile, string message, string name,

string toolTip);

重载的BaseTool构造函数有以下签名:

public BaseTool(System.Drawing.Bitmap bitmap, string caption, string category,

System.Windows.Forms.Cursor cursor, int helpContextId, string helpFile, string message,

string name, string toolTip);

(1)继承基类

当编写一个新类时,可以使用这些参数化的构造函数。下面是一个名为PanTool的新类,它继承了BaseTool类。

public PanTool():base(null,\地图漫游\,\,CursorType,0,\,\地图漫游

\,\,\)

{ //...

}

(2)直接设置基类成员

作为使用参数化构造函数的备选方案,可以直接设置基类成员。基类暴露其内部成员变量给继承类,每个属性一个,这样就可以在继承类中直接访问它们。例如,代替使用构造函数设置Caption属性或重载Caption函数,可以在继承类的构造函数中,设置基类中声明的m_caption类成员变量。

(3)重载成员

当创建继承一个基类的自定义命令和工具时,可能需要重载几个成员。当重载类中的一个成员时,会执行用户提供给该成员的实现代码,而不会执行从基类继承而来的实现代码。

在下面的开发实例中,将结合HookHelper和基类(BaseCommand或BaseTool)进行自定义命令、工具及菜单的开发。

2.6.2 自定义命令开发实例

当定义新类开发自定义命令时,需要继承基类BaseCommand,一般只需重写OnCreate()和OnClick()方法。在类的构造函数中,需要对自定义命令类进行初始化。下面通过两个自定义命令进行举例说明。

(1)清除当前活动工具的命令

在GIS应用程序操作过程中,某个工具一旦使用,若不使用下一个工具,它将一直处于活动状态,这将给软件操作带来不便。可以开发一个工具,清除ToolbarControl上当前的活动工具。具体步骤如下:

1)点击开发环境中的项目菜单,选择其中的添加新类,在弹出的对话框中,选择左侧类别列表框中的ArcGIS,选择右侧模板列表框中的BaseCommand,输入新建类名称,本例为GeoMapComm,点击底部的添加按钮,在弹出的对话框中,选择MapControl or PageLayoutControl Tool选项,就能生成一个名为GeoMapComm的新类。

2)在类中定义IToolbarControl的接口变量pToolbarControl,并在类的构造函数中,改变所继承的基类属性。

3)在OnCreate重载函数中,将hook显示转换为IToolbarControl,并赋给pToolbarControl

变量。

4)在OnClick重载函数中,将pToolbarControl接口变量的CurrentTool属性,将其设置为空即可。

图 自定义命令类继承BaseCommand基类

图 自定义工具用于操作MapControl或PageLayoutControl

5)全部代码如下:

using System; using System.Drawing;

using System.Runtime.InteropServices; using ESRI.ArcGIS.ADF.BaseClasses; using ESRI.ArcGIS.ADF.CATIDs; using ESRI.ArcGIS.Controls;

namespace AppGIS1//包含类的项目名称

{

public sealed class GeoMapComm : BaseCommand {

IToolbarControl pToolbarControl; public GeoMapComm() {

//在构造函数可以改变类的属性

base.m_category = \; //将命令进行归类 base.m_caption = \清除当前活动工具\; //标明命令标题 base.m_message = \清除当前活动工具\; //信息提示 base.m_toolTip = \清除当前活动工具\; //鼠标提示信息 base.m_name = \清除当前活动工具\; //命令名称 try {

//设命令图标和鼠标显示状态

string bitmapResourceName = GetType().Name + \; base.m_bitmap = new Bitmap(GetType(), bitmapResourceName); }

catch (Exception ex) {

System.Diagnostics.Trace.WriteLine(ex.Message, \); } }

#region Overriden Class Methods

public override void OnCreate(object hook) {

if (hook == null) return;

pToolbarControl= hook as IToolbarControl; }

public override void OnClick() {

//添加用户点击时的操作,以清除当前活动工具 pToolbarControl.CurrentTool = null; }

#endregion } }

6)利用下面的代码可将命令添加到ToolbarControl控件上。

axToolbarControl1.AddItem(new GeoMapComm(), -1, -1, false, 0, esriCommandStyles.esriCommandStyleIconOnly);

(2)清除当前地图中的选择要素

下面通过自定义命令,以清除当前地图中的选择要素。步骤与上例一样,所建新类名为ClearFeaSele。具体软件代如下:

using System; using System.Drawing;

using System.Runtime.InteropServices; using ESRI.ArcGIS.ADF.BaseClasses; using ESRI.ArcGIS.Controls; using ESRI.ArcGIS.Carto; namespace AppGIS1 {

public sealed class ClearFeaSele : BaseCommand {

private IHookHelper m_hookHelper;

public ClearFeaSele() {

//改变继承类的属性

base.m_category = \; base.m_caption = \清除当前地图选择要素\; base.m_message = \清除当前地图选择要素\; base.m_toolTip = \清除当前地图选择要素\; base.m_name = \清除当前地图选择要素\; try {

//设置命令的图标

string bitmapResourceName = GetType().Name + \; base.m_bitmap = new Bitmap(GetType(), bitmapResourceName); }

catch (Exception ex) {

System.Diagnostics.Trace.WriteLine(ex.Message, \); } }

#region Overriden Class Methods

public override void OnCreate(object hook) {

if (hook == null) return;

if (m_hookHelper == null)

m_hookHelper = new HookHelperClass(); //将mapControl作为钩子传递 m_hookHelper.Hook = hook; }

public override void OnClick() {

m_hookHelper.FocusMap.ClearSelection();

//获取焦点地图的IActiveView接口

IActiveView pActiveView = (IActiveView)m_hookHelper.FocusMap; pActiveView.Refresh(); }

#endregion } }

2.6.3 自定义开发工具

自定义工具的开发需继承基类BaseTool,在类的构造函数中,需对自定义工具进行初始化。因工具需要鼠标与地图类控件(MapControl、PageLayoutControl等)进行交互,以完成某个特定功能。除需重写OnCreate()方法外,还要根据具体情况重写其它方法,如OnMouseDown()、OnMouseMove()和OnMouseUp()等。下例通过开发实现地图缩小的ZoomOut工具进行举例说明。基本步骤如下:

(1)建立新类MapZoomOutTool,需要继承BaseTool基类,如下图所示。在随后弹出的对话框中仍然选择MapControl or PageLayoutControl Tool选项。

图 自定义工具类继承BaseTool基类

(2)在类中需要判断用户是点击缩小还是拉框缩小,具体实现代码如下:

using System.Runtime.InteropServices; using ESRI.ArcGIS.ADF.CATIDs; using ESRI.ArcGIS.Carto; using ESRI.ArcGIS.Controls; using ESRI.ArcGIS.esriSystem; using ESRI.ArcGIS.Geometry; using ESRI.ArcGIS.ADF.BaseClasses; using ESRI.ArcGIS.ADF; using ESRI.ArcGIS.Display;

public sealed class MapZoomOutTool : BaseTool {

private INewEnvelopeFeedback m_feedBack; private IPoint m_point; private Boolean m_isMouseDown;

private IHookHelper m_HookHelper = new HookHelperClass(); public MapZoomOutTool() {

//设置command属性 base.m_caption = \缩小\; base.m_message = \缩小\; base.m_toolTip = \缩小\; base.m_name = \缩小\;

base.m_category = \;

base.m_bitmap = new System.Drawing.Bitmap(string.Format(\软件开发统一图标\\\\ForStockIcon\\\\ZoomOut.bmp\,Application.StartupPath));

base.m_cursor = new System.Windows.Forms.Cursor(string.Format(\软件开发统一图标\\\\ForStockIcon\\\\ZoomOut.cur\,Application.StartupPath)); }

public override void OnCreate(object hook) {

m_HookHelper.Hook = hook; }

public override bool Enabled { get {

if (m_HookHelper.FocusMap == null) return false; return true; } }

public override void OnMouseDown(int button, int shift, int x, int y) {

if (m_HookHelper.ActiveView == null) return; //如果是视图

if (m_HookHelper.ActiveView is IActiveView) {

//图上创建一个点 IPoint pPoint =

(IPoint)m_HookHelper.ActiveView.ScreenDisplay.DisplayTransformation.ToMapPoint(x, y); //得到地图框

IMap pMap = m_HookHelper.ActiveView.HitTestMap(pPoint); if (pMap == null) return;

if (pMap != m_HookHelper.FocusMap)

{

m_HookHelper.ActiveView.FocusMap = pMap;

m_HookHelper.ActiveView.PartialRefresh(esriViewDrawPhase.esriViewGraphics, null, null); } }

//在焦点图上创建一个点

IActiveView pActiveView = (IActiveView)m_HookHelper.FocusMap;

m_point = pActiveView.ScreenDisplay.DisplayTransformation.ToMapPoint(x, y); m_isMouseDown = true; }

public override void OnMouseMove(int button, int shift, int x, int y) {

if (!m_isMouseDown) return; //得到焦点图

IActiveView pActiveView = (IActiveView)m_HookHelper.FocusMap; //初始一个范围

if (m_feedBack == null) {

m_feedBack = new NewEnvelopeFeedbackClass(); m_feedBack.Display = pActiveView.ScreenDisplay; m_feedBack.Start(m_point); }

base.m_cursor = new System.Windows.Forms.Cursor(string.Format(\软件开发统一图标\\\\ForStockIcon\\\\MoveZoomOut.cur\,Application.StartupPath)); //画范围

m_feedBack.MoveTo(pActiveView.ScreenDisplay.DisplayTransformation.ToMapPoint(x, y)); }

public override void OnMouseUp(int button, int shift, int x, int y) {

base.m_cursor = new System.Windows.Forms.Cursor(string.Format(\软件开发统一图标\\\\ForStockIcon\\\\ZoomOut.cur\,Application.StartupPath)); if (!m_isMouseDown) return; IEnvelope pEnvelope; IEnvelope pFeedEnvelope; double newWidth, newHeight; //得到焦点图

IActiveView pActiveView = (IActiveView)m_HookHelper.FocusMap; //如果不画范围

if (m_feedBack == null) {

//点击缩小当前范围

pEnvelope = pActiveView.Extent; pEnvelope.Expand(1.5, 1.5, true); pEnvelope.CenterAt(m_point);

} else {

//停止画框

pFeedEnvelope = m_feedBack.Stop();

if (pFeedEnvelope.Width == 0 || pFeedEnvelope.Height == 0) {

m_feedBack = null; m_isMouseDown = false; }

newWidth = pActiveView.Extent.Width * (pActiveView.Extent.Width / pFeedEnvelope.Width);

newHeight = pActiveView.Extent.Height * (pActiveView.Extent.Height / pFeedEnvelope.Height);

//建立新的范围坐标

pEnvelope = new EnvelopeClass();

pEnvelope.PutCoords(pActiveView.Extent.XMin - ((pFeedEnvelope.XMin - pActiveView.Extent.XMin) * (pActiveView.Extent.Width / pFeedEnvelope.Width)), pActiveView.Extent.YMin - ((pFeedEnvelope.YMin - pActiveView.Extent.YMin) * (pActiveView.Extent.Height / pFeedEnvelope.Height)), (pActiveView.Extent.XMin - ((pFeedEnvelope.XMin -

pActiveView.Extent.XMin) * (pActiveView.Extent.Width / pFeedEnvelope.Width))) + newWidth, (pActiveView.Extent.YMin - ((pFeedEnvelope.YMin -

pActiveView.Extent.YMin) * (pActiveView.Extent.Height / pFeedEnvelope.Height))) + newHeight); }

//建立新的视图范围

pActiveView.Extent = pEnvelope; pActiveView.Refresh(); m_feedBack = null; m_isMouseDown = false; } }

2.6.4 自定菜单开发实例

在ToolbarControl上不仅可以驻留命令和工具,还可以驻留菜单条。要在工具条上驻留菜单条,首先需要实现IMenuDef接口,在工具条上实现菜单功能,还需要将菜单中的所有命令类组织在一个项目文件(DLL)中,再在其它项目中添加该项目的引用。下面通过举例,将地图符号化的6个命令类和实现IMenuDef接口的类SymbologyMenu组织在一个项目Symbology中。

本实例将自定义下拉菜单和六种符号化的方法封装在了一个DLL中,并在新建的工程中引用封装好的DLL,具体步骤如下:

(1)新建DLL文件。打开VS2008,选择新建项目对话框中的“类库”,如下图所示,输入一个项目名,点击“确定”。

本文来源:https://www.bwwdw.com/article/am36.html

Top