ArcObjects SDK开发 007 自定义App-Command-Tool框架
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框架的更多相关文章
- 【转】快速开发移动医疗App!开源框架mHealthDroid
原文地址:http://www.csdn.net/article/2014-12-12/2823096-mHealhDroid mHealthDroid是一款开源的移动框架,主要用于帮助开发者快速而又 ...
- PIE SDK Command&&Tool工具命令一览表
PIE SDK Command&&Tool工具命令一览表 编号 模板 名称(中文) Command&Tool 程序集 备注 1 数据管理 加载栅格数据 PIE.Controls ...
- 如何基于App SDK快速地开发一个IoT App?
一.背景及大纲介绍 在如今物联网DCM(Device.Connect.Manage)的大框架下,有一个应用层来分析和处理数据,是必备技能.但是,对于一个公司来说,因为研发能力或者研发时间的原因,可能很 ...
- Android基础新手教程——1.2.1 使用Eclipse + ADT + SDK开发Android APP
Android基础新手教程--1.2.1 使用Eclipse + ADT + SDK开发Android APP 标签(空格分隔): Android基础新手教程 1.前言 这里我们有两条路能够选,直接使 ...
- iOS SDK开发汇总
以前也做过静态库的开发,不过都是一些简单的调用,最近在做项目的时候,发现其中还有很多问题,所以建个小项目简单记录遇到的问题以及正确的解决办法. 在项目中遇到的问题如下:xib文件获取不到, story ...
- MUI框架开发HTML5手机APP(一)--搭建第一个手机APP
前 言 JRedu 随着HTML5的不断发展,移动开发成为主流趋势!越来越多的公司开始选择使用HTML5开发手机APP,而随着手机硬件设备配置的不断提升,各种开发框架的不断优化,也使着H5开发的 ...
- MUI框架开发HTML5手机APP(一)--搭建第一个手机APP(转)
出处:http://www.cnblogs.com/jerehedu/p/7832808.html 前 言 JRedu 随着HTML5的不断发展,移动开发成为主流趋势!越来越多的公司开始选择使用H ...
- Android SDK开发与使用的那些事儿
前言 最近由于工作需要,将应用里的部分功能独立了出来,封装成 SDK 提供给合作伙伴使用.由于经验不足,网上也没多少写这方面内容的文章,遇到了不少的坑,决定记录下来. SDK 其实,刚说到要写SDK也 ...
- SDK 开发 .a .framework .bundle (xcode引用) 依赖sdk工程
一. 静态库.a 1.创建静态库工程 Cocoa Touch Static Libray ,然后可以创建一个测试视图 TestView 2.暴露头文件 -> Build Phases--> ...
- MUI框架开发HTML5手机APP
出处:http://www.cnblogs.com/jerehedu/p/7832808.html 前 言 JRedu 随着HTML5的不断发展,移动开发成为主流趋势!越来越多的公司开始选择使用H ...
随机推荐
- AVL Tree (1) - Definition, find and Rotation
1. 定义 (15-1) [AVL tree]: 一棵空二叉树是 AVL tree; 若 T 是一棵非空二叉树, 则 T 满足以下两个条件时, T 是一棵 AVL tree: T_LeftSubtre ...
- Portainer 基本功能介紹之升級映像檔並更新 Container
文档地址:https://www.asustor.com/zh-tw/online/College_topic?topic=145#dpt7
- 14. Fluentd输出插件:out_forward用法详解
out_forward是一个带缓存的输出插件,用于向其他节点转发日志事件,并支持转发节点之间的负载均衡和自动故障切换. out_forward支持至多一次和至少一次传输模式,默认为至多一次. out_ ...
- C#-2 C#程序
一 C#程序是一组类型声明 C#程序或DLL的源代码是一组一种或多种类型声明. 对于可执行程序,类型声明中必须有一个包含Main方法的类. 命名空间是一种把相关的类型声明分组并命名的方法.是类在程序集 ...
- Node.js(六)MongoDB
student.js var express = require('express'); var router = express.Router(); const _=require("lo ...
- vue3 vite2 封装 SVG 图标组件 - 基于 vite 创建 vue3 全家桶项目续篇
在<基于 vite 创建 vue3 全家桶>一文整合了 Element Plus,并将 Element Plus 中提供的图标进行全局注册,这样可以很方便的延续 Element UI 的风 ...
- Maximum Entropy Population-Based Training for Zero-Shot Human-AI Coordination
原文:https://www.cnblogs.com/Twobox/p/16791412.html 熵 熵:表述一个概率分布的不确定性.例如一个不倒翁和一个魔方抛到地上,看他们平稳后状态.很明显,魔方 ...
- Python模拟服务端
本机服务端 import socket # 获取到socket sk = socket.socket() # 获取到地址 ip 和 端口号 address = ('127.0.0.1', 8001) ...
- CentOS 7 安全基线检查
注意:操作时建议做好记录或备份 1.设置密码失效时间 | 身份鉴别 描述: 设置密码失效时间,强制定期修改密码,减少密码被泄漏和猜测风险,使用非密码登陆方式(如密钥对)请忽略此项. 加固建议: 使用非 ...
- Application保存作用域
Application保存作用域,作用范围:一次应用程序范围有效.Application属性范围值,只要设置一次,则所有的网页窗口都可以取得数据. ServletContext在服务器启动时创建,在服 ...