ArcEngine开发之Command控件

上一篇文章提到了如何灵活地使用ArcEngine内置的Command控件,如果这些控件已经满足你的功能需求,就尽量使用它们,因为ArcEngine的开发一旦开始自己coding,往往遇到大量的代码量和性能问题。而ArcEgnine的SDK提供了很多减轻开发人员负担的模板、代码段、示例等等。下面我们对Command控件进行的扩展就利用了SDK提供的模板。

由于Command对象封装了太多的信息,往往使用很方法,进行扩展却不易。比如内置的Identify控件,你只需要在工具栏中添加它就可以了:
axToolbarControl1.AddItem("esriControls.ControlsMapIdentifyTool");

它的功能极其完善,到与ArcDesktop提供的Identify工具几乎一样。但是我们想自定义弹出的对话框,省掉一些功能,把界面中文化,对不起,No door~。于是我们只好硬着头皮自己开发Identify功能,还有诸如EditingToolbar,MeasureTool这些控件都存在这样的烦恼。开发它们的难度是很大的,但是利用SDK提供的模板还是可以扫清基本思路上的障碍,并且开发出来以后可以在不同项目中重用。

思路是这样,要使自己开发的Command控件能够像内置控件那样方便使用,你需要将你的Command类实现ICommand接口,如果进一步,这个Command会持久地与MapControl交互,你还需要实现ITool接口。比这高明一点的方法是继承自BaseCommand或BaseTool抽象类。这样需要手工编写实现的代码更少。最高明的
方法则是使用SDK提供的模板创建这样的Command控件。
在项目中选择添加新项,选择ArcGIS类别下的模板Base Command或Base Tool,如图:


点击确定之后会弹出一个对话框,让你选择要创建的Command适用的ArcGIS组件,选择MapControl or PageLayoutControl Command则可使Command用在普通的平面地图控件上。


最终生成的代码文件中,创建了一个类,继承自BaseCommand,提供了可供COM识别的一系列标签,实现了BaseCommand的必要方法OnCreate和OnClick。其中重要的部分说明如下:
  • [Guid("4f2b2bfe-3f62-43ca-9ed3-36c8d233280c")]:使这个全局ID使此组件可以注册在你的电脑上;
  • [ProgId("GTLControls.Command2")]:提供了这个COM组件的ProgId,这成为调用AddItem时传入的参数。
  • base.m_name:这个名称成为你在查找Command时进行比较的字符串名称。
  • OnCreate:方法中传递的参数hook成为绑定到这个Command控件的交互对象,被保存在一个IHookHelper成员变量中,通过该成员的Hook属性即可获得对交互对象(往往是MapControl的弱引用)。
同时被创建的还有一个bmp图片,是被添加到工具栏时显示的按钮图片,默认是一个傻傻的兔子。

你需要实现的逻辑往往被放置在OnClick方法中。通过观察其中的TODO注释你可以找到在哪里开始编写自己的代码。

在此没有必要对一个具体的Command对象如何实现进行更详细的说明,但要使一个自己开发的Command正常工作还是需要注意以下几点:

1、这个Command必须位于一个Library项目中,当在一个exe项目中创建这样一个Command对象会无法使用,或者是我还没找到正确的办法。
2、这个Library项目的属性需要勾选为COM Interop注册选项,在生成选项卡下。
3、不要忘记在使用它的项目中添加对这个Library项目的引用。
在ArcEngine类库中有大量的Command控件用来与地图控件进行操作和交互。比如有一系列的地图浏览控件、地图查询控件、图斑选取控件、编辑控件来与MapControl和PageLayoutControl进行交互。这些控件被包含在ESRI.ArcGIS.Controls.dll类库中,位于ESRI.ArcGIS.Controls命名空间下。

这些内置的Command控件可以单独实例化来使用,也可以被安置在一个AxToolbarControl工具栏控件中,继而被存放在一个CommandPool池中以备调用。下面对这两种方式分别加以说明:

第一种使用方式是实例化一个Command对象并显式地运行它:
 
ICommand command  =   new  ControlsOpenDocCommandClass();
command.OnCreate(m_mapControl.Object);
command.OnClick();

其中ControlsOpenDocCommandClass就是一个Command控件,通过调用它的OnCreate方法传递给它需要交互的MapControl,然后调用它的OnClick方法就可以运行。上面的例子会激活一个打开地图文档的对话框。

另一种方法是更加常用的方法。

由于每个Command对象都是一个COM组件,所以ESRI.ArcGIS.Controls下的各个类只是对底层的COM对象的一种封装。由于是COM对象,所以每一个Command对象都有自己的CLSID和ProgID,并在安装Engine Runtime的时候被注册了,你可以在注册表的HKEY_LOCAL_MACHINE/SOFTWARE/Classes/CLSID/找到这些Command的注册信息,如果要查找所有Command的信息,请访问 Built-in commands。由于是COM组件,在实例化它的过程中,.net需要实例化一个Runtime Callable Wrapper(RCW)对象,来代理对COM组件的调用。

由于大部分对地图控件的操作都直接或间接的来自于工具栏,比如点击工具栏上面的放大、缩小、全图按钮。那么实际上我们的绝大部分Command对象都可以被寄放到这个工具栏之中。方法非常简单:

axToolbarControl1.AddItem( " esriControls.ControlsMapZoomInTool " );

此处使用的esriControls.ControlsMapZoomInTool就是ControlsMapZoomInToolClass类所指向的COM组件的ProgID,需要所有这些Command的信息时,你应该到 Built-in commands这个页面下查找,或在你的Engine帮助文档中ms-help://ESRI.EDNv9.2/NET_Engine/b9a335a2-f653-44a1-8961-89051f2e958f.htm的位置。

通过AddItem添加到工具栏中的Command控件使用非常方便,添加了它以后你就再不用操心进一步的处理它与地图控件的交互了。Engine内部是怎么做到这一点的呢,记得我们在第一种方法中实例化一个Command控件时调用的OnCreate方法吗,当时我们传递给它一个MapControl来告诉它需要控制哪一个地图控件。而通过axToolbarControl1.AddItem添加的控件,由于ToolbarControl将这个控件放到自己的控件池时,就已经调用了它的OnCreate方法,并传递给了它自己的BuddyControl作为控制对象,于是,一切都变得简单了。

在实际开发项目中,我们往往不想暴露出ArcEngine内置的ToolBar控件,而是给用户展示我们自己开发的工具栏控件。另一方面,我们也想使用这种简单的添加控件机制。如果做到呢,方法就是在设计阶段,仍然拖放一个ToolbarControl到窗体上,但是在属性中将它的Visible设置为false,这样就不会显示出来了,而我们可以往这个工具栏中添加大量的Command控件。

这个隐藏的工具栏如何使用呢,请看下面这个函数:

public   void  SetCurrentMapTool( string  commandName)
{
  
// 先重置地图当前工具
  m_Map.CurrentTool  =   null ;
  
for  ( int  i  =   0 ; i  <  m_toolbarControl.CommandPool.Count; i ++ )
  {
    _command 
=  m_toolbarControl.CommandPool.get_Command(i);

    
if  (_command.Name  ==  commandName)
    {
      m_Map.CurrentTool 
=  _command  as  ITool;
      _command.OnClick();
      
break ;
    }
  }
}

前面提到ToolbarControl内部维护一个Command池,在这个池中存放了所有已添加的Command对象,获取其中的Command可以通过CommandPool的get_Command方法,通过比较Command的名称,可以得到想要的Command对象。这个名称可不同于ProgID,它是另一个用来标识Command控件的字符串,具体的请在 Built-in commands上查找(第一列),比如我们之前添加的ZoomInTool控件,它的ProgID是esriControls.ControlsMapZoomInTool,它的Name是ControlToolsMapNavigation_ZoomIn。
请注意这一行:

m_Map.CurrentTool  =  _command  as  ITool;

当Command对象处理的不只是打开地图,显示全图这类没有与地图交互的功能时,简单的使用OnClick即可,但是当需要的是拖动鼠标控制缩放,空间查询这类必须与地图进行交互的动作时,就必须设置MapControl的CurrentTool属性。

通过上面介绍的方法,就可以不仅利用Toolbar控件管理Command功能,又可以提供丰富的UI,保证表现层的灵活性。

内置的一系列Command控件可以满足我们很多的功能需求,但是仍有稍显不足的时候,比如Identify控件的全英文界面,会使有些用户抓狂。这时,我们可以通过自己开发Command控件来扩展出我们需要的功能,并可以像内置Command一样的使用。以后的文章会给大家来介绍。
首先给大家介绍一个网站 ColorBrewer,我们在制作专题图时,都要根据地图的内容来选择合适的颜色主题,比如,表现降水的多采用蓝色,表现高温多采用红色,表现沙尘多采用黄色。然后根据等级来分出不同的色阶。 ColorBrewer为我们提供了一个很好的参考,你可以选择等级的数量,此网站默认的是不能大于10个级别,然后选择一个主色调。接下来它会为你生成每个等级的颜色值,并可以转换成各种格式。


其实这个功能我们可以通过AO中的IAlgorithmicColorRamp来实现,并且不会有等级多少的限制,用法就是传递给它起始和结束的颜色,然后通过CreateRamp就可以生成其间渐变的各个颜色值。

示例的代码 在此下载

在编写这段代码的过程中我遇到了这样的问题,AO生成的IColor对象,不能直接转换成.net提供的Color对象。这样就需要我们手工来完成提取IColor的R,G,B颜色,然后初始化一个Color对象。其中提取R,G,B的值用到了按位运算。

int  R  =  color.RGB  &   0xff ;
int  G  =  (color.RGB  &   0xff00 /   0x100 ;
int  B  =  (color.RGB  &   0xff0000 /   0x10000 ;


试验的结果转换的Color对象颜色是正确的,而直接通过FromArgb(color.RGB)得到的是错误的,尽管FromArgb接受一个32bit的颜色参数值。

希望能帮大家少走弯路。

为什么题目要加个“成功地”,因为ITopologicalOperator的方法是bug非常多的,在90%的情况下能够按照你预想的工作,但是在10%的情况下,既不报错也不工作。这个merge的操作,成功地折磨了我两天。终于找到了解决的方法。

      ICursor pCursor;
      featureSelect.SelectionSet.Search(
null false out  pCursor);

      IFeatureCursor pFeatureCursor 
=  pCursor  as  IFeatureCursor;
      IFeature pFeatureFirst 
=  pFeatureCursor.NextFeature();

      
//  开始一个编辑操作,以能够撤销
      m_EditWorkspace.StartEditOperation();

      IGeometry pGeometryFirst 
=  pFeatureFirst.Shape;
      ITopologicalOperator2 topo_oper 
=  (ITopologicalOperator2)pGeometryFirst;

      
// ITopologicalOperator的操作是bug很多的,先强制的检查下面三个步骤,再进行操作
      
// 成功的可能性大一些
      topo_oper.IsKnownSimple_2  =   false ;
      topo_oper.Simplify();
      pGeometryFirst.SnapToSpatialReference();

      
// 这是准备合并的图斑使用的
      ITopologicalOperator2 topo_oper2;
      IGeometry pGeometryNext;
      IFeature pFeatureNext 
=  pFeatureCursor.NextFeature();

      
while  (pFeatureNext  !=   null )
      
{
        pGeometryNext 
= pFeatureNext.ShapeCopy;

        
//与上面的同理
        topo_oper2 = pGeometryNext as ITopologicalOperator2;
        topo_oper2.IsKnownSimple_2 
= false;
        topo_oper2.Simplify();
        pGeometryNext.SnapToSpatialReference();

        
//这才是合并图斑的关键
        pGeometryFirst = topo_oper.Union(pGeometryNext);
        pFeatureNext.Delete();

        pFeatureNext 
= pFeatureCursor.NextFeature();
      }

      topo_oper.IsKnownSimple_2 
=   false ;
      topo_oper.Simplify();
      pFeatureFirst.Shape 
=  pGeometryFirst;
      pFeatureFirst.Store();
      m_EditWorkspace.StopEditOperation();
  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值