地图数据和版式数据联动
在ArcGIS二维中有数据视图和版式视图两种模式。两种视图可以切换显示。笔者在开始做Engine二次开发时,每次在数据和视图切换时,都拷贝数据,或者两个视图同事加载同一份数据。在切换时速度很慢,特别是数据量较大的时候。用户体验很差。
但当仔细看ArcEngine自带的例子时,里面的例子中就有一个数据视图和版式视图数据同步的例子,我们可以参考ArcEngine提供的例子来写我们自己的同步代码。
既然我们要做数据视图数据和版式视图数据的同步,我们首先要确定我们要同步那些信息,也就是公用哪些信息。两个视图模式公用的就是地图,也就是Layer的几何。不包含各各自的Element等信息。
ArcGIS的二维地图数据的配置文件是Mxd文件,Mxd文件中存储着版式和数据视图所包含的信息。而且我们也要清楚版式视图是可以包含多个地图的,除了多个地图之外还包括对应的智能图饰,如指北针、图例、比例尺等。但只能一个地图时当前激活的,而当前激活的地图就是数据视图要显示的地图。首先我们打开一个Mxd文件。代码如下:
public override void OnClick() { base.OnClick(); OpenFileDialog myOpenFileDialog = new OpenFileDialog(); myOpenFileDialog.Filter = "Mxd文档|*.mxd"; if (myOpenFileDialog.ShowDialog()!=true) { return; } string myMxdFilePath = myOpenFileDialog.FileName; MapDocumentClass myMapDocument = new MapDocumentClass(); myMapDocument.Open(myMxdFilePath,""); this._mapApplication.MapDocument = myMapDocument; this._mapApplication.MapControl.Refresh(esriViewDrawPhase.esriViewNone, null, null) ; } /// <summary> /// 地图文档对象 /// </summary> public IMapDocument MapDocument { get { return this._mapDocument; } set { this._mapDocument = value; if (this._mapDocument != null) { this._controlsSynchronizer.PageLayoutControl.PageLayout = this._mapDocument.PageLayout; List<IMap> myMapList = new List<IMap>(); for (int i = 0; i < this._mapDocument.MapCount; i++) { myMapList.Add(this._mapDocument.get_Map(i)); } this._controlsSynchronizer.ReaplaceMaps(myMapList); this.LayerTree.SetActiveView(this.ActiveView); } } } /// <summary> /// 替换当前显示的地图 /// </summary> /// <param name="pMap"></param> public void ReaplaceMaps(List<IMap> pMaps) { IMaps myMaps = new Maps(); foreach (IMap myMap in pMaps) { myMaps.Add(myMap); } MapActivePattern myActivePattern = this._activePattern; //只有当在PageLayoutControl被激活时,才能调用ReplaceMap方法。 this.ActivatePageLayout(); this.PageLayoutControl.PageLayout.ReplaceMaps(myMaps); //把map传递给mapControl this.MapControl.Map = this.PageLayoutControl.ActiveView.FocusMap; //保证一个处于激活状态 if (myActivePattern == MapActivePattern.Map) { this.ActivateMap(); this.MapControl.ActiveView.Refresh(); } else { this.ActivatePageLayout(); this.PageLayoutControl.ActiveView.Refresh(); } }
从上面的代码中,我们可以看到,打开一个地图文件,并保证两个视图地图数据同步需要三个步骤。
1.打开Mxd文件,得到MapDocument对象。
2.把读取到的地图对象中的版式对象PageLayout赋值给版式对象PageLayoutControl。
3.第三步就是同步地图数据了。先从MapDocument中把地图对象都取出来,组成一个地图列表对象。然后调用PageLayout的ReplaceMaps函数替换当前版式中的地图,然后把当前版式数据中激活的地图赋给数据视图MapControl的Map。
这样就保证了打开一个地图文件后,版式视图中激活的地图和数据视图展示的地图时一个对象,当我们对当前激活的地图有什么操作时,两者都可以响应。
但有一点,数据视图和版式视图当前只能有一个被激活。所以一般我们在项目中都把数据视图UI和版式视图UI放在两个Tab页面中,当切换时分别调用二者的激活函数,代码如下:
/// <summary> /// 激活map /// </summary> public void ActivateMap() { try { if (this.PageLayoutControl.ActiveView.IsActive()) { this.PageLayoutControl.ActiveView.Deactivate(); this.MapControl.ActiveView.Activate(this.MapControl.hWnd); } this._activePattern = MapActivePattern.Map; } catch (Exception ex) { throw new Exception("激活地图模式错误。" + ex.Message); } } /// <summary> /// 激活版式视图 /// </summary> public void ActivatePageLayout() { try { if (this.MapControl.ActiveView.IsActive()) { this.MapControl.ActiveView.Deactivate(); this.PageLayoutControl.ActiveView.Activate(this.PageLayoutControl.hWnd); } this._activePattern = MapActivePattern.PageLayout; } catch (Exception ex) { throw new Exception("激活版式模式错误。" + ex.Message); } }
下面的代码是继承IMaps实现的Maps的代码,这个在PageLayout对象Replace函数会用到。Engine的例子代码中就有该类的定义。
// Copyright 2006 ESRI // // All rights reserved under the copyright laws of the United States // and applicable international laws, treaties, and conventions. // // You may freely redistribute and use this sample code, with or // without modification, provided you include the original copyright // notice and use restrictions. // // See use restrictions at /arcgis/developerkit/userestrictions. using System; using ESRI.ArcGIS.Carto; using System.Collections; namespace BM.AE.Engine { /// <summary> /// Implementation of interface IMaps which is eventually a collection of Maps /// </summary> public class Maps : IMaps, IDisposable { //class member - using internally an ArrayList to manage the Maps collection private ArrayList m_array = null; #region class constructor public Maps() { m_array = new ArrayList(); } #endregion #region IDisposable Members /// <summary> /// Dispose the collection /// </summary> public void Dispose() { if (m_array != null) { m_array.Clear(); m_array = null; } } #endregion #region IMaps Members /// <summary> /// Remove the Map at the given index /// </summary> /// <param name="Index"></param> public void RemoveAt(int Index) { if (Index > m_array.Count || Index < 0) throw new Exception("Maps::RemoveAt:\r\nIndex is out of range!"); m_array.RemoveAt(Index); } /// <summary> /// Reset the Maps array /// </summary> public void Reset() { m_array.Clear(); } /// <summary> /// Get the number of Maps in the collection /// </summary> public int Count { get { return m_array.Count; } } /// <summary> /// Return the Map at the given index /// </summary> /// <param name="Index"></param> /// <returns></returns> public IMap get_Item(int Index) { if (Index > m_array.Count || Index < 0) throw new Exception("Maps::get_Item:\r\nIndex is out of range!"); return m_array[Index] as IMap; } /// <summary> /// Remove the instance of the given Map /// </summary> /// <param name="Map"></param> public void Remove(IMap Map) { m_array.Remove(Map); } /// <summary> /// Create a new Map, add it to the collection and return it to the caller /// </summary> /// <returns></returns> public IMap Create() { IMap newMap = new MapClass(); m_array.Add(newMap); return newMap; } /// <summary> /// Add the given Map to the collection /// </summary> /// <param name="Map"></param> public void Add(IMap Map) { if (Map == null) throw new Exception("Maps::Add:\r\nNew Map is mot initialized!"); m_array.Add(Map); } #endregion } }