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. AVL Tree (1) - Definition, find and Rotation

    1. 定义 (15-1) [AVL tree]: 一棵空二叉树是 AVL tree; 若 T 是一棵非空二叉树, 则 T 满足以下两个条件时, T 是一棵 AVL tree: T_LeftSubtre ...

  2. Portainer 基本功能介紹之升級映像檔並更新 Container

    文档地址:https://www.asustor.com/zh-tw/online/College_topic?topic=145#dpt7

  3. 14. Fluentd输出插件:out_forward用法详解

    out_forward是一个带缓存的输出插件,用于向其他节点转发日志事件,并支持转发节点之间的负载均衡和自动故障切换. out_forward支持至多一次和至少一次传输模式,默认为至多一次. out_ ...

  4. C#-2 C#程序

    一 C#程序是一组类型声明 C#程序或DLL的源代码是一组一种或多种类型声明. 对于可执行程序,类型声明中必须有一个包含Main方法的类. 命名空间是一种把相关的类型声明分组并命名的方法.是类在程序集 ...

  5. Node.js(六)MongoDB

    student.js var express = require('express'); var router = express.Router(); const _=require("lo ...

  6. vue3 vite2 封装 SVG 图标组件 - 基于 vite 创建 vue3 全家桶项目续篇

    在<基于 vite 创建 vue3 全家桶>一文整合了 Element Plus,并将 Element Plus 中提供的图标进行全局注册,这样可以很方便的延续 Element UI 的风 ...

  7. Maximum Entropy Population-Based Training for Zero-Shot Human-AI Coordination

    原文:https://www.cnblogs.com/Twobox/p/16791412.html 熵 熵:表述一个概率分布的不确定性.例如一个不倒翁和一个魔方抛到地上,看他们平稳后状态.很明显,魔方 ...

  8. Python模拟服务端

    本机服务端 import socket # 获取到socket sk = socket.socket() # 获取到地址 ip 和 端口号 address = ('127.0.0.1', 8001) ...

  9. CentOS 7 安全基线检查

    注意:操作时建议做好记录或备份 1.设置密码失效时间 | 身份鉴别 描述: 设置密码失效时间,强制定期修改密码,减少密码被泄漏和猜测风险,使用非密码登陆方式(如密钥对)请忽略此项. 加固建议: 使用非 ...

  10. Application保存作用域

    Application保存作用域,作用范围:一次应用程序范围有效.Application属性范围值,只要设置一次,则所有的网页窗口都可以取得数据. ServletContext在服务器启动时创建,在服 ...