1、为什么再设计一套App-Command-Tool框架

为什么我们要自己再设计一套App-Command框架,而不直接使用AO API中的AxControl-ICommand这套已经非常好的框架呢?

1、宿主不同。我们系统的宿主对象除了可能要包含MapControl等地图显示控件外,还可能会包含我们业务系统特有的信息。例如当前登录用户,在一些Command中,可能需要根据当前登录用户的觉得来判断功能是否可用等。

2、AO中的ICmmand和ITool已经和UI绑定到一起了,我们并不想直接用AO中定义的ToolBar,这样会和我们的系统风格不一致。还有ICommand中定义的Bitmap以及ITool中定义的Cursor都是int类型,这并不符合我们的使用习惯。如果我们使用传统菜单+工具条的模式,使用的都是16*16的图标,如果我们采用Office的Ribbon风格,那么可能会出现很多32*32的图标,这个如何兼容?

3、我们想让我们定义的工具适应更多的UI。例如定义的Command和Tool和绑定到WPF自带的按钮上,也可以绑定到第三方库例如DEV定义的按钮上。这就需要多UI进行抽象。

2、基于ArcObjects SDK设计的App-Command-Tool框架

我们参考借鉴AO,定义我们自己的App-Command框架如下,定义的时候,主要是解决上述的几个问题。我们定义的框架如下。

IApplication、ICommand、IMapTool以及ICmmandUI四个接口以及MapApplication类是整个框架的核心部分。除了上图中体现出来的内容外,框架还包含Command、MapTool以及 ViewSynchronizer等基类和辅助类。

3、IMapTool接口与宿主是怎么交互的

我们在MapApplication类中封装了宿主与ITool接口的交互。封装代码如下。

this.AxMapControl.OnMouseDown += (x, y) =>
{
this._CrruteTool.OnMouseDown(y.button, y.shift, y.x, y.y);
};
this.AxMapControl.OnMouseMove += (x, y) =>
{
this._CrruteTool.OnMouseMove(y.button, y.shift, y.x, y.y);
};
this.AxMapControl.OnMouseUp += (x, y) =>
{
this._CrruteTool.OnMouseUp(y.button, y.shift, y.x, y.y);
};
this.AxMapControl.OnDoubleClick += (x, y) =>
{
this._CrruteTool.OnDblClick();
};
this.AxMapControl.OnKeyDown += (x, y) =>
{
this._CrruteTool.OnKeyDown(y.keyCode, y.shift);
};
this.AxMapControl.OnKeyUp += (x, y) =>
{
this._CrruteTool.OnKeyUp(y.keyCode, y.shift);
};

对于宿主来说,并不关心当前使用的到底是哪个Tool,只管在触发动作的时候,去调用当前工具对应的函数即可。

工具切换的代码如下。

public IMapTool CrruteTool
{
get
{
return this._CrruteTool;
}
set
{
this._CrruteTool.OnDeActivate();
this._CrruteTool.IsChecked = false;
this._CrruteTool = value;
if (this._CrruteTool == null)
{
this._CrruteTool = new NullMapTool(this);
}
this._CrruteTool.OnActive();
this._CrruteTool.IsChecked = true;
}
}

切换工具的时候,首先要调用工具的OnDeActivate函数,把当前工具的使用痕迹清理掉。设置新工具,调用新工具的OnActive函数,激活该工具。

4、封装ArcObjects已有的Command和Tool

ArcObjects SDK本身为我们提供了很多已经实现好的工具和命令,如何把这些命令融合到我们自己的框架中呢?

Command以全图命令为例。

public class MapFullExtentCommand : MapCommand
{
private ESRI.ArcGIS.SystemUI.ICommand _EsriCommand = null;
public MapFullExtentCommand(MapApplication pMapApplication)
: base(pMapApplication)
{
this._EsriCommand = new ControlsMapFullExtentCommandClass();
this._EsriCommand.OnCreate(pMapApplication.MapControl);
this.SetIcon(CommandIconSize.IconSize16, "MapTools/Res/MapFullExtent16.png");
}
public override void OnClick()
{
base.OnClick();
this._EsriCommand.OnClick();
}
}

我们初始化了一个ArcObjects SDK定义的ControlsMapFullExtentCommandClass类,并与我们定义的MapApplication中的MapControl绑定。实现命令点击函数的时候,直接调用AO中定义的全图类的OnClick函数即可。

Tool以地图放大工具为例。

public class MapZoomInTool : MapTool
{
private readonly ESRI.ArcGIS.SystemUI.ITool _EsriTool = null;
public MapZoomInTool(MapApplication pMapApplication)
: base(pMapApplication)
{
this._EsriTool = new ControlsMapZoomInToolClass();
this.SetIcon(CommandIconSize.IconSize16, "MapTools/Res/MapZoomIn16.png");
}
public override void OnActive()
{
base.OnActive();
(this._EsriTool as ESRI.ArcGIS.SystemUI.ICommand).OnCreate(this.MapApplication.ActiveControl);
this.MapApplication.ActiveControl.CurrentTool = this._EsriTool;
}
public override void OnDeActivate()
{
base.OnDeActivate();
this.MapApplication.ActiveControl.CurrentTool = null;
}
public override void OnMouseDown(int button, int shift, int x, int y)
{
if (button == 4)
{
this.MapApplication.AxControlPan();
}
base.OnMouseDown(button, shift, x, y);
}
}

我们初始化了一个ArcObjects SDK定义的ControlsMapZoomInToolClass类,在激活该工具的时候和当前激活的Control绑定,如果是数据模式,会绑定MapControl,如果是布局模式,会绑定PageLayoutControl。并把定义的工具赋值给当前激活的Control的CurrentTool属性。失活的时候,把当前激活Control的CurrentTool设置为null。

这样我们就可以充分利用ArcObjects SDK已经实现的各类命令和工具了。

5、扩展ArcObjects SDK已有的Tool

如果我们想在已有工具的基础上做些其他事情呢?例如在出图的时候,选择一个元素,在右侧显示该元素的属性面板。正常思路下,我们会点击PageLayoutControl,根据坐标去判断是否选中的Element,如果选中了,则把Element显示为选中状态,并获取该对象,在右侧显示其属性面板。

那是不是有更简单的方法?ArcObjects SDK是有选择Element工具的,类名称为ControlsSelectToolClass,使用该工具可以使用鼠标进行点选、框选、删除、移动以及调整元素大小等操作,这些功能如何我们自己去写代码实现,将有非常大的工作量。如果能用这个工具,那就再好不过了。但我们需要解决两个问题。

1、ArcObjects SDK中定义的选择类,在选择元素后,我们要捕捉到该动作,并获取选中的元素,显示元素的面板;

2、ArcObjects SDK中定义的选择类,选择元素后,按下Delete键,会删除元素,这个逻辑我们需要控制,禁止删除MapFarme,并且删除其他元素的时候,要弹出提示是否确定删除对话框,确定后,再删除。

代码定义如下。

public class SelectTool : MapTool
{
private readonly LayoutDesignApplication _LayoutDesignAplication = null;
private readonly ControlsSelectToolClass _EsriTool = null;
private double _MouseDownPageX = 0;
private double _MouseDownPageY = 0;
public SelectTool(LayoutDesignApplication pLayoutDesignAplication)
: base(pLayoutDesignAplication)
{
this._LayoutDesignAplication = pLayoutDesignAplication;
this._EsriTool = new ControlsSelectToolClass();
this.SetIcon(CommandIconSize.IconSize16, "Designs/Res/Select16.png");
}
public override void OnActive()
{
base.OnActive();
this._EsriTool.OnCreate(this._LayoutDesignAplication.ActiveControl);
}
public override void OnKeyDown(int keyCode, int shift)
{
if (keyCode == (int)ConsoleKey.Delete)
{
IGraphicsContainerSelect myGraphicsContainerSelect
= this._LayoutDesignAplication.PageLayoutControl.GraphicsContainer as IGraphicsContainerSelect;
IElement mySelectElement = myGraphicsContainerSelect.DominantElement;
if (mySelectElement is IMapFrame == true)
{
return;
}
MessageBoxResult myMessageBoxResult = MessageBox.Show("Is it determined to remove?", "Info", MessageBoxButton.YesNo);
if (myMessageBoxResult != MessageBoxResult.Yes)
{
return;
}
base.OnKeyDown(keyCode, shift);
this._EsriTool.OnKeyDown(keyCode, shift);
}
else
{
base.OnKeyDown(keyCode, shift);
}
}
public override void OnMouseDown(int button, int shift, int x, int y)
{
if (button == 4)
{
this._LayoutDesignAplication.AxControlPan();
}
else
{
this._EsriTool.OnMouseDown(button, shift, x, y);
}
}
public override void OnMouseUp(int button, int shift, int x, int y)
{
base.OnMouseUp(button, shift, x, y);
this._EsriTool.OnMouseUp(button, shift, x, y); IPageLayoutControl myPageLayoutControl = this._LayoutDesignAplication.PageLayoutControl;
IPageLayout myPageLayout = myPageLayoutControl.PageLayout;
IGraphicsContainerSelect myGraphicsContainerSelect = myPageLayout as IGraphicsContainerSelect;
IElement myDominantElement = myGraphicsContainerSelect.DominantElement;
}
public override void OnMouseMove(int button, int shift, int x, int y)
{
base.OnMouseMove(button, shift, x, y);
this._EsriTool.OnMouseMove(button, shift, x, y);
}
}

6、完全自定义Command和Tool

完全自定义的Command就比较简单些了。例如我们定义移除当前选中的图层命令,定义如下。

public class LayerRemoveCommand : MapCommand
{
public LayerRemoveCommand(MapApplication pMapApplication)
: base(pMapApplication)
{
this.Caption = "Remove";
this.IsEnabled = false; this.MapApplication.OnActiveStateChanged += (x, y) =>
{
this.UpdateIsEnableState();
};
this.MapApplication.OnSelectTocObjectChanged += (x, y) =>
{
this.UpdateIsEnableState();
};
}
public override void OnClick()
{
base.OnClick();
ILayer myLayer = this.MapApplication.SelectTocObject as ILayer;
if (myLayer == null)
{
MessageBox.Show("Please Select A Layer。");
return;
}
if (MessageBox.Show("Are You Sure Remove The Layer?", "Info", MessageBoxButton.YesNo) == MessageBoxResult.Yes)
{
this.MapApplication.MapControl.ActiveView.FocusMap.DeleteLayer(myLayer);
this.MapApplication.TOCControl.Update();
}
}
private void UpdateIsEnableState()
{
if (this.MapApplication.ActivePattern == MapActivePattern.None)
{
this.IsEnabled = false;
return;
}
ILayer myLayer = this.MapApplication.SelectTocObject as ILayer;
this.IsEnabled = (myLayer != null);
}
}

该定义就可以添加到图层的右键菜单上,用来移除当前选中的图层。我们不光可以通过调用宿主的属性和事件来控制自己是否可用,还可以加入很多逻辑判断。例如如果没有选择任何图层,则提示用户请选择一个图层。在移除的时候,可以提示用户是否确定移除等。

自定义的工具如下所示。

public class PointTextTool : MapTool
{
private readonly LayoutDesignApplication _LayoutDesignApplication = null;
public PointTextTool(LayoutDesignApplication pLayoutDesignApplication)
: base(pLayoutDesignApplication)
{
this._LayoutDesignApplication = pLayoutDesignApplication;
this.SetIcon(CommandIconSize.IconSize16, "Designs/Res/Text16.png");
}
public override void OnMouseDown(int button, int shift, int x, int y)
{
base.OnMouseDown(button, shift, x, y);
IPoint myPagePoint = this._LayoutDesignApplication.PageLayoutControl.ToPagePoint(x, y);
IPageLayout myPageLayout = this._LayoutDesignApplication.PageLayoutControl.PageLayout;
IGraphicsContainerSelect myGraphicsContainerSelect = myPageLayout as IGraphicsContainerSelect;
myGraphicsContainerSelect.UnselectAllElements();
PointTextItem myPointTextItem = new PointTextItem();
myPointTextItem.X = myPagePoint.X;
myPointTextItem.Y = myPagePoint.Y;
this._LayoutDesignApplication.LayoutDesign.PageLayoutItemList.Add(myPointTextItem);
myPointTextItem.Apply(this._LayoutDesignApplication);
(myPageLayout as IActiveView).PartialRefresh(esriViewDrawPhase.esriViewGraphics, null, null);
this._LayoutDesignApplication.CrruteTool = this._LayoutDesignApplication.SelectTool;
}
}

实现工具的OnMouseDown函数,获取当前点击位置的坐标,实例化一个文本元素,添加到该位置。然后马上把工具切换到系统定义好的SelectTool上,这样会避免不小心点击两次,添加了两个文本元素,提高用户体验。

切换到SelectTool后,再次点击刚添加文本元素就可以选中该元素,这样右侧该元素的信息面板就展示出来了,完成了一个非常自然的操作过程。

通过命令和工具通过自己控制自己的状态、行为等,可以做到很细微的逻辑控制,并且这些操作会很好的封装在自己的代码中。这样系统功能可以通过实现各类Command和Tool不断扩展系统功能,但又不会影响系统的整体结构。

ArcObjects SDK开发 007 自定义App-Command-Tool框架的更多相关文章

  1. 【转】快速开发移动医疗App!开源框架mHealthDroid

    原文地址:http://www.csdn.net/article/2014-12-12/2823096-mHealhDroid mHealthDroid是一款开源的移动框架,主要用于帮助开发者快速而又 ...

  2. PIE SDK Command&&Tool工具命令一览表

    PIE SDK Command&&Tool工具命令一览表 编号 模板 名称(中文) Command&Tool 程序集 备注 1 数据管理 加载栅格数据 PIE.Controls ...

  3. 如何基于App SDK快速地开发一个IoT App?

    一.背景及大纲介绍 在如今物联网DCM(Device.Connect.Manage)的大框架下,有一个应用层来分析和处理数据,是必备技能.但是,对于一个公司来说,因为研发能力或者研发时间的原因,可能很 ...

  4. Android基础新手教程——1.2.1 使用Eclipse + ADT + SDK开发Android APP

    Android基础新手教程--1.2.1 使用Eclipse + ADT + SDK开发Android APP 标签(空格分隔): Android基础新手教程 1.前言 这里我们有两条路能够选,直接使 ...

  5. iOS SDK开发汇总

    以前也做过静态库的开发,不过都是一些简单的调用,最近在做项目的时候,发现其中还有很多问题,所以建个小项目简单记录遇到的问题以及正确的解决办法. 在项目中遇到的问题如下:xib文件获取不到, story ...

  6. MUI框架开发HTML5手机APP(一)--搭建第一个手机APP

      前  言 JRedu 随着HTML5的不断发展,移动开发成为主流趋势!越来越多的公司开始选择使用HTML5开发手机APP,而随着手机硬件设备配置的不断提升,各种开发框架的不断优化,也使着H5开发的 ...

  7. MUI框架开发HTML5手机APP(一)--搭建第一个手机APP(转)

    出处:http://www.cnblogs.com/jerehedu/p/7832808.html  前  言 JRedu 随着HTML5的不断发展,移动开发成为主流趋势!越来越多的公司开始选择使用H ...

  8. Android SDK开发与使用的那些事儿

    前言 最近由于工作需要,将应用里的部分功能独立了出来,封装成 SDK 提供给合作伙伴使用.由于经验不足,网上也没多少写这方面内容的文章,遇到了不少的坑,决定记录下来. SDK 其实,刚说到要写SDK也 ...

  9. SDK 开发 .a .framework .bundle (xcode引用) 依赖sdk工程

    一. 静态库.a 1.创建静态库工程 Cocoa Touch Static Libray  ,然后可以创建一个测试视图 TestView 2.暴露头文件 -> Build Phases--> ...

  10. MUI框架开发HTML5手机APP

    出处:http://www.cnblogs.com/jerehedu/p/7832808.html  前  言 JRedu 随着HTML5的不断发展,移动开发成为主流趋势!越来越多的公司开始选择使用H ...

随机推荐

  1. 利用rpmbuild 打包可执行文件和链接库生成rpm 包

    Background: Background: 遇到一个打包可执行程序和链接库生成rpm 包的需求,查遍了很多网站的资料,现在整理下解决方案. 前期参考的是这篇帖子: https://codeante ...

  2. ProxySQL(3):Admin管理接口

    文章转载自:https://www.cnblogs.com/f-ck-need-u/p/9281199.html ProxySQL的Admin管理接口 当ProxySQL启动后,将监听两个端口: (1 ...

  3. kubeoperator 使用外部mysql

    1.导出 kubeoperator 的数据库 sql 文件,然后导入到外部mysql 2.正常关闭 kubeoperator 3.关闭 kubeoperator 不会影响已经部署的 k8s 集群 4. ...

  4. Elasticsearch : alias数据类型

    就像其他的很多语言一样,我们可以给已有的变量取一个别名(alias).即便是对高级语言一样,比如我们定义不同的指针变量,指向同一个内存空间.这个有些类似别名的概念. 在Elasticsearch中,我 ...

  5. ES小知识点

    elasticsearch.yml配置文件 network.host: _site_ # network.host设置为"_site_",表明它绑定到我们的本地电脑的IP地址 di ...

  6. 企业微信报警中关于markdown的用法

    官方文档地址:https://open.work.weixin.qq.com/api/doc/90002/90151/90853#markdown消息 请求方式:POST(HTTPS) 请求地址: h ...

  7. Elasticsearch:Split index API - 把一个大的索引分拆成更多分片

    文章转载自:https://blog.csdn.net/UbuntuTouch/article/details/108960950

  8. 如何生成均匀随机数 C++

    #include <iostream> #include <fstream> #include <cstdlib> #include <ctime> u ...

  9. JSP页面实现验证码校验

    目录 验证码校验分析 生成验证码 测试验证码 校验验证码 测试验证码校验 添加验证码刷新 在网页页面的使用中为防止"非人类"的大量操作和防止一些的信息冗余,增加验证码校验是许多网站 ...

  10. 常用排序算法(C语言)

    1.冒泡排序 void BubbleSort(int a[],int len) {int tmp; for (int i=0; i<n-1; i++) { int flag = FALSE; f ...