昨天下午下了课,老板给打电话来说帮师兄研究一下如何实现这样如题的功能,具体情境是拿C#用ArcEngine做二次开发的时候,他要开四个MapControl控件,分别显示大气、土壤等等的信息,在每个地图容器里面拖拽放大的时候,其他容器里面的地图联动,鼠标也要在四个窗口里面同步显示。

好的,分成三步来做。以两个MapControl联动为例,做窗口上放了两个ArcGIS Engine MapControl控件,分别命名Map1和Map2。

先实现地图联动,每当地图位置、比例尺发生变化的时候,会触发MapControl的OnExtentUpdated事件,在Map1的响应函数里面写:

Map2.Extent = (IEnvelope)e.newEnvelope;

Map2同理,这样就好了。之前有试过Map2.Extent = Map1.Extent.Envelope的用法,但是联动的时候慢半拍。

接着实现同步操作状态,即鼠标进入MapControl控件的时候,保持一致的漫游、放大、缩小等功能。再放两个ArcGIS Engine ToolBarControl控件,分别命名Bar1和Bar2,添加好漫游、放大、缩小、全图四个按钮,并绑定对应的MapControl控件。

实现的原理是当按下一个ToolBar上按钮的时候,让别的ToolBar的该按钮也处于按下状态。在ToolBar的OnItemClick事件里面写,这里有一点要注意的是ToolBar里面的全图按钮算Command不算Tool(AO里面的强行不合理分类,Command是按下就弹起的按钮,Tool是按下后保持状态的。),在代码里面利用e.index把他排除在同步操作的状态以外。关键代码如下

Bar2.CurrentTool = Bar2.GetItem(e.index).Command as ITool;

Bar1同理,这样效果就出来了。前面几次操作在显示上略有点小问题,我归结到AE的Bug里面,多点几次基本就是无缝同步了,最后出成品的时候,隐藏到其他的ToolBar就好了。

鼠标这个功能比较麻烦,我试过用PicureBox装在个鼠标图片然后用位置控制的方法,但是PictrueBox本身不透明,效果不好,况且暂时也不需要也没必要实现同时弹出右键菜单,跟师兄沟通了一下决定改用在其他地图容器里面画一个大的十字丝来作为鼠标的定位显示,这个功能用NewLineFeedbackClass来实现。

首先做定义两个接口。

private IDisplayFeedback pDisplayFeedback1 = null;
private IScreenDisplay pScreenDisplay1 = null;

在窗体的构造函数里面添加初始化的代码。

pDisplayFeedback1 = new NewLineFeedbackClass();
pScreenDisplay1 = Map1.ActiveView.ScreenDisplay;
pDisplayFeedback1.Display = pScreenDisplay1;

在Map2的OnMouseMove事件里面写响应代码。

((INewLineFeedback)pDisplayFeedback1).Stop(); //结束之前的绘制,DisplayFeedback在执行Start()方法之后开始画,Stop()之后就清掉了。
//下面是画十字丝的,因为没有NewMultiLineFeedbackClass,这个十字丝只能一笔画成,所以就只能在显示区域外面多画了一点。

((INewLineFeedback)pDisplayFeedback1).Start(Map1.ActiveView.ScreenDisplay.
DisplayTransformation.ToMapPoint(0, e.y));
((INewLineFeedback)pDisplayFeedback1).AddPoint(Map1.ActiveView.ScreenDisplay.
DisplayTransformation.ToMapPoint(Map1.Width + 1, e.y));
((INewLineFeedback)pDisplayFeedback1).AddPoint(Map1.ActiveView.ScreenDisplay.
DisplayTransformation.ToMapPoint(Map1.Width + 1, -1));
((INewLineFeedback)pDisplayFeedback1).AddPoint(Map1.ActiveView.ScreenDisplay.
DisplayTransformation.ToMapPoint(e.x, -1));
((INewLineFeedback)pDisplayFeedback1).AddPoint(Map1.ActiveView.ScreenDisplay.
DisplayTransformation.ToMapPoint(e.x, Map1.Height));

这样就在当鼠标在Map2上时,Map1上会同步打一个十字丝。画出来的是默认宽度为1的黑色线,正好符合我的要求。也可以自定义线形,关键代码如下,加在初始化代码里面。

IRgbColor fcolor = new RgbColorClass();
fcolor.RGB = 0;
ISimpleLineSymbol pSymbol = (ISimpleLineSymbol)pDisplayFeedback1.Symbol;
pSymbol.Color = fcolor;
pSymbol.Width = 1;
pSymbol.Style = esriSimpleLineStyle.esriSLSSolid;
//放在这句之前 pDisplayFeedback1.Display = pScreenDisplay1;

这种做法比直接用MapControl的DrawShape或者用GraphicsContainer的AddElement方法好的地方在于,后两者重绘的时候需要刷新地图,这样整张地图都要重绘,效果巨差。

好的,完成了,说两句题外话,ArcGIS的ArcObject的库确实难用,正如上面看的NewFeedbackClass,另外还有一个FeedbackClass,而MapControlClass的接口则有IMapControl1、IMapControl2和IMapControl3,这哥仨大同小异,另外关于Symbol的用法也是各个不同,着实混乱。这还算好的了,我这里是ArcEngine9.3的产品,之前的8系列连命名空间都没有,Orz…本来下个版本是发布9.4的,现在据说改进很多,直接迈进10了,期待一下看看二次开发库有没有便民改进。