《ArcGISEngine+C#实例开发教程》第一讲桌面GIS应用程序框架的建立
教程采用C#语言,以VS2005为开发工具。读者对象:ArcGISEngine(以下简称AE)开发初学者,了解AE基本体系,了解C#基本语法,了解VS2005的基本使用方法。预期学习效果:进一步理解AE的体系结构与开发方法,掌握基本的GIS桌面应用程序的开发。教程目录:第一讲桌面GIS应用程序框架的建立第二讲菜单的添加及其实现第三讲MapControl与PageLayoutControl同步第四讲状态栏信息的添加与实现第五讲鹰眼的实现第六讲右键菜单添加与实现教程Bug及优化方案1第七讲图层符号选择器的实现1第七讲图层符号选择器的实现2第八讲属性数据表的查询显示------------------------------------------------------------------第一讲:桌面GIS应用程序框架的建立本讲主要是使用MapControl、PageLayoutControl、ToolbarControl、TOCControl四个控件建立起基本的桌面GIS应用程序框架。最终成果预览如下:1、新建项目启动VS2005,选择“文件|新建|项目”,在项目类型中选择VisualC#,再选择Windows应用程序模板,输入名称“3sdnMap”,点击确定。在解决方案管理器中将“Form1.cs”重命名为“3sdnMap.cs”,在设计视图中,选中窗体,将其属性中的“Text”改为“3sdnMap”。2、添加控件选择工具箱中的“菜单和工具栏|MenuStrip”,将其拖入窗体。选择工具箱中的“ArcGISWindowsForms”节,将“ToolbarControl”控件拖入窗体,并将其属性中的Dock设置为Top。选择工具箱中的“菜单和工具栏|StatusStrip”,将其拖入到窗体。选择工具箱中的“容器|SplitContainer”容器拖入窗体,并将其属性中的Dock设置为Fill。将TabControl控件拖入Panel1,将Alignment属性设置为Bottom,Dock属性设置为Fill。点击TabPages属性右边的按钮,弹出TabPage集合编辑器,将tabPage1的Name设置为tabPageLayer,Text设置为图层,将tabPage2的Name设置为tabPageProperty,Text设置为属性。如下所示。选择“图层”选项卡,拖入TOCControl控件,设置Dock属性为Fill。选择“属性”选项卡,拖入DataGridView控件,设置Dock属性为Fill。拖入TabControl控件到Panel2,设置Dock属性为Fill。并上述类似的方法,将两个选项卡的Name和Text分别设置为:(tabPageMap、地图),(tabPageLayout,制版)。选择“地图”选项卡,拖入MapControl控件,设置Dock属性为Fill。选择“制版”选项卡,拖入PageLayoutControl控件,设置Dock属性为Fill。最后将LicenseControl控件拖入到窗体的任意地方。按F5编译运行,可以看到刚才布局好的程序界面了。3、控件绑定通过以上步骤添加的控件还只是单独存在,而我们的程序需要各控件间协同工作,因此要进行控件绑定。分别右击ToolbarControl、TOCControl控件,将Buddy设置为axMapControl1,如下图所示。这样,工具条和图层控件就与地图控件关联了。4、添加工具此时,工具条中还没有任何工具,添加的方法也很简单。右击ToolbarControl,选择“属性|Items”,点击Add,选择Commands选项卡中的Generic,双击Open、SaveAs、Redo、Undo即可将相应工具添加到工具条。常见的工具有:MapNavigation中的导航工具,MapInquiry中的查询工具,FeatureSelection中的选择工具,你可以根据需要酌情添加工具。5、编译运行按F5即可编译运行程序,至此桌面GIS应用程序框架基本框架已经搭建好了,你可以通过工具条的工具打开地图文档,浏览地图了,效果如开篇所示.第二讲菜单的添加及其实现
------------------------------------------------------------------在上一讲中,我们实现了应用程序基本框架,其中有个小错误,在此先跟大家说明下。在“属性”选项卡中,我们当时添加的是DataGridView控件,这个控件是用来显示数据表的,而专门用于属性的查询和设置的控件是PropertyGrid控件。因此请你删除“属性”选项卡中的DataGridView控件,再把位于“工具箱|所有Windows窗体|PropertyGrid”(如果没有,右击选择“选择项”以添加此控件)控件拖到该选项卡。在这一讲中,主要讲解菜单的添加和实现。1、添加菜单在设计视图中,单击菜单栏,会出现“请在此处键入”的提示,单击提示就可以键入菜单名称,如“文件”,再单击“文件”,即可输入其下拉子菜单,如下所示:Tips:每创建一个菜单,请在其属性面板中设置Name属性,而且不要为中文,因此Name值将是此菜单响应函数的函数名的一部分,带中文的函数名,总是不好吧。本讲中,我们将添加新建(New)、打开(Open)、添加数据(AddData)、保存(Save)、另存为(SaveAs)、退出(Exit)这些菜单,()内为相应的Name属性值。Tips:你可以在属性面板中的Text属性中,把菜单名设置为中英文形式,如“打开Open”,带下划线的O表示此项菜单的快捷键是字母O,设置方法是在相应字母前加上“&”字符,如“打开&Open”。但这种快捷键只在打开此下拉菜单时才有效,即当你单击“文件”菜单弹出下拉菜单时,按下字母O就可以定位到“打开”菜单。还有一种在程序运行时都有效的全局快捷键,可以在属性面板中的“ShortCutKeys”中设置。你还可以在属性面板中的Image属性中设置你喜欢的菜单图标。单击Image那一行右边的按钮,弹出如下菜单。选择“项目资源文件”,再单击导入就可以选择你的图标了。最终效果如下所示。注意,在解决方案面板中,选中刚才添加的所有图标,在其属性面板中将生成操作设置为“嵌入的资源”,这一点很重要!2、实现相关菜单首先定义指针(写在publicpartialclassForm1:Form下面即可):privateESRI.ArcGIS.Controls.IMapControl3m_mapControl=null;privateESRI.ArcGIS.Controls.IPageLayoutControl2m_pageLayoutControl=null;privateIMapDocumentpMapDocument;若以上指针无效,请添加以下引用:usingESRI.ArcGIS.Carto;usingESRI.ArcGIS.Controls;usingESRI.ArcGIS.esriSystem;usingESRI.ArcGIS.Display;usingESRI.ArcGIS.Geometry;usingESRI.ArcGIS.SystemUI;在设计视图中的属性面板中,选择Form1,即主窗体,单击事件按钮(闪电形状的那个按钮),打到“Load”事件并双击,添加此事件。在Form1_Load函数中初始化这些指针://取得MapControl和PageLayoutControl的引用m_mapControl=(IMapControl3)this.axMapControl1.Object;m_pageLayoutControl=(IPageLayoutControl2)this.axPageLayoutControl1.Object;依次双击每个菜单项,添加菜单响应函数。实现代码如下://////新建地图命令/////////privatevoidNew_Click(objectsender,EventArgse){//本命令涉及到MapControl和PageLayoutControl同步问题,将在下一讲中实现}//////打开地图文档Mxd命令/////////privatevoidOpen_Click(objectsender,EventArgse){//本命令涉及到MapControl和PageLayoutControl同步问题,将在下一讲中实现}//////添加数据命令/////////privatevoidAddData_Click(objectsender,EventArgse){intcurrentLayerCount=this.axMapControl1.LayerCount;ICommandpCommand=newControlsAddDataCommandClass();pCommand.OnCreate(this.axMapControl1.Object);pCommand.OnClick();}//////保存地图文档命令/////////privatevoidSave_Click(objectsender,EventArgse){//首先确认当前地图文档是否有效if(null!=m_pageLayoutControl.DocumentFilenamem_mapControl.CheckMxFile(m_pageLayoutControl.DocumentFilename)){//创建一个新的地图文档实例IMapDocumentmapDoc=newMapDocumentClass();//打开当前地图文档mapDoc.Open(m_pageLayoutControl.DocumentFilename,string.Empty);//用PageLayout中的文档替换当前文档中的PageLayout部分mapDoc.ReplaceContents((IMxdContents)m_pageLayoutControl.PageLayout);//保存地图文档mapDoc.Save(mapDoc.UsesRelativePaths,false);mapDoc.Close();}}//////另存为地图文档命令/////////privatevoidSaveAs_Click(objectsender,EventArgse)&&{//调用另存为命令ICommandcommand=newControlsSaveAsDocCommandClass();command.OnCreate(m_controlsSynchronizer.ActiveControl);command.OnClick();}//////退出程序/////////privatevoidExit_Click(objectsender,EventArgse){Application.Exit();}3、编译运行按F5编译运行程序。也许你会发现,菜单命令的实现方式都是类型的。没错,在AE9.2中,内置了许多常用的Command和Tool,如ControlsAddDataCommandClass、ControlsMapZoomInToolClass、ControlsMapPanToolClass等等,这些内置对象在ESRI.ArcGIS.Controls命名空间中,你可以对象浏览器中查看。而且这些内置对象的调用方式都类似,如下所示://定义ICommandcommand=newControlsSaveAsDocCommandClass();//创建command.OnCreate(m_controlsSynchronizer.ActiveControl);//调用command.OnClick();第三讲MapControl与PageLayoutControl同步------------------------------------------------------------------在ArcMap中,能够很方面地进行MapView和LayoutView两种视图的切换,而且二者之间的数据是同步显示的。关于两种视图同步的实现方法有多种,可以使用ObjectCopy对象进行数据硬拷贝,而比较简单的方法莫过于二者共享一份地图了,这也是最常用的方法。1、新建同步类ControlsSynchronizer在解决方案面板中右击项目名,选择“添加|类”,在类别中选择“VisualC#项目项”,在模板中选择“类”,输入类名“ControlsSynchronizer.cs”,将以下代码覆盖自动生成的代码:usingSystem;usingSystem.Drawing;usingSystem.Collections;usingSystem.ComponentModel;usingSystem.Windows.Forms;usingSystem.IO;usingSystem.Runtime.InteropServices;usingESRI.ArcGIS.esriSystem;usingESRI.ArcGIS.Carto;usingESRI.ArcGIS.Controls;usingESRI.ArcGIS.SystemUI;namespace_sdnMap{//////ThisclassisusedtosynchronizeagvenPageLayoutControlandaMapControl.///Wheninitialized,theusermustpassthereferenceofthesecontroltotheclass,bind///thecontroltogetherbycalling'BindControls'whichinturnsetsajoinedMapreferenced///bybothcontrol;andsetallthebuddycontrolsjoinedbetweenthesetwocontrols.///WhenalternatingbetweentheMapControlandPageLayoutControl,youshouldactivatethevisiblecontrol///anddeactivatetheotherbycallingActivateXXX.///Thiscalssislimitedtoasituationwherethecontrolsarenotsimultaneouslyvisible.///publicclassControlsSynchronizer{#regionclassmembersprivateIMapControl3m_mapControl=null;privateIPageLayoutControl2m_pageLayoutControl=null;privateIToolm_mapActiveTool=null;privateIToolm_pageLayoutActiveTool=null;privateboolm_IsMapCtrlactive=true;privateArrayListm_frameworkControls=null;#endregion#regionconstructor//////默认构造函数///publicControlsSynchronizer(){//初始化ArrayListm_frameworkControls=newArrayList();}//////构造函数/////////publicControlsSynchronizer(IMapControl3mapControl,pageLayoutControl):this(){//为类成员赋值m_mapControl=mapControl;m_pageLayoutControl=pageLayoutControl;}#endregion#regionproperties//////取得或设置MapControl///publicIMapControl3MapControl{get{returnm_mapControl;}set{m_mapControl=value;}}//////取得或设置PageLayoutControl///publicIPageLayoutControl2PageLayoutControl{IPageLayoutControl2get{returnm_pageLayoutControl;}set{m_pageLayoutControl=value;}}//////取得当前ActiveView的类型///publicstringActiveViewType{get{if(m_IsMapCtrlactive)return\"MapControl\";elsereturn\"PageLayoutControl\";}}//////取得当前活动的Control///publicobjectActiveControl{get{if(m_mapControl==null||m_pageLayoutControl==null)thrownewException(\"ControlsSynchronizer::ActiveControl:\\r\\nEitherMapControlorPageLayoutControlarenotinitialized!\");if(m_IsMapCtrlactive)returnm_mapControl.Object;elsereturnm_pageLayoutControl.Object;}}#endregion#regionMethods//////激活MapControl并解除thePagleLayoutControl///publicvoidActivateMap(){try{if(m_pageLayoutControl==null||m_mapControl==null)thrownewException(\"ControlsSynchronizer::ActivateMap:\\r\\nEitherMapControlorPageLayoutControlarenotinitialized!\");//缓存当前PageLayout的CurrentToolif(m_pageLayoutControl.CurrentTool!=null)m_pageLayoutActiveTool=m_pageLayoutControl.CurrentTool;//解除PagleLayoutm_pageLayoutControl.ActiveView.Deactivate();//激活MapControlm_mapControl.ActiveView.Activate(m_mapControl.hWnd);//将之前MapControl最后使用的tool,作为活动的tool,赋给MapControl的CurrentToolifm_mapActiveTool;m_IsMapCtrlactive=true;//为每一个的frameworkcontrols,设置Buddycontrol为MapControlthis.SetBuddies(m_mapControl.Object);}catch(Exceptionex){throwException(string.Format(\"ControlsSynchronizer::ActivateMap:\\r\\n{0}\ex.Message));}}//////激活PagleLayoutControl并减活MapCotrol///publicvoidActivatePageLayout(){try{if(m_pageLayoutControl==null||m_mapControl==null)throwException(\"ControlsSynchronizer::ActivatePageLayout:\\r\\nEitherMapControlPageLayoutControlarenotinitialized!\");(m_mapActiveTool!=null)m_mapControl.CurrentTool=newnewor//缓存当前MapControl的CurrentToolif(m_mapControl.CurrentTool!=m_mapControl.CurrentTool;//解除MapControlm_mapControl.ActiveView.Deactivate();null)m_mapActiveTool=//激活PageLayoutControlm_pageLayoutControl.ActiveView.Activate(m_pageLayoutControl.hWnd);//将之前PageLayoutControl最后使用的tool,作为活动的tool,赋给PageLayoutControl的CurrentToolif(m_pageLayoutActiveTool!=null)m_pageLayoutControl.CurrentTool=m_pageLayoutActiveTool;m_IsMapCtrlactive=false;//为每一个的frameworkcontrols,设置Buddycontrol为PageLayoutControlthis.SetBuddies(m_pageLayoutControl.Object);}catch(Exceptionex){thrownewException(string.Format(\"ControlsSynchronizer::ActivatePageLayout:\\r\\n{0}\ex.Message));}}//////给予一个地图,置换PageLayoutControl和MapControl的focusmap//////publicvoidReplaceMap(IMapnewMap){if(newMap==null)thrownewException(\"ControlsSynchronizer::ReplaceMap:\\r\\nNewmapforreplacementisnotinitialized!\");if(m_pageLayoutControl==null||m_mapControl==null)thrownewException(\"ControlsSynchronizer::ReplaceMap:\\r\\nEitherMapControlorPageLayoutControlarenotinitialized!\");//createanewinstanceofIMapscollectionwhichisneededbythePageLayout//创建一个PageLayout需要用到的,新的IMapscollection的实例IMapsmaps=newMaps();//addthenewmaptotheMapscollection//把新的地图加到Mapscollection里头去maps.Add(newMap);boolbIsMapActive=m_IsMapCtrlactive;//callreplacemaponthePageLayoutinordertoreplacethefocusmap//wemustcallActivatePageLayout,sinceitisthecontrolwecall'ReplaceMaps'//调用PageLayout的replacemap来置换focusmap//我们必须调用ActivatePageLayout,因为它是那个我们可以调用\"ReplaceMaps\"的Controlthis.ActivatePageLayout();m_pageLayoutControl.PageLayout.ReplaceMaps(maps);//assignthenewmaptotheMapControl//把新的地图赋给MapControlm_mapControl.Map=newMap;//resettheactivetools//重设activetoolsm_pageLayoutActiveTool=null;m_mapActiveTool=null;//makesurethatthelastactivecontrolisactivated//确认之前活动的control被激活if(bIsMapActive){this.ActivateMap();m_mapControl.ActiveView.Refresh();}else{this.ActivatePageLayout();m_pageLayoutControl.ActiveView.Refresh();}}//////bindtheMapControlandPageLayoutControltogetherbyassigninganewjointfocusmap///指定共同的Map来把MapControl和PageLayoutControl绑在一起////////////trueiftheMapControlsupposedtobeactivatedfirst,如果MapControl被首先激活,则为truepublicvoidBindControls(IMapControl3mapControl,IPageLayoutControl2pageLayoutControl,boolactivateMapFirst){if(mapControl==null||pageLayoutControl==null)thrownewException(\"ControlsSynchronizer::BindControls:\\r\\nEitherMapControlorPageLayoutControlarenotinitialized!\");m_mapControl=MapControl;m_pageLayoutControl=pageLayoutControl;this.BindControls(activateMapFirst);}//////bindtheMapControlandPageLayoutControltogetherbyassigninganewjointfocusmap///指定共同的Map来把MapControl和PageLayoutControl绑在一起//////trueiftheMapControlsupposedtobeactivatedfirst,如果MapControl被首先激活,则为truepublicvoidBindControls(boolactivateMapFirst){if(m_pageLayoutControl==null||m_mapControl==null)thrownewException(\"ControlsSynchronizer::BindControls:\\r\\nEitherMapControlorPageLayoutControlarenotinitialized!\");//createanewinstanceofIMap//创造IMap的一个实例IMapnewMap=newMapClass();newMap.Name=\"Map\";//createanewinstanceofIMapscollectionwhichisneededbythePageLayout//创造一个新的IMapscollection的实例,这是PageLayout所需要的IMapsmaps=newMaps();//addthenewMapinstancetotheMapscollection//把新的Map实例赋给Mapscollectionmaps.Add(newMap);//callreplacemaponthePageLayoutinordertoreplacethefocusmap//调用PageLayout的replacemap来置换focusmapm_pageLayoutControl.PageLayout.ReplaceMaps(maps);//assignthenewmaptotheMapControl//把新的map赋给MapControlm_mapControl.Map=newMap;//resettheactivetools//重设activetoolsm_pageLayoutActiveTool=null;m_mapActiveTool=null;//makesurethatthelastactivecontrolisactivated//确定最后活动的control被激活if(activateMapFirst)this.ActivateMap();elsethis.ActivatePageLayout();}//////bypassingtheapplication'stoolbarsandTOCtothesynchronizationclass,itsavesyouthe///managementofthebuddycontroleachtimetheactivecontrolchanges.Thismethodadstheframework///controltoanarray;oncetheactivecontrolchanges,theclassiteratesthroughthearrayand///callesSetBuddyControloneachofthestoredframeworkcontrol.//////publicvoidAddFrameworkControl(objectcontrol){if(control==null)thrownewException(\"ControlsSynchronizer::AddFrameworkControl:\\r\\nAddedcontrolisnotinitialized!\");m_frameworkControls.Add(control);}//////Removeaframeworkcontrolfromthemanagedlistofcontrols//////publicvoidRemoveFrameworkControl(objectcontrol){if(control==null)thrownewException(\"ControlsSynchronizer::RemoveFrameworkControl:\\r\\nControltoberemovedisnotinitialized!\");m_frameworkControls.Remove(control);}//////Removeaframeworkcontrolfromthemanagedlistofcontrolsbyspecifyingitsindexinthelist//////publicvoidRemoveFrameworkControlAt(intindex){if(m_frameworkControls.Count///whentheactivecontrolchanges,theclassiteratesthroughthearrayoftheframeworkcontrols///andcallesSetBuddyControloneachofthecontrols.//////theactivecontrolprivatevoidSetBuddies(objectbuddy){try{if(buddy==null)thrownewException(\"ControlsSynchronizer::SetBuddies:\\r\\nTargetBuddyControlisnotinitialized!\");foreach(objectobjinm_frameworkControls){if(objisIToolbarControl){((IToolbarControl)obj).SetBuddyControl(buddy);}elseif(objisITOCControl){((ITOCControl)obj).SetBuddyControl(buddy);}}}catch(Exceptionex){throwException(string.Format(\"ControlsSynchronizer::SetBuddies:\\r\\n{0}\ex.Message));}}#endregion}}new2、新建Maps类在同步类中,要用到Maps类,用于管理地图对象。与新建同步类ControlsSynchronizer类似,我们新建一Maps类,其所有代码如下所示:usingSystem;usingSystem.Collections;usingSystem.Collections.Generic;usingSystem.Text;usingSystem.Runtime.InteropServices;usingESRI.ArcGIS.Carto;namespace_sdnMap{[Guid(\"f27d8789-fbbc-4801-be78-0e3cd8fff9d5\")][ClassInterface(ClassInterfaceType.None)][ProgId(\"_sdnMap.Maps\")]publicclassMaps:IMaps,IDisposable{//classmember-usinginternallyanArrayListtomanagetheMapscollectionprivateArrayListm_array=null;#regionclassconstructorpublicMaps(){m_array=newArrayList();}#endregion#regionIDisposableMembers//////Disposethecollection///publicvoidDispose(){if(m_array!=null){m_array.Clear();m_array=null;}}#endregion#regionIMapsMembers//////RemovetheMapatthegivenindex//////publicvoidRemoveAt(intIndex){if(Index>m_array.Count||Index<0)thrownewException(\"Maps::RemoveAt:\\r\\nIndexisoutofrange!\");m_array.RemoveAt(Index);}//////ResettheMapsarray///publicvoidReset(){m_array.Clear();}//////GetthenumberofMapsinthecollection///publicintCount{get{returnm_array.Count;}}//////ReturntheMapatthegivenindex/////////publicIMapget_Item(intIndex){if(Index>m_array.Count||Index<0)thrownewException(\"Maps::get_Item:\\r\\nIndexisoutofrange!\");returnm_array[Index]asIMap;}//////RemovetheinstanceofthegivenMap//////publicvoidRemove(IMapMap){m_array.Remove(Map);}//////CreateanewMap,addittothecollectionandreturnittothecaller//////publicIMapCreate(){IMapnewMap=newMapClass();m_array.Add(newMap);returnnewMap;}//////AddthegivenMaptothecollection//////publicvoidAdd(IMapMap){if(Map==null)thrownewException(\"Maps::Add:\\r\\nNewMapismotinitialized!\");m_array.Add(Map);}#endregion}}3、新建打开文档类OpenNewMapDocument由于从工具栏自带的打开按钮打开地图文档的时候,不会自动进行两种视图之间的同步,所以我们要自己派生一个OpenNewMapDocument类,用于打开地图文档。右击项目名,选择“添加|类”,再选择ArcGIS类别中的BaseCommand模板,输入类名为“OpenNewMapDocument.cs”。首先添加引用:usingSystem.Windows.Forms;usingESRI.ArcGIS.Carto;再添加如下成员变量:privateControlsSynchronizerm_controlsSynchronizer=null;修改默认的构造函数如下所示://添加参数publicOpenNewMapDocument(ControlsSynchronizercontrolsSynchronizer){////TODO:Definevaluesforthepublicproperties////设定相关属性值base.m_category=\"Generic\";//localizabletextbase.m_caption=\"Open\";//localizabletextbase.m_message=\"ThisshouldworkinArcMap/MapControl/PageLayoutControl\";//localizabletextbase.m_toolTip=\"Open\";//localizabletextbase.m_name=\"Generic_Open\";//uniqueid,non-localizable(e.g.\"MyCategory_MyCommand\")//初始化m_controlsSynchronizerm_controlsSynchronizer=controlsSynchronizer;try{////TODO:changebitmapnameifnecessary//stringbitmapResourceName=GetType().Name+\".bmp\";base.m_bitmap=newBitmap(GetType(),bitmapResourceName);}catch(Exceptionex){System.Diagnostics.Trace.WriteLine(ex.Message,\"InvalidBitmap\");}}再在OnClick函数中添加如下代码:publicoverridevoidOnClick(){//TODO:AddOpenNewMapDocument.OnClickimplementationOpenFileDialogdlg=newOpenFileDialog();dlg.Filter=\"MapDocuments(*.mxd)|*.mxd\";dlg.Multiselect=false;dlg.Title=\"OpenMapDocument\";if(dlg.ShowDialog()==DialogResult.OK){stringdocName=dlg.FileName;IMapDocumentmapDoc=newMapDocumentClass();if(mapDoc.get_IsPresent(docName)&&!mapDoc.get_IsPasswordProtected(docName)){mapDoc.Open(docName,string.Empty);IMapmap=mapDoc.get_Map(0);m_controlsSynchronizer.ReplaceMap(map);mapDoc.Close();}}}在添加类时,模板会自动添加一个名为“OpenNewMapDocument.bmp”的图标,你可以自己修改或者替换为打开的文件夹的图标4、两种视图的同步
在3sdnMap.cs中添加成员变量,即同步类对象:privateControlsSynchronizerm_controlsSynchronizer=null;在Form1_Load函数中进行初始化工作://初始化controlssynchronizationcalssm_controlsSynchronizer=newControlsSynchronizer(m_mapControl,m_pageLayoutControl);//把MapControl和PageLayoutControl绑定起来(两个都指向同一个Map),然后设置MapControl为活动的Controlm_controlsSynchronizer.BindControls(true);//为了在切换MapControl和PageLayoutControl视图同步,要添加FrameworkControlm_controlsSynchronizer.AddFrameworkControl(axToolbarControl1.Object);m_controlsSynchronizer.AddFrameworkControl(this.axTOCControl1.Object);//添加打开命令按钮到工具条OpenNewMapDocumentopenMapDoc=OpenNewMapDocument(m_controlsSynchronizer);axToolbarControl1.AddItem(openMapDoc,-1,0,false,esriCommandStyles.esriCommandStyleIconOnly);new-1,因为我们自动派生了打开文档类,并自己将其添加到工具条,所以我们就不需要工具条原来的“打开”按钮了,可以ToolbarControl的属性中将其删除。下面,我们可完成上一讲遗留的功能了。//////新建地图命令/////////privatevoidNew_Click(objectsender,EventArgse){//询问是否保存当前地图DialogResultres=MessageBox.Show(\"是否保存当前地图?\\"提示\MessageBoxButtons.YesNo,MessageBoxIcon.Question);if(res==DialogResult.Yes){//如果要保存,调用另存为对话框ICommandcommand=newControlsSaveAsDocCommandClass();if(m_mapControl!=null)command.OnCreate(m_controlsSynchronizer.MapControl.Object);elsecommand.OnCreate(m_controlsSynchronizer.PageLayoutControl.Object);command.OnClick();}//创建新的地图实例IMapmap=newMapClass();map.Name=\"Map\";m_controlsSynchronizer.MapControl.DocumentFilename=string.Empty;//更新新建地图实例的共享地图文档m_controlsSynchronizer.ReplaceMap(map);}//////打开地图文档Mxd命令/////////privatevoidOpen_Click(objectsender,EventArgse){if(this.axMapControl1.LayerCount>0){DialogResultresult=MessageBox.Show(\"是否保存当前地图?\\"警告\MessageBoxButtons.YesNoCancel,MessageBoxIcon.Question);if(result==DialogResult.Cancel)return;if(result==DialogResult.Yes)this.Save_Click(null,null);}OpenNewMapDocumentopenMapDoc=newOpenNewMapDocument(m_controlsSynchronizer);openMapDoc.OnCreate(m_controlsSynchronizer.MapControl.Object);openMapDoc.OnClick();}在添加数据AddData时,我们也要进行地图共享,故在AddData_Click函数后面添加如下代码:IMappMap=this.axMapControl1.Map;this.m_controlsSynchronizer.ReplaceMap(pMap);在另存为地图文档时,有可能会丢失数据,因此我们需要提示用户以确认操作,故需修改SaveAs_Click函数,如下所示://////另存为地图文档命令/////////privatevoidSaveAs_Click(objectsender,EventArgse){//如果当前视图为MapControl时,提示用户另存为操作将丢失PageLayoutControl中的设置if(m_controlsSynchronizer.ActiveControlisIMapControl3){if(MessageBox.Show(\"另存为地图文档将丢失制版视图的设置\\r\\n您要继续吗?\\"提示\MessageBoxButtons.YesNo,MessageBoxIcon.Question)==DialogResult.No)return;}//调用另存为命令ICommandcommand=newControlsSaveAsDocCommandClass();command.OnCreate(m_controlsSynchronizer.ActiveControl);command.OnClick();}在切换视图时,我们要激活相关的视图,故在设计视图的属性面板中选择tabControl2控件,再选择事件按钮,找到“SelectedIndexChanged”事件双击添加之。其实现代码如下所示://////切换地图和制版视图/////////privatevoidtabControl2_SelectedIndexChanged(objectsender,EventArgse){if(this.tabControl2.SelectedIndex==0){//激活MapControlm_controlsSynchronizer.ActivateMap();}else{//激活PageLayoutControlm_controlsSynchronizer.ActivatePageLayout();}}5、编译运行按F5编译运行程序,至此我们完成了MapControl和PageLayoutControl两种视图的同步工作。第四讲状态栏信息的添加与实现
------------------------------------------------------------------在上一讲中,我们完成了MapControl和PageLayoutControl两种视图的同步工作,本讲我们将完成状态栏信息的添加与实现。应用程序的状态栏一般用来显示程序的当前状态,当前所使用的工具。GIS应用程序一般也在状态栏显示当前光标的坐标、比例尺等信息。学习完本讲内容,您将学会状态栏编程的基本方法,并且能够在我们的程序的状态栏中添加且显示以下信息:当前所用工具信息当前比例尺、当前坐标1、添加状态栏项目在设计视图中,点击窗体中的状态栏,在其属性面板中找到“Items”项,单击其右边的按钮,在下拉框中选择“StatusLabel”,单击“添加按钮”,依次添加四个StatusLabel,依次修改属性参数如下表所示:序号Name属性Text属性Spring属性说明1MessageLabel就绪False当前所用工具信息2BlankTrue占位3ScaleLabel比例尺False当前比例尺4CoordinateLabel当前坐标False当前坐标Tips:我们设计出的状态栏最终如下所示:就绪(Blank)比例尺当前坐标Spring属性表示可以按状态栏剩余空间自动伸缩。所以加入Blank项目,只是为了占个位子,以达到ScaleLabel和CoordinateLabel项目右对齐而MessageLabel项目左对齐的目的。2、显示当前所用工具信息首先添加axToolbarControl1的OnMouseMove事件(相信大家看了以上的教程,已经知道怎么添加事件了吧,还不知道的建议再温习下前几讲的内容)。在其事件响应函数代码如下:privatevoidaxToolbarControl1_OnMouseMove(objectsender,IToolbarControlEvents_OnMouseMoveEvente){//取得鼠标所在工具的索引号intindex=axToolbarControl1.HitTest(e.x,e.y,false);if(index!=-1){//取得鼠标所在工具的ToolbarItemIToolbarItemtoolbarItem=axToolbarControl1.GetItem(index);//设置状态栏信息MessageLabel.Text=toolbarItem.Command.Message;}else{MessageLabel.Text=\"就绪\";}}3、显示当前比例尺添加axMapControl1的OnMouseMove事件,其代码如下:PrivatevoidaxMapControl1_OnMouseMove(objectsender,IMapControlEvents2_OnMouseMoveEvente){//显示当前比例尺ScaleLabel.Text=\"比例尺1:\"+((long)this.axMapControl1.MapScale).ToString();}4、显示当前坐标显示当前坐标也是axMapControl1的OnMouseMove事件中响应,故只要在axMapControl1_OnMouseMove函数中添加如下代码即可://显示当前坐标CoordinateLabel.Text=\"当前坐标X=\"+e.mapX.ToString()+\"Y=\"+e.mapY.ToString()+\"\"+this.axMapControl1.MapUnits;按F5编译运行,可以看到,我们的程序已经能够正常工作了。但是细心的你可能会发现,当前坐标的后面的坐标单位为“esriUnknownUnits”或“esriMeters”之类,即系统在正常单位的前面加上了“esri”,追求完美的我们自然看得不舒服。那就进行简单的替换吧。首先定义个全局坐标单位变量sMapUnits,如下所示:privatestringsMapUnits;再Form1_Load函数中进行初始化:sMapUnits=\"Unknown\";添加axMapControl1控件的OnMapReplaced事件,在事件响应函数中进行坐标单位替换,代码如下:privatevoidaxMapControl1_OnMapReplaced(objectsender,IMapControlEvents2_OnMapReplacedEvente){esriUnitsmapUnits=axMapControl1.MapUnits;switch(mapUnits){caseesriUnits.esriCentimeters:sMapUnits=\"Centimeters\";break;caseesriUnits.esriDecimalDegrees:sMapUnits=\"DecimalDegrees\";break;caseesriUnits.esriDecimeters:sMapUnits=\"Decimeters\";break;caseesriUnits.esriFeet:sMapUnits=\"Feet\";break;caseesriUnits.esriInches:sMapUnits=\"Inches\";break;caseesriUnits.esriKilometers:sMapUnits=\"Kilometers\";break;caseesriUnits.esriMeters:sMapUnits=\"Meters\";break;caseesriUnits.esriMiles:sMapUnits=\"Miles\";break;caseesriUnits.esriMillimeters:sMapUnits=\"Millimeters\";break;caseesriUnits.esriNauticalMiles:sMapUnits=\"NauticalMiles\";break;caseesriUnits.esriPoints:sMapUnits=\"Points\";break;caseesriUnits.esriUnknownUnits:sMapUnits=\"Unknown\";break;caseesriUnits.esriYards:sMapUnits=\"Yards\";break;}}5、编译运行按F5编译运行程序。如果你足够细心的话,相信你已经成功了!在本讲中,介绍中StatusStrip控件的基本使用方法和AE中当所用工具信息、当前比例尺和当前坐标的显示调用方法。