这个例子将引导您创建第一个简单的地图显示程序,并添加基本的缩放和漫游功能。如果您之前没有接触过ArcGIS Engine的开发,那么这个例子是您迈入ArcGIS Engine二次开发大门的极好例子,如果您之前没有接触过C#.NET,也无需担心,这个例子将从零开始引导您一步一步完成任务。
1创建一个新的工程
首先打开Microsoft Visual Studio 2008,点击菜单栏中的“文件”—>“新建”—>“项目”,在弹出的对话框中选择新建一个Visual C#的Windows应用程序,之后更改项目名称为“ZZUMap”,更改文件的路径为个人实习文件夹,点击“确定”即可。
选中项目“ZZUMap”中的窗体“Form1”,修改其Name属性为“MainForm”,Text属性为“ZZUMap”。
2添加控件
选择工具箱中的“菜单和工具栏|MenuStrip”,将其拖入窗体。
选择工具箱中的“ArcGIS Windows Forms”节,将“ToolbarControl”控件拖入窗体,并将其属性中的Dock设置为Top。
选择工具箱中的“菜单和工具栏|StatusStrip”,将其拖入到窗体。
选择工具箱中的“容器|SplitContainer”容器拖入窗体,并将其属性中的Dock设置为Fill。
再将“容器|SplitContainer”容器拖入Panel1,设置为水平拆分方向,并将其属性中的Dock设置为Fill。
将TabControl控件拖入Panel1,将Alignment属性设置为Bottom,Dock属性设置为Fill。点击TabPages属性右边的按钮,弹出TabPage集合编辑器,将tabPage1的Name设置为tabPageLayer,Text设置为图层,将tabPage2的Name设置为tabPageProperty,Text设置为属性。
选择“图层”选项卡,拖入TOCControl控件,设置Dock属性为Fill。
选择“属性”选项卡,拖入PropertyGrid设置Dock属性为Fill。
拖入TabControl控件到第一个SplitContainer容器的Panel2,设置Dock属性为Fill。并上述类似的方法,将两个选项卡的Name和Text分别设置为:(tabPageMap、地图),(tabPageLayout,制版)。
选择“地图”选项卡,拖入MapControl控件,设置Dock属性为Fill。
选择“制版”选项卡,拖入PageLayoutControl控件,设置Dock属性为Fill。
将MapControl控件拖入第二个SplitContainer容器的Panel2,设置Dock属性为Fill。
最后将LicenseControl控件拖入到窗体的任意地方。
按F5编译运行还不能运行,因为就很可能是你的ArcEngine10.0没有许可,因为我的就是这个情况,所以会报错,重新授权下就OK了,解决方法:打开program.cs
把ESRI.ArcGIS.RuntimeManager.Bind(ESRI.ArcGIS.ProductCode.EngineOrDesktop);
这句放到Application.SetCompatibleTextRenderingDefault(false);和Application.Run(new Form1());之间。
3控件绑定
通过以上步骤添加的控件还只是单独存在,而我们的程序需要各控件间协同工作,因此要进行控件绑定。
分别右击ToolbarControl、TOCControl控件,将Buddy设置为axMapControl1,如下图所示。
这样,工具条和图层控件就与地图控件关联了。
4添加工具
此时,工具条中还没有任何工具,添加的方法也很简单。右击ToolbarControl,选择“属性|Items”,点击Add,选择Commands选项卡中的Generic,双击Open、SaveAs、Redo、Undo即可将相应工具添加到工具条。
常见的工具有:
Map Navigation中的导航工具,Map Inquiry中的查询工具,Feature Selection中的选择工具,你可以根据需要酌情添加工具,如下图。
目前为止运行程序界面如下:
5添加菜单
在设计视图中,单击菜单栏,会出现“请在此处键入”的提示,单击提示就可以键入菜单名称,如“文件”,再单击“文件”,即可输入其下拉子菜单。
每创建一个菜单,请在其属性面板中设置 Name 属性,而且不要为中文,因此 Name 值将是此菜单响应函数的函数名的一部分,带中文的函数名,总是不好吧。我们将添加新建( New )、打开( Open )、添加数据( AddData )、保存( Save )、另存为( SaveAs )、退出( Exit )这些菜单,()内为相应的 Name 属性值。
你可以在属性面板中的 Text 属性中,把菜单名设置为中英文形式,如“打开 O pen ”,带下划线的 O 表示此项菜单的快捷键是字母 O ,设置方法是在相应字母前加上“ & ”字符,如“打开 &Open ”。但这种快捷键只在打开此下拉菜单时才有效,即当你单击“文件”菜单弹出下拉菜单时,按下字母 O 就可以定位到“打开”菜单。
还有一种在程序运行时都有效的全局快捷键,可以在属性面板中的“ ShortCutKeys ”中设置。
你还可以在属性面板中的 Image 属性中设置你喜欢的菜单图标。单击 Image 那一行右边的按钮,弹出如下菜单。选择“项目资源文件”,再单击导入就可以选择你的图标了。
最终效果如下所示。
注意,在解决方案面板中,选中刚才添加的所有图标,在其属性面板中将生成操作设置为“嵌入的资源”,这一点很重要!菜单的实现参考ArcEngine自带的模板MapControl Application中的菜单实现。先新建一个类,命名为CreateNewDocument.cs,添加引用:
using ESRI.ArcGIS.ADF.BaseClasses;
using ESRI.ArcGIS.ADF.CATIDs;
using ESRI.ArcGIS.Controls;
using ESRI.ArcGIS.Carto;
using ESRI.ArcGIS.SystemUI;
using System.Windows.Forms;
在构造函数中继承类:
public class CreateNewDocument : BaseCommand;
{
……
}
然后将以下代码复制覆盖到类CreateNewDocument中
private IHookHelper m_hookHelper = null;
//constructor
public CreateNewDocument()
{
//update the base properties
base.m_category = ".NET Application";
base.m_caption = "NewDocument";
base.m_message = "Create a new map";
base.m_toolTip = "Create a new map";
base.m_name = "DotNetTemplate_NewDocumentCommand";
}
#region Overridden Class Methods
/// <summary>
/// Occurs when this command is created
/// </summary>
/// <param name="hook">Instance of the application</param>
public override void OnCreate(object hook)
{
if (m_hookHelper == null)
m_hookHelper = new HookHelperClass();
m_hookHelper.Hook = hook;
}
/// <summary>
/// Occurs when this command is clicked
/// </summary>
public override void OnClick()
{
IMapControl3 mapControl = null;
//get the MapControl from the hook in case the container is a ToolbarControl
if (m_hookHelper.Hook is IToolbarControl)
{
mapControl = (IMapControl3)((IToolbarControl)m_hookHelper.Hook).Buddy;
}
//In case the container is MapControl
else if (m_hookHelper.Hook is IMapControl3)
{
mapControl = (IMapControl3)m_hookHelper.Hook;
}
else
{
MessageBox.Show("Active control must be MapControl!", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
return;
}
//check to see if there is an active edit session and whether edits have been made
DialogResult result;
IEngineEditor engineEditor = new EngineEditorClass();
if ((engineEditor.EditState == esriEngineEditState.esriEngineStateEditing) && (engineEditor.HasEdits() == true))
{
result = MessageBox.Show("Would you like to save your edits", "Save Edits", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question);
switch (result)
{
case DialogResult.Cancel:
return;
case DialogResult.No:
engineEditor.StopEditing(false);
break;
case DialogResult.Yes:
engineEditor.StopEditing(true);
break;
}
}
//allow the user to save the current document
DialogResult res = MessageBox.Show("Would you like to save the current document?", "AoView", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
if (res == DialogResult.Yes)
{
//launch the save command
ICommand command = new ControlsSaveAsDocCommandClass();
command.OnCreate(m_hookHelper.Hook);
command.OnClick();
}
//create a new Map
IMap map = new MapClass();
map.Name = "Map";
//assign the new map to the MapControl
mapControl.DocumentFilename = string.Empty;
mapControl.Map = map;
}
#endregion
然后定义指针和变量:
privEmpty; ate ESRI.ArcGIS.Controls.IMapControl4 m_mapControl = null;
private ESRI.ArcGIS.Controls.IPageLayoutControl3 m_pageLayoutControl = null;
private string m_mapDocumentName = string.
最后在菜单按钮的事件中填入以下代码:
private void AddData_Click(object sender, EventArgs e)
{
int currentLayerCount = this.axMapControl1.LayerCount;
ICommand pCommand = new ControlsAddDataCommandClass();
pCommand.OnCreate(this.axMapControl1.Object);
pCommand.OnClick();
}
private void Save_Click(object sender, EventArgs e)
{
//execute Save Document command
if (m_mapControl.CheckMxFile(m_mapDocumentName))
{
//create a new instance of a MapDocument
IMapDocument mapDoc = new MapDocumentClass();
mapDoc.Open(m_mapDocumentName, string.Empty);
//Make sure that the MapDocument is not readonly
if (mapDoc.get_IsReadOnly(m_mapDocumentName))
{
MessageBox.Show("Map document is read only!");
mapDoc.Close();
return;
}
}
}
private void SavaAs_Click(object sender, EventArgs e)
{
//execute SaveAs Document command
ICommand command = new ControlsSaveAsDocCommandClass();
command.OnCreate(m_mapControl.Object);
command.OnClick();
}
private void Open_Click(object sender, EventArgs e)
{
//execute Open Document command
ICommand command = new ControlsOpenDocCommandClass();
command.OnCreate(m_mapControl.Object);
command.OnClick();
}
private void Exit_Click(object sender, EventArgs e)
{
//exit the application
Application.Exit();
}
private void New_Click(object sender, EventArgs e)
{
//execute New Document command
ICommand command = new CreateNewDocument();
command.OnCreate(m_mapControl.Object);
command.OnClick();
}
6PageLayout与MapControl联动
在ArcMap程序中,数据视图和布局视图中的数据改变是实时互动的,那是因为它们本来就是在处理同一份数据。将axMapControl1中的Map拷贝到axPageLayoutControl1代码如下:
private voidCopyAndOverwriteMap()
{
//Get IObjectCopy interface
IObjectCopy objectCopy =newObjectCopyClass();
//Get IUnknown interface (map to copy)
object toCopyMap = axMapControl1.Map;
//Get IUnknown interface (copied map)
object copiedMap = objectCopy.Copy(toCopyMap);
//Get IUnknown interface (map to overwrite)
object toOverwriteMap = axPageLayoutControl1.ActiveView.FocusMap;
//Overwrite the PageLayoutControl's map
objectCopy.Overwrite(copiedMap,reftoOverwriteMap);
}
要实现axPageLayoutControl1中的数据与axMapControl1中的数据同步,需要在axMapControl1的以下事件中写入相应的代码:
private voidaxMapControl1_OnMapReplaced(objectsender,
ESRI.ArcGIS.MapControl.IMapControlEvents2_OnMapReplacedEvent e)
{
CopyAndOverwriteMap();
}
private voidaxMapControl1_OnAfterScreenDraw(objectsender,
ESRI.ArcGIS.MapControl.IMapControlEvents2_OnAfterScreenDrawEvent e)
{
//Get IActiveView interface
IActiveView activeView = (IActiveView)
axPageLayoutControl1.ActiveView.FocusMap;
//Get IDisplayTransformation interface
IDisplayTransformation displayTransformation =
activeView.ScreenDisplay.DisplayTransformation;
//Set the visible extent of the focus map
displayTransformation.VisibleBounds = axMapControl1.Extent;
axPageLayoutControl1.ActiveView.Refresh(); //根据MapControl的视图范围,确定PageLayoutControl的视图范围
CopyAndOverwriteMap();
}
private voidaxMapControl1_OnViewRefreshed(objectsender,
ESRI.ArcGIS.MapControl.IMapControlEvents2_OnViewRefreshedEvent e)
{
//axTOCControl1.Update ();
CopyAndOverwriteMap();
}
运行但是axTOCControl控件图层不显示,这是因为还要设置SetBuddyControl,ToolBarControl控件中同样要设置,只需要在在主窗口Load函数中添加下面一句话就可以了
private void MainForm_Load(object sender, EventArgs e)
{
axTOCControl1.SetBuddyControl(axMapControl1);//ADDaxToolbarControl1.SetBuddyControl(m_mapControl);
}
7鹰眼的实现
鹰眼用来显示主窗体当前视图范围在全景视图中的位置,在ArcMap中使用一个线框在鹰眼视图中标识。当主视图中的视图范围改变时,鹰眼中的线框随之改变,当拖动鹰眼视图中的红线框时,主视图中的视图范围也随之改变。
下面开始实现鹰眼功能,添加using ESRI.ArcGIS.Carto、using ESRI.ArcGIS.Geometry、
using ESRI.ArcGIS.Display三个引用。首先在axMapControl1中视图范围改变时鹰眼窗体要做出对应的响应,即绘制线框并显示,在OnExtentUpdated事件中添加代码如下:
private void axMapControl1_OnExtentUpdated (object sender, ESRI.ArcGIS.Controls.IMapControlEvents2_OnExtentUpdatedEvent e)
{
//创建鹰眼中线框
IEnvelope pEnv = (IEnvelope)e.newEnvelope;
IRectangleElement pRectangleEle = new RectangleElementClass();
IElement pEle = pRectangleEle as IElement;
pEle.Geometry = pEnv;
//设置线框的边线对象,包括颜色和线宽
IRgbColor pColor = new RgbColorClass();
pColor.Red = 255;
pColor.Green = 0;
pColor.Blue = 0;
pColor.Transparency = 255;
// 产生一个线符号对象
ILineSymbol pOutline = new SimpleLineSymbolClass();
pOutline.Width = 2;
pOutline.Color = pColor;
// 设置颜色属性
pColor.Red = 255;
pColor.Green = 0;
pColor.Blue = 0;
pColor.Transparency = 0;
// 设置线框填充符号的属性
IFillSymbol pFillSymbol = new SimpleFillSymbolClass();
pFillSymbol.Color = pColor;
pFillSymbol.Outline = pOutline;
IFillShapeElement pFillShapeEle = pEle as IFillShapeElement;
pFillShapeEle.Symbol = pFillSymbol;
// 得到鹰眼视图中的图形元素容器
IGraphicsContainer pGra = axMapControl2.Map as IGraphicsContainer;
IActiveView pAv = pGra as IActiveView;
// 在绘制前,清除 axMapControl2 中的任何图形元素
pGra.DeleteAllElements();
// 鹰眼视图中添加线框
pGra.AddElement((IElement)pFillShapeEle, 0);
// 刷新鹰眼
pAv.PartialRefresh(esriViewDrawPhase.esriViewGraphics, null, null);
}
当鼠标点击鹰眼窗体时,主窗体Extent随之改变。在axMapControl2的OnMouseDown事件中添加代码如下:
private void axMapControl2_OnMouseDown(object sender, ESRI.ArcGIS.Controls.IMapControlEvents2_OnMouseDownEvent e)
{
if (this.axMapControl2.Map.LayerCount != 0)
{
// 按下鼠标左键移动矩形框
if (e.button == 1)
{
IPoint pPoint = new PointClass();
pPoint.PutCoords(e.mapX, e.mapY);
IEnvelope pEnvelope = this.axMapControl1.Extent;
pEnvelope.CenterAt(pPoint);
this.axMapControl1.Extent = pEnvelope;
this.axMapControl1.ActiveView.PartialRefresh(esriViewDrawPhase.esriViewGeography, null, null);
}
// 按下鼠标右键绘制矩形框
else if (e.button == 2)
{
IEnvelope pEnvelop = this.axMapControl2.TrackRectangle();
this.axMapControl1.Extent = pEnvelop;
this.axMapControl1.ActiveView.PartialRefresh(esriViewDrawPhase.esriViewGeography, null, null);
}
}
}
当鼠标在鹰眼窗体移动时,主窗体Extent随之改变。在axMapControl2的OnMouseMove事件中添加代码如下:
private void axMapControl2_OnMouseMove(object sender, ESRI.ArcGIS.Controls.IMapControlEvents2_OnMouseMoveEvent e)
{
// 如果不是左键按下就直接返回
if (e.button != 1) return;
IPoint pPoint = new PointClass();
pPoint.PutCoords(e.mapX, e.mapY);
this.axMapControl1.CenterAt(pPoint);
this.axMapControl1.ActiveView.PartialRefresh(esriViewDrawPhase.esriViewGeography, null, null);
}
下面代码用于实现axMapControl2与axMapControl1的数据的同步更新,获取主视图中视图范围最大的图层作为鹰眼中的视图。这个更新由两部分组成,一个是对axMapControl1添加地图文档(mxd文件)的响应,通过axMapControl1的OnMapReplace事件实现,一个是对axMapControl1添加单个图层的响应,通过axMapControl1的OnFullExtentUpdated事件实现。我们获取主视图中的视图范围最大的图层写成一个独立的函数,方便调用。
private ILayer GetOverviewLayer(IMap map)
{
//获取主视图的第一个图层
ILayer pLayer = map.get_Layer(0);
//遍历其他图层,并比较视图范围的宽度,返回宽度最大的图层
ILayer pTempLayer = null;
for (int i = 1; i < map.LayerCount;i++ )
{
pTempLayer = map.get_Layer(i);
if (pLayer.AreaOfInterest.Width < pTempLayer.AreaOfInterest.Width)
pLayer = pTempLayer;
}
return pLayer;
}
然后在axMapControl1的OnMapReplaced事件中调用。
private void axMapControl1_OnMapReplaced(object sender, IMapControlEvents2_OnMapReplacedEvent e)
{
//获取鹰眼图层
this.axMapControl2.AddLayer(this.GetOverviewLayer(this.axMapControl1.Map));
// 设置 MapControl 显示范围至数据的全局范围
this.axMapControl2.Extent = this.axMapControl1.FullExtent;
// 刷新鹰眼控件地图
this.axMapControl2.Refresh();
}
在axMapControl1的OnFullExtentUpdated添加代码,用于实现在主视图添加图层时,实现对鹰眼视图的更新。代码如下:
private void axMapControl1_OnFullExtentUpdated(object sender, ESRI.ArcGIS.Controls.IMapControlEvents2_OnFullExtentUpdatedEvent e)
{
//获取鹰眼图层
this.axMapControl2.AddLayer(this.GetOverviewLayer(this.axMapControl1.Map));
// 设置 MapControl 显示范围至数据的全局范围
this.axMapControl2.Extent = this.axMapControl1.FullExtent;
// 刷新鹰眼控件地图
this.axMapControl2.Refresh();
this.axMapControl2.Map = new MapClass();
// 添加主地图控件中的所有图层到鹰眼控件中
for (int i = 1; i <= this.axMapControl1.LayerCount; i++)
{
this.axMapControl2.AddLayer(this.axMapControl1.get_Layer(this.axMapControl1.LayerCount -
i));
}
// 设置 MapControl 显示范围至数据的全局范围
this.axMapControl2.Extent = this.axMapControl1.FullExtent;
// 刷新鹰眼控件地图
this.axMapControl2.Refresh();
}
运行程序,添加地图数据,可以在主视图进行相关操作,鹰眼视图同步响应,在鹰眼视图可以移动红线框可以同步更新主视图的视图范围,在鹰眼视图单击右键拉框可以重新绘制红线框,效果如下:
8状态栏的实现
应用程序的状态栏一般用来显示程序的当前状态,当前所使用的工具。 GIS 应用程序一般也在状态栏显示当前光标的坐标、比例尺等信息。学习完本讲内容,您将学会状态栏编程的基本方法,并且能够在我们的程序的状态栏中添加且显示以下信息:
当前所用工具信息
当前比例尺
当前坐标
在设计视图中,点击窗体中的状态栏,在其属性面板中找到“ Items ”项,单击其右边的按钮,在下拉框中选择“ StatusLabel ”,单击“添加按钮”,依次添加四个 StatusLabel ,依次修改属性参数如下表所示:
序号
Name 属性
Text 属性
Spring 属性
说明
1
MessageLabel
就绪
False
当前所用工具信息
2
Blank
True
占位
3
ScaleLabel
比例尺
False
当前比例尺
4
CoordinateLabel
当前坐标
False
当前坐标
设置好之后如下图所示:
Spring 属性表示可以按状态栏剩余空间自动伸缩。所以加入 Blank 项目,只是为了占个位子,以达到 ScaleLabel 和 CoordinateLabel 项目右对齐而 MessageLabel 项目左对齐的目的。
首先添加 axToolbarControl1 的 OnMouseMove 事件 ( 相信大家看了以上的教程,已经知道怎么添加事件了吧,还不知道的建议再温习下前几讲的内容 ) 。在其事件响应函数代码如下:
private void axToolbarControl1_OnMouseMove(object sender, IToolbarControlEvents_OnMouseMoveEvent e)
{// 取得鼠标所在工具的索引号
int index = axToolbarControl1.HitTest(e.x, e.y, false);
if (index != -1)
{
// 取得鼠标所在工具的 ToolbarItem
IToolbarItem toolbarItem = axToolbarControl1.GetItem(index);
// 设置状态栏信息
MessageLabel.Text = toolbarItem.Command.Message;
}
else
{
MessageLabel.Text = " 就绪 ";
}
}
添加 axMapControl1 的 OnMouseMove 事件,其代码如下:
private void axMapControl1_OnMouseMove(object sender, IMapControlEvents2_OnMouseMoveEvent e)
{
// 显示当前比例尺
ScaleLabel.Text = " 比例尺 1:" + ((long)this.axMapControl1.MapScale).ToString();
}
显示当前坐标也是 axMapControl1 的 OnMouseMove 事件中响应,故只要在 axMapControl1_OnMouseMove 函数中添加如下代码即可:
// 显示当前坐标
CoordinateLabel.Text = " 当前坐标 X = " + e.mapX.ToString() + " Y = " + e.mapY.ToString() + " " + this.axMapControl1.MapUnits;
按 F5 编译运行,可以看到,我们的程序已经能够正常工作了。但是细心的你可能会发现,当前坐标的后面的坐标单位为“ esriUnknownUnits ”或“ esriMeters ”之类,即系统在正常单位的前面加上了“ esri ”,追求完美的我们自然看得不舒服。那就进行简单的替换吧。
首先定义个全局坐标单位变量 sMapUnits ,如下所示:
private string sMapUnits;
再 Form1_Load 函数中进行初始化:
sMapUnits = "Unknown";
添加 axMapControl1 控件的 OnMouseMove 事件,在事件响应函数中进行坐标单位替换,并将this.axMapControl1.MapUnits替换为sMapUnits,代码如下:
esriUnits mapUnits = axMapControl1.MapUnits;
switch (mapUnits)
{
case esriUnits.esriCentimeters:
sMapUnits = "Centimeters";
break;
case esriUnits.esriDecimalDegrees:
sMapUnits = "Decimal Degrees";
break;
case esriUnits.esriDecimeters:
sMapUnits = "Decimeters";
break;
case esriUnits.esriFeet:
sMapUnits = "Feet";
break;
case esriUnits.esriInches:
sMapUnits = "Inches";
break;
case esriUnits.esriKilometers:
sMapUnits = "Kilometers";
break;
case esriUnits.esriMeters:
sMapUnits = "Meters";
break;
case esriUnits.esriMiles:
sMapUnits = "Miles";
break;
case esriUnits.esriMillimeters:
sMapUnits = "Millimeters";
break;
case esriUnits.esriNauticalMiles:
sMapUnits = "NauticalMiles";
break;
case esriUnits.esriPoints:
sMapUnits = "Points";
break;
case esriUnits.esriUnknownUnits:
sMapUnits = "Unknown";
break;
case esriUnits.esriYards:
sMapUnits = "Yards";
break;
}
按 F5 编译运行程序。如果你足够细心的话,相信你已经成功了!
9右键的实现
在AE开发中,右键菜单有两种实现方式,一是使用VS2005自带的ContextMenuStrip控件,二是用AE封装的IToolbarMenu接口。相比较而言,后者更为简单实用,本文采用后者的实现方法。由于在TOCControl控件右键菜单上要查询属性表,所以先新建一个普通类,命名为FrmAttribute,并添加以下代码:
public DataGridView GetDataGrid
{
get
{
return dataGridView1;
}
}
在Form1类里面添加如下变量的定义:
//右键菜单
private ITOCControl2 m_tocControl;
private IToolbarMenu m_menuMap;
private IToolbarMenu m_menuLayer;
private FrmAttribute frmOpenAttributeTable;
ILayer pmovelayer;
当然,添加菜单项之前,必须实现相应命令或工具。这里的命令或工具可以AE内置的也可以是自定义的。AE内置了许多可以直接调用的常用命令和工具,如ControlsAddDataCommandClass,在ESRI.ArcGIS.Controls命名空间中,大家可以对象浏览器中查看。当然,这里也可以直接调用AE内置的菜单,如ControlsFeatureSelectionMenu。另外,本讲也实现三自定义命令,以做示范。它们分别为图层可视控制命令(用于控制图层显示与否)、移除图层和放大到整个图层命令。实现方法也很简单,就是右击ZZUMap项目,选择“添加|类”,选择C#普通的类模板,用以下代码覆盖系统自己生成的所有代码。
LayerSelectable类代码:
using System;
using System.Collections.Generic;
using System.Text;
using ESRI.ArcGIS.ADF.BaseClasses;
using ESRI.ArcGIS.Carto;
using ESRI.ArcGIS.Controls;
using ESRI.ArcGIS.SystemUI;
namespace ZZUMap
{
public sealed class LayerSelectable : BaseCommand, ICommandSubType
{
private IMapControl3 m_mapControl;
private long m_subType;public LayerSelectable()
{
}public override void OnClick()
{
IFeatureLayer layer = (IFeatureLayer)m_mapControl.CustomProperty;
if (m_subType == 1) layer.Selectable = true;
if (m_subType == 2) layer.Selectable = false;
}public override void OnCreate(object hook)
{
m_mapControl = (IMapControl3)hook;
}public override bool Enabled
{
get
{
ILayer layer = (ILayer)m_mapControl.CustomProperty;
if (layer is IFeatureLayer)
{
IFeatureLayer featureLayer = (IFeatureLayer)layer;
if (m_subType == 1) return !featureLayer.Selectable;
else return featureLayer.Selectable;
}
else
{
return false;
}
}
}public int GetCount()
{
return 2;
}public void SetSubType(int SubType)
{
m_subType = SubType;
}public override string Caption
{
get
{
if (m_subType == 1) return "图层可选";
else return "图层不可选";
}
}
}
}
LayerVisibility类代码
using System;
using System.Collections.Generic;
using System.Text;
using ESRI.ArcGIS.ADF.BaseClasses;
using ESRI.ArcGIS.Controls;
using ESRI.ArcGIS.Carto;
using ESRI.ArcGIS.SystemUI;namespace ZZUMap
{
public sealed class LayerVisibility : BaseCommand, ICommandSubType
{
private IHookHelper m_hookHelper = new HookHelperClass();
private long m_subType;public LayerVisibility()
{
}public override void OnClick()
{
for (int i = 0; i <= m_hookHelper.FocusMap.LayerCount - 1; i++)
{
if (m_subType == 1) m_hookHelper.FocusMap.get_Layer(i).Visible = true;
if (m_subType == 2) m_hookHelper.FocusMap.get_Layer(i).Visible = false;
}
m_hookHelper.ActiveView.PartialRefresh(esriViewDrawPhase.esriViewGeography, null, null);
}public override void OnCreate(object hook)
{
m_hookHelper.Hook = hook;
}public int GetCount()
{
return 2;
}public void SetSubType(int SubType)
{
m_subType = SubType;
}public override string Caption
{
get
{
if (m_subType == 1) return "Turn All Layers On";
else return "Turn All Layers Off";
}
}public override bool Enabled
{
get
{
bool enabled = false; int i;
if (m_subType == 1)
{
for (i = 0; i <= m_hookHelper.FocusMap.LayerCount - 1; i++)
{
if (m_hookHelper.ActiveView.FocusMap.get_Layer(i).Visible == false)
{
enabled = true;
break;
}
}
}
else
{
for (i = 0; i <= m_hookHelper.FocusMap.LayerCount - 1; i++)
{
if (m_hookHelper.ActiveView.FocusMap.get_Layer(i).Visible == true)
{
enabled = true;
break;
}
}
}
return enabled;
}
}
}
}OpenAttributeTable类代码:
using System;
using System.Collections.Generic;
using System.Text;
using ESRI.ArcGIS.ADF.BaseClasses;
using ESRI.ArcGIS.Carto;
using ESRI.ArcGIS.Controls;
using ESRI.ArcGIS.CartoUI;
using System.Data;
using System.Collections;
using ESRI.ArcGIS.Geodatabase;
using System.IO;
using ESRI.ArcGIS.GeoDatabaseUI;
using ESRI.ArcGIS.Display;
using System.Windows.Forms;
namespace ZZUMap
{
public class OpenAttributeTable : BaseCommand
{private IMapControl3 m_mapControl;
private IHookHelper m_hookHelper = new HookHelperClass();
private FrmAttribute frmOpenAttributeTable;public OpenAttributeTable()
{
base.m_category = ""; //localizable text
base.m_caption = " 打开属性表"; //localizable text
base.m_message = "This should work in ArcMap/MapControl/PageLayoutControl"; //localizable text
base.m_toolTip = ""; //localizable text
base.m_name = ""; //unique id, non-localizable (e.g. "MyCategory_MyCommand")}
#region Overriden Class Methodspublic override void OnClick()
{frmOpenAttributeTable = new FrmAttribute();
DataGridView dataGrid;
dataGrid = frmOpenAttributeTable.GetDataGrid;frmOpenAttributeTable.Show();
frmOpenAttributeTable.TopMost = true;base.OnClick();
IFeatureLayer pFeatLyr = (IFeatureLayer)m_mapControl.CustomProperty;
//查询ILayerFields接口
ILayerFields pFeatLyrFields = (ILayerFields)pFeatLyr;
IFeatureClass pFeatCls = pFeatLyr.FeatureClass;
frmOpenAttributeTable.Text = pFeatLyr.Name + "--属性表";
DataSet myDataset = new DataSet("Test");
DataTable pTable = new DataTable(pFeatLyr.Name);
for (int n = 0; n < pFeatLyrFields.FieldCount - 1; n++)
{
DataColumn pTableCol = new DataColumn(pFeatLyrFields.get_Field(n).Name);
pTable.Columns.Add(pTableCol);
pTableCol = null;
}
IFeatureCursor pFeatCursor = pFeatCls.Search(null, false);
IFeature pFeat = pFeatCursor.NextFeature();
while (pFeat != null)
{
DataRow pTableRow = pTable.NewRow();
for (int i = 0; i < pFeatLyrFields.FieldCount - 1; i++)
{
if (pFeatLyrFields.FindField(pFeatCls.ShapeFieldName) == i)
{
pTableRow[i] = pFeatCls.ShapeType.ToString();
}
else
{
pTableRow[i] = pFeat.get_Value(i).ToString();
}
}
pTable.Rows.Add(pTableRow);
pFeat = pFeatCursor.NextFeature();
}
myDataset.Tables.Add(pTable);
dataGrid.DataSource = myDataset;dataGrid.DataMember = pFeatLyr.Name;
}
#endregionpublic override void OnCreate(object hook)
{
m_mapControl = (IMapControl3)hook;
throw new NotImplementedException();}
}
}RemoveLayer类代码:
using System;
using System.Collections.Generic;
using System.Text;
using ESRI.ArcGIS.ADF.BaseClasses;
using ESRI.ArcGIS.Carto;
using ESRI.ArcGIS.Controls;namespace ZZUMap
{public sealed class RemoveLayer : BaseCommand
{
private IMapControl3 m_mapcontrol;
public RemoveLayer()
{
base.m_caption = "移除图层";
}
public override void OnCreate(object hook)
{
m_mapcontrol = (IMapControl3)hook;
throw new NotImplementedException();
}
public override void OnClick()
{base.OnClick();
ILayer layer = (ILayer)m_mapcontrol.CustomProperty;
m_mapcontrol.Map.DeleteLayer(layer);
}}
}
ScaleThresholds类代码:
using System;
using System.Collections.Generic;
using System.Text;
using ESRI.ArcGIS.ADF.BaseClasses;
using ESRI.ArcGIS.Carto;
using ESRI.ArcGIS.Controls;
using ESRI.ArcGIS.SystemUI;
namespace ZZUMap
{public class ScaleThresholds : BaseCommand, ICommandSubType
{
private IMapControl3 m_mapControl;
private long m_subType;public ScaleThresholds()
{}
public override void OnClick()
{
ILayer layer = (ILayer)m_mapControl.CustomProperty;
if (m_subType == 1) layer.MaximumScale = m_mapControl.MapScale;
if (m_subType == 2) layer.MinimumScale = m_mapControl.MapScale;
if (m_subType == 3)
{
layer.MaximumScale = 0;
layer.MinimumScale = 0;
}
m_mapControl.Refresh(esriViewDrawPhase.esriViewGeography, null, null);
}public override void OnCreate(object hook)
{
m_mapControl = (IMapControl3)hook;
}public int GetCount()
{
return 3;
}public void SetSubType(int SubType)
{
m_subType = SubType;
}public override string Caption
{
get
{
if (m_subType == 1) return "图层最大尺寸";
else if (m_subType == 2) return "图层最小尺寸";
else return "删除图层比例阈值";
}
}public override bool Enabled
{
get
{
bool enabled = true;
ILayer layer = (ILayer)m_mapControl.CustomProperty;if (m_subType == 3)
{
if ((layer.MaximumScale == 0) & (layer.MinimumScale == 0)) enabled = false;
}
return enabled;
}
}
}
}ZoomToLayer类代码:
using System;
using System.Collections.Generic;
using System.Text;using ESRI.ArcGIS.ADF.BaseClasses;
using ESRI.ArcGIS.Carto;
using ESRI.ArcGIS.Controls;
namespace ZZUMap
{
public sealed class ZoomToLayer : BaseCommand
{
private IMapControl3 m_mapControl;public ZoomToLayer()
{
base.m_caption = "全屏缩放";
}public override void OnClick()
{
ILayer layer = (ILayer)m_mapControl.CustomProperty;
m_mapControl.Extent = layer.AreaOfInterest;
}public override void OnCreate(object hook)
{
m_mapControl = (IMapControl3)hook;
}
}
}
在Form1_Load函数进行初始化,即菜单的创建:
//右键菜单
m_tocControl = (ITOCControl2)axTOCControl1.Object;
m_mapControl = (IMapControl3)axMapControl1.Object;
frmOpenAttributeTable = new FrmAttribute();
Add pre-defined control commands to the ToolbarControl
//axToolbarControl1.AddItem("esriControls.ControlsSelectFeaturesTool", -1, 0, false, 0, esriCommandStyles.esriCommandStyleIconOnly);
//axToolbarControl1.AddToolbarDef("esriControls.ControlsMapNavigationToolbar", 0, false, 0, esriCommandStyles.esriCommandStyleIconOnly);
//axToolbarControl1.AddItem("esriControls.ControlsOpenDocCommand", -1, 0, false, 0, esriCommandStyles.esriCommandStyleIconOnly);
//Add custom commands to the map menu
m_menuMap = new ToolbarMenuClass();
m_menuMap.AddItem(new LayerVisibility(), 1, 0, false, esriCommandStyles.esriCommandStyleTextOnly);
m_menuMap.AddItem(new LayerVisibility(), 2, 1, false, esriCommandStyles.esriCommandStyleTextOnly);
//Add pre-defined menu to the map menu as a sub menu
m_menuMap.AddSubMenu("esriControls.ControlsFeatureSelectionMenu", 2, true);
//Add custom commands to the map menu
m_menuLayer = new ToolbarMenuClass();
m_menuLayer.AddItem(new RemoveLayer(), -1, 0, false, esriCommandStyles.esriCommandStyleTextOnly);
m_menuLayer.AddItem(new ScaleThresholds(), 1, 1, true, esriCommandStyles.esriCommandStyleTextOnly);
m_menuLayer.AddItem(new ScaleThresholds(), 2, 2, false, esriCommandStyles.esriCommandStyleTextOnly);
m_menuLayer.AddItem(new ScaleThresholds(), 3, 3, false, esriCommandStyles.esriCommandStyleTextOnly);
m_menuLayer.AddItem(new LayerSelectable(), 1, 4, true, esriCommandStyles.esriCommandStyleTextOnly);
m_menuLayer.AddItem(new LayerSelectable(), 2, 5, false, esriCommandStyles.esriCommandStyleTextOnly);
m_menuLayer.AddItem(new ZoomToLayer(), -1, 6, true, esriCommandStyles.esriCommandStyleTextOnly);
m_menuLayer.AddItem(new OpenAttributeTable(), -1, 0, true, esriCommandStyles.esriCommandStyleTextOnly);
//Set the hook of each menu
m_menuLayer.SetHook(m_mapControl);
m_menuMap.SetHook(m_mapControl);这样就完成了TOCControl控件的右键菜单功能,接下来做地图右键菜单功能的开发。先添加一个contextMenuStrip控件,如下图添加子菜单:
各个菜单函数如下:
private void 全图ToolStripMenuItem_Click(object sender, EventArgs e)
{
ICommand pCommand = new ControlsMapFullExtentCommandClass();
pCommand.OnCreate(this.axMapControl1.Object);
pCommand.OnClick();
}private void 前一视图ToolStripMenuItem_Click(object sender, EventArgs e)
{
ICommand pCommand = new ControlsMapZoomToLastExtentBackCommandClass();
pCommand.OnCreate(this.axMapControl1.Object);
pCommand.OnClick();
}private void 后一视图ToolStripMenuItem_Click(object sender, EventArgs e)
{
ICommand pCommand = new ControlsMapZoomToLastExtentForwardCommandClass();
pCommand.OnCreate(this.axMapControl1.Object);
pCommand.OnClick();
}private void 局部放大ToolStripMenuItem_Click(object sender, EventArgs e)
{
ICommand pCommand = new ControlsMapZoomInFixedCommandClass();
pCommand.OnCreate(this.axMapControl1.Object);
pCommand.OnClick();
}private void 局部缩小ToolStripMenuItem_Click(object sender, EventArgs e)
{
ICommand pCommand = new ControlsMapZoomOutFixedCommandClass();
pCommand.OnCreate(this.axMapControl1.Object);
pCommand.OnClick();
}private void 居中ToolStripMenuItem_Click(object sender, EventArgs e)
{
this.axMapControl1.CenterAt(this.mapRightClickPoint);
}private void 选择要素ToolStripMenuItem_Click(object sender, EventArgs e)
{
IMap pMap = this.axMapControl1.Map;
ISelectionEnvironment pSelectionEnvironment = new SelectionEnvironmentClass();
pSelectionEnvironment.LinearSearchDistance = this.axMapControl1.ActiveView.Extent.Width / 200;
pSelectionEnvironment.PointSearchDistance = this.axMapControl1.ActiveView.Extent.Width / 200;
pMap.SelectByShape(this.mapRightClickPoint as IGeometry, null, false);
this.axMapControl1.ActiveView.PartialRefresh(esriViewDrawPhase.esriViewGeoSelection, null, null);
}private void 识别ToolStripMenuItem_Click(object sender, EventArgs e)
{}
最后在地图的MouseDown事件中添加响应事件,代码如下:
private void axMapControl1_OnMouseDown(object sender, IMapControlEvents2_OnMouseDownEvent e)
{
if (e.button == 2)
{
if (this.mapRightClickPoint == null)
{
this.mapRightClickPoint = new PointClass();
}
this.mapRightClickPoint.PutCoords(e.mapX, e.mapY);
IGraphicsContainer pGraphicContainer = this.axMapControl1.Map as IGraphicsContainer;
IEnumElement pEnumElement = pGraphicContainer.LocateElements(this.mapRightClickPoint, this.axMapControl1.ActiveView.Extent.Width / 500);
if (pEnumElement != null)
{
return;
}
else
{
this.contextMenuStrip1.Show(this.axMapControl1 as Control, new System.Drawing.Point(e.x, e.y));
}
}
}这样就完成了地图右键功能的开发。
10符号选择器的实现
新建Winodws窗体,命名为SymbolSelectorFrm,修改窗体的Text属性为“选择符号”。并添加SymboloryControl、PictureBox、Button、Label、NumericUpDown、GroupBox、ColorDialog、OpenFileDialog、ContextMenuStrip控件。控件布局如下所示:
设置相应控件的相关属性,如下表所示(空则不用修改):
在解决方案资源管理器中添加ArcGIS Engine的ESRI.ArcGIS.Geodatabase引用,在SymbolSelectorFrm.cs文件中添加如下引用代码:
using ESRI.ArcGIS.Carto;
using ESRI.ArcGIS.Display;
using ESRI.ArcGIS.esriSystem;
using ESRI.ArcGIS.SystemUI;
using ESRI.ArcGIS.Controls;
using ESRI.ArcGIS.Geodatabase;
修改SymbolSelectorFrm的构造函数,传入图层和图例接口。代码如下:
/// <summary>
/// 构造函数,初始化全局变量
/// </summary>
/// <param name="tempLegendClass">TOC图例</param>
/// <param name="tempLayer">图层</param>
public SymbolSelectorFrm(ILegendClass tempLegendClass, ILayer tempLayer)
{
InitializeComponent();
this.pLegendClass = tempLegendClass;
this.pLayer = tempLayer;
}
添加SymbolControl的SymbologyStyleClass设置函数SetFeatureClassStyle(),代码如下:
/// <summary>
/// 初始化SymbologyControl的StyleClass,图层如果已有符号,则把符号添加到SymbologyControl中的第一个符号,并选中
/// </summary>
/// <param name="symbologyStyleClass"></param>
private void SetFeatureClassStyle(esriSymbologyStyleClass symbologyStyleClass)
{
this.axSymbologyControl.StyleClass = symbologyStyleClass;
ISymbologyStyleClass pSymbologyStyleClass = this.axSymbologyControl.GetStyleClass(symbologyStyleClass);
if (this.pLegendClass != null)
{
IStyleGalleryItem currentStyleGalleryItem = new ServerStyleGalleryItem();
currentStyleGalleryItem.Name = "当前符号";
currentStyleGalleryItem.Item = pLegendClass.Symbol;
pSymbologyStyleClass.AddItem(currentStyleGalleryItem,0);
this.pStyleGalleryItem = currentStyleGalleryItem;
}
pSymbologyStyleClass.SelectItem(0);
}
添加注册表读取函数ReadRegistry(),此函数从注册表中读取ArcGIS的安装路径,代码如下:
/// <summary>
/// 从注册表中取得指定软件的路径
/// </summary>
/// <param name="sKey"></param>
/// <returns></returns>
private string ReadRegistry(string sKey)
{
//Open the subkey for reading
Microsoft.Win32.RegistryKey rk = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(sKey);
if (rk == null) return "";
// Get the data from a specified item in the key.
return (string)rk.GetValue("InstallDir");
}
添加SymbolSelectorFrm的Load事件。根据图层类型为SymbologyControl导入相应的符号样式文件,如点、线、面,并设置控件的可视性。代码如下:
private void SymbolSelectorFrm_Load(object sender, EventArgs e)
{
//-----------------------------------------------------------------------------------
//取得ArcGIS安装路径
//string sInstall = ReadRegistry("SOFTWARE\\ESRI\\ArcGIS License Manager 10.0");
//载入ESRI.ServerStyle文件到SymbologyControl
this.axSymbologyControl.LoadStyleFile(sInstall + "\\Styles\\ESRI.ServerStyle");
//-----------------------------------------------------------------------------------
this.axSymbologyControl.LoadStyleFile("D:\\Program Files\\ArcGIS\\Engine10.0\\Styles\\ESRI.ServerStyle");//这个地方有点问题,如果用以上代码也能获取到路径名,但是经调试发现执行会调用mapcontrol的onafterdraw函数(以前用于地图联动),而进入循环,无法调出符号选择器,故改为这段代码。望高手解决。
//确定图层的类型(点线面),设置好SymbologyControl的StyleClass,设置好各控件的可见性(visible)
IGeoFeatureLayer pGeoFeatureLayer = (IGeoFeatureLayer)pLayer;
switch (((IFeatureLayer)pLayer).FeatureClass.ShapeType)
{
case ESRI.ArcGIS.Geometry.esriGeometryType.esriGeometryPoint:
this.SetFeatureClassStyle(esriSymbologyStyleClass.esriStyleClassMarkerSymbols);
this.lblAngle.Visible = true;
this.nudAngle.Visible = true;
this.lblSize.Visible = true;
this.nudSize.Visible = true;
this.lblWidth.Visible = false;
this.nudWidth.Visible = false;
this.lblOutlineColor.Visible = false;
this.btnOutlineColor.Visible = false;
break;
case ESRI.ArcGIS.Geometry.esriGeometryType.esriGeometryPolyline:
this.SetFeatureClassStyle(esriSymbologyStyleClass.esriStyleClassLineSymbols);
this.lblAngle.Visible = false;
this.nudAngle.Visible = false;
this.lblSize.Visible = false;
this.nudSize.Visible = false;
this.lblWidth.Visible = true;
this.nudWidth.Visible = true;
this.lblOutlineColor.Visible = false;
this.btnOutlineColor.Visible = false;
break;
case ESRI.ArcGIS.Geometry.esriGeometryType.esriGeometryPolygon:
this.SetFeatureClassStyle(esriSymbologyStyleClass.esriStyleClassFillSymbols);
this.lblAngle.Visible = false;
this.nudAngle.Visible = false;
this.lblSize.Visible = false;
this.nudSize.Visible = false;
this.lblWidth.Visible = true;
this.nudWidth.Visible = true;
this.lblOutlineColor.Visible = true;
this.btnOutlineColor.Visible = true;
break;
case ESRI.ArcGIS.Geometry.esriGeometryType.esriGeometryMultiPatch:
this.SetFeatureClassStyle(esriSymbologyStyleClass.esriStyleClassFillSymbols);
this.lblAngle.Visible = false;
this.nudAngle.Visible = false;
this.lblSize.Visible = false;
this.nudSize.Visible = false;
this.lblWidth.Visible = true;
this.nudWidth.Visible = true;
this.lblOutlineColor.Visible = true;
this.btnOutlineColor.Visible = true;
break;
default:
this.Close();
break;
}
}
双击确定按钮和取消按钮,分别添加如下代码:
/// <summary>
/// 确定按钮
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnOK_Click(object sender, EventArgs e)
{
//取得选定的符号
this.pSymbol = (ISymbol)pStyleGalleryItem.Item;
//更新预览图像
this.pSymbolImage = this.ptbPreview.Image;
//关闭窗体
this.Close();
}
private void btnCancel_Click(object sender, EventArgs e)
{
this.Close();
}
为了操作上的方便,我们添加SymbologyControl的DoubleClick事件,当双击符号时同按下确定按钮一样,选定符号并关闭符号选择器窗体。代码如下:
private void axSymbologyControl_OnDoubleClick(object sender, ISymbologyControlEvents_OnDoubleClickEvent e)
{
this.btnOK.PerformClick();
}
再添加符号预览函数,当用户选定某一符号时,符号可以显示在PictureBox控件中,方便预览,函数代码如下:
/// <summary>
/// 把选中并设置好的符号在picturebox控件中预览
/// </summary>
private void PreviewImage()
{
stdole.IPictureDisp picture = this.axSymbologyControl.GetStyleClass(this.axSymbologyControl.StyleClass).PreviewItem(pStyleGalleryItem, this.ptbPreview.Width, this.ptbPreview.Height);
System.Drawing.Image image = System.Drawing.Image.FromHbitmap(new System.IntPtr(picture.Handle));
this.ptbPreview.Image = image;
}
当SymbologyControl的样式改变时,需要重新设置符号参数调整控件的可视性,故要添加SymbologyControl的OnStyleClassChanged事件,事件代码与Load事件类似,如下:
/// <summary>
/// 当样式(Style)改变时,重新设置符号类型和控件的可视性
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void axSymbologyControl_OnStyleClassChanged(object sender, ESRI.ArcGIS.Controls.ISymbologyControlEvents_OnStyleClassChangedEvent e)
{
switch ((esriSymbologyStyleClass)(e.symbologyStyleClass))
{
case esriSymbologyStyleClass.esriStyleClassMarkerSymbols:
this.lblAngle.Visible = true;
this.nudAngle.Visible = true;
this.lblSize.Visible = true;
this.nudSize.Visible = true;
this.lblWidth.Visible = false;
this.nudWidth.Visible = false;
this.lblOutlineColor.Visible = false;
this.btnOutlineColor.Visible = false;
break;
case esriSymbologyStyleClass.esriStyleClassLineSymbols:
this.lblAngle.Visible = false;
this.nudAngle.Visible = false;
this.lblSize.Visible = false;
this.nudSize.Visible = false;
this.lblWidth.Visible = true;
this.nudWidth.Visible = true;
this.lblOutlineColor.Visible = false;
this.btnOutlineColor.Visible = false;
break;
case esriSymbologyStyleClass.esriStyleClassFillSymbols:
this.lblAngle.Visible = false;
this.nudAngle.Visible = false;
this.lblSize.Visible = false;
this.nudSize.Visible = false;
this.lblWidth.Visible = true;
this.nudWidth.Visible = true;
this.lblOutlineColor.Visible = true;
this.btnOutlineColor.Visible = true;
break;
}
}
通过以上操作,本符号选择器雏形已经完成,我们可以3sdnMap主窗体中调用并进行测试。如果您已经完成“直接调用ArcMap中的符号选择器”这一节,请注释axTOCControl_OnDoubleClick事件响应函数里的代码,并添加如下代码。如果您是直接学习自定义符号选择器这一节的,请先添加axTOCControl1控件的OnDoubleClick事件,再添加如下事件响应函数代码:
/// <summary>
/// 双击TOCControl控件时触发的事件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void axTOCControl1_OnDoubleClick(object sender, ITOCControlEvents_OnDoubleClickEvent e)
{
esriTOCControlItem itemType = esriTOCControlItem.esriTOCControlItemNone;
IBasicMap basicMap = null;
ILayer layer = null;
object unk = null;
object data = null;
axTOCControl1.HitTest(e.x, e.y, ref itemType, ref basicMap, ref layer, ref unk, ref data);
if (e.button == 1)
{
if (itemType == esriTOCControlItem.esriTOCControlItemLegendClass)
{
//取得图例
ILegendClass pLegendClass = ((ILegendGroup)unk).get_Class((int)data);
//创建符号选择器SymbolSelector实例
SymbolSelectorFrm SymbolSelectorFrm = new SymbolSelectorFrm(pLegendClass, layer);
if (SymbolSelectorFrm.ShowDialog() == DialogResult.OK)
{
//局部更新主Map控件
m_mapControl.ActiveView.PartialRefresh(esriViewDrawPhase.esriViewGeography, null, null);
//设置新的符号
pLegendClass.Symbol = SymbolSelectorFrm.pSymbol;
//更新主Map控件和图层控件
this.axMapControl1.ActiveView.Refresh();
this.axTOCControl1.Refresh();
}
}
}
}
按F5编译运行,相信你已经看到自己新手打造的符号选择器已经出现在眼前了,但还有些功能没实现。当然,它还比较简陋,下面我们将一起把它做得更完美些。在地图整饰中,符号参数的调整是必须的功能。下面我们将实现符号颜色、外框颜色、线宽、角度等参数的调整。添加SymbologyControl的OnItemSelected事件,此事件在鼠标选中符号时触发,此时显示出选定符号的初始参数,事件响应函数代码如下:
/// <summary>
/// 选中符号时触发的事件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void axSymbologyControl_OnItemSelected(object sender, ESRI.ArcGIS.Controls.ISymbologyControlEvents_OnItemSelectedEvent e)
{
pStyleGalleryItem = (IStyleGalleryItem)e.styleGalleryItem;
Color color;
switch (this.axSymbologyControl.StyleClass)
{
//点符号
case esriSymbologyStyleClass.esriStyleClassMarkerSymbols:
color = this.ConvertIRgbColorToColor(((IMarkerSymbol)pStyleGalleryItem.Item).Color as IRgbColor);
//设置点符号角度和大小初始值
this.nudAngle.Value = (decimal)((IMarkerSymbol)this.pStyleGalleryItem.Item).Angle;
this.nudSize.Value = (decimal)((IMarkerSymbol)this.pStyleGalleryItem.Item).Size;
break;
//线符号
case esriSymbologyStyleClass.esriStyleClassLineSymbols:
color = this.ConvertIRgbColorToColor(((ILineSymbol)pStyleGalleryItem.Item).Color as IRgbColor);
//设置线宽初始值
this.nudWidth.Value = (decimal)((ILineSymbol)this.pStyleGalleryItem.Item).Width;
break;
//面符号
case esriSymbologyStyleClass.esriStyleClassFillSymbols:
color = this.ConvertIRgbColorToColor(((IFillSymbol)pStyleGalleryItem.Item).Color as IRgbColor);
this.btnOutlineColor.BackColor = this.ConvertIRgbColorToColor(((IFillSymbol)pStyleGalleryItem.Item).Outline.Color as IRgbColor);
//设置外框线宽度初始值
this.nudWidth.Value = (decimal)((IFillSymbol)this.pStyleGalleryItem.Item).Outline.Width;
break;
default:
color = Color.Black;
break;
}
//设置按钮背景色
this.btnColor.BackColor = color;
//预览符号
this.PreviewImage();
}
调整点符号的大小。添加nudSize控件的ValueChanged事件,即在控件的值改变时响应此事件,然后重新设置点符号的大小。代码如下:
private void nudSize_ValueChanged(object sender, EventArgs e)
{
((IMarkerSymbol)this.pStyleGalleryItem.Item).Size = (double)this.nudSize.Value;
this.PreviewImage();
}
调整点符号的角度,添加nudAngle控件的ValueChanged事件,以重新设置点符号的角度。代码如下:
private void nudAngle_ValueChanged(object sender, EventArgs e)
{
((IMarkerSymbol)this.pStyleGalleryItem.Item).Angle = (double)this.nudAngle.Value;
this.PreviewImage();
}
调整线符号和面符号的线宽,添加nudWidth控件的ValueChanged事件,以重新设置线符号的线宽和面符号的外框线的线宽。代码如下:
private void nudWidth_ValueChanged(object sender, EventArgs e)
{
switch (this.axSymbologyControl.StyleClass)
{
case esriSymbologyStyleClass.esriStyleClassLineSymbols:
((ILineSymbol)this.pStyleGalleryItem.Item).Width = Convert.ToDouble(this.nudWidth.Value);
break;
case esriSymbologyStyleClass.esriStyleClassFillSymbols:
//取得面符号的轮廓线符号
ILineSymbol pLineSymbol = ((IFillSymbol)this.pStyleGalleryItem.Item).Outline;
pLineSymbol.Width = Convert.ToDouble(this.nudWidth.Value);
((IFillSymbol)this.pStyleGalleryItem.Item).Outline = pLineSymbol;
break;
}
this.PreviewImage();
}
颜色转换,在ArcGIS Engine中,颜色由IRgbColor接口实现,而在.NET框架中,颜色则由Color结构表示。故在调整颜色参数之前,我们必须完成以上两种不同颜色表示方式的转换。关于这两种颜色结构的具体信息,请大家自行查阅相关资料。下面添加两个颜色转换函数。
ArcGIS Engine中的IRgbColor接口转换至.NET中的Color结构的函数:
/// <summary>
/// 将ArcGIS Engine中的IRgbColor接口转换至.NET中的Color结构
/// </summary>
/// <param name="pRgbColor">IRgbColor</param>
/// <returns>.NET中的System.Drawing.Color结构表示ARGB颜色</returns>
public Color ConvertIRgbColorToColor(IRgbColor pRgbColor)
{
return ColorTranslator.FromOle(pRgbColor.RGB);
}
.NET中的Color结构转换至于ArcGIS Engine中的IColor接口的函数:
/// <summary>
/// 将.NET中的Color结构转换至于ArcGIS Engine中的IColor接口
/// </summary>
/// <param name="color">.NET中的System.Drawing.Color结构表示ARGB颜色</param>
/// <returns>IColor</returns>
public IColor ConvertColorToIColor(Color color)
{
IColor pColor = new RgbColorClass();
pColor.RGB = color.B * 65536 + color.G * 256 + color.R;
return pColor;
}
调整所有符号的颜色。选择颜色时,我们调用.NET的颜色对话框ColorDialog,选定颜色后,修改颜色按钮的背景色为选定的颜色,以方便预览。双击btnColor按钮,添加如下代码:
private void btnColor_Click(object sender, EventArgs e)
{
//调用系统颜色对话框
if (this.colorDialog.ShowDialog() == DialogResult.OK)
{
//将颜色按钮的背景颜色设置为用户选定的颜色
this.btnColor.BackColor = this.colorDialog.Color;
//设置符号颜色为用户选定的颜色
switch (this.axSymbologyControl.StyleClass)
{
//点符号
case esriSymbologyStyleClass.esriStyleClassMarkerSymbols:
((IMarkerSymbol)this.pStyleGalleryItem.Item).Color = this.ConvertColorToIColor(this.colorDialog.Color);
break;
//线符号
case esriSymbologyStyleClass.esriStyleClassLineSymbols:
((ILineSymbol)this.pStyleGalleryItem.Item).Color = this.ConvertColorToIColor(this.colorDialog.Color);
break;
//面符号
case esriSymbologyStyleClass.esriStyleClassFillSymbols:
((IFillSymbol)this.pStyleGalleryItem.Item).Color = this.ConvertColorToIColor(this.colorDialog.Color);
break;
}
//更新符号预览
this.PreviewImage();
}
}
调整面符号的外框线颜色。同上一步类似,双击btnOutlineColor按钮,添加如下代码:
private void btnOutlineColor_Click(object sender, EventArgs e)
{
if (this.colorDialog.ShowDialog() == DialogResult.OK)
{
//取得面符号中的外框线符号
ILineSymbol pLineSymbol = ((IFillSymbol)this.pStyleGalleryItem.Item).Outline;
//设置外框线颜色
pLineSymbol.Color = this.ConvertColorToIColor(this.colorDialog.Color);
//重新设置面符号中的外框线符号
((IFillSymbol)this.pStyleGalleryItem.Item).Outline = pLineSymbol;
//设置按钮背景颜色
this.btnOutlineColor.BackColor = this.colorDialog.Color;
//更新符号预览
this.PreviewImage();
}
}
至此,你可以编译运行程序,看看效果如何,是不是感觉很不错了?我们已经能够修改符号的参数,自定义符号了。但是,SymbologyControl默认加载的是ESRI.ServerStyle文件的样式,用过ArcMap的你可能已经注意到,ArcMap中的Symbol Selector有一个“More Symbols”按钮,可以加载其它的符号和ServerStyle文件。
还记得我们在开始的时候添加了ContextMenuStrip控件吗?现在它终于派上用场了。我们要实现的功能是:单击“更多符号”弹出菜单(ContextMenu),菜单中列出了ArcGIS自带的其它符号,勾选相应的菜单项就可以在SymbologyControl中增加相应的符号。在菜单的最后一项是“添加符号”,选择这一项时,将弹出打开文件对话框,我们可以由此选择其它的ServerStyle文件,以加载更多的符号。
定义全局变量,在SymbolSelectorFrm中定义如下全局变量,用于判断菜单是否已经初始化。
//菜单是否已经初始化标志
bool contextMenuMoreSymbolInitiated = false;
双击“更多符号”按钮,添加Click事件。在此事件响应函数中,我们要完成ServerStyle文件的读取,将其文件名作为菜单项名称生成菜单并显示菜单。代码如下:
/// <summary>
/// “更多符号”按下时触发的事件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnMoreSymbol_Click(object sender, EventArgs e)
{
if (this.contextMenuMoreSymbolInitiated == false)
{
//string sInstall = ReadRegistry("SOFTWARE\\ESRI\\CoreRuntime");
//string path = System.IO.Path.Combine(sInstall, "Styles");
string sInstall = "";
string path = System.IO.Path.Combine(sInstall, "D:\\Program Files\\ArcGIS\\Engine10.0\\Styles");
//取得菜单项数量
string[] styleNames = System.IO.Directory.GetFiles(path, "*.ServerStyle");
ToolStripMenuItem[] symbolContextMenuItem = new ToolStripMenuItem[styleNames.Length + 1];
//循环添加其它符号菜单项到菜单
for (int i = 0; i < styleNames.Length; i++)
{
symbolContextMenuItem[i] = new ToolStripMenuItem();
symbolContextMenuItem[i].CheckOnClick = true;
symbolContextMenuItem[i].Text = System.IO.Path.GetFileNameWithoutExtension(styleNames[i]);
if (symbolContextMenuItem[i].Text == "ESRI")
{
symbolContextMenuItem[i].Checked = true;
}
symbolContextMenuItem[i].Name = styleNames[i];
}
//添加“更多符号”菜单项到菜单最后一项
symbolContextMenuItem[styleNames.Length] = new ToolStripMenuItem();
symbolContextMenuItem[styleNames.Length].Text = "添加符号";
symbolContextMenuItem[styleNames.Length].Name = "AddMoreSymbol";
//添加所有的菜单项到菜单
this.contextMenuStrip.Items.AddRange(symbolContextMenuItem);
this.contextMenuMoreSymbolInitiated = true;
}
//显示菜单
this.contextMenuStrip.Show(this.btnMoreSymbol.Location);
}
添加contextMenuStripMoreSymbol控件的ItemClicked事件。单击某一菜单项时响应ItemClicked事件,将选中的ServerStyle文件导入到SymbologyControl中并刷新。当用户单击“添加符号”菜单项时,弹出打开文件对话框,供用户选择其它的ServerStyle文件。代码如下:
/// <summary>
/// “更多符号”按钮弹出的菜单项单击事件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void contextMenuStrip_ItemClicked(object sender, ToolStripItemClickedEventArgs e)
{
ToolStripMenuItem pToolStripMenuItem = (ToolStripMenuItem)e.ClickedItem;
//如果单击的是“添加符号”
if (pToolStripMenuItem.Name == "AddMoreSymbol")
{
//弹出打开文件对话框
if (this.openFileDialog.ShowDialog() == DialogResult.OK)
{
//导入style file到SymbologyControl
this.axSymbologyControl.LoadStyleFile(this.openFileDialog.FileName);
//刷新axSymbologyControl控件
this.axSymbologyControl.Refresh();
}
}
else//如果是其它选项
{
if (pToolStripMenuItem.Checked == false)
{
this.axSymbologyControl.LoadStyleFile(pToolStripMenuItem.Name);
this.axSymbologyControl.Refresh();
}
else
{
this.axSymbologyControl.RemoveFile(pToolStripMenuItem.Name);
this.axSymbologyControl.Refresh();
}
}
}
相信你已经盼这一步很久了吧,按照惯例,按下F5吧!大功造成。
11图层文本标注
这一讲给大家讲解图层标注的实现方法。图层标注实现起来并不复杂,本例仅做一个简单示范,只加载AE的样式库,标注选定的字段,旨在抛砖引玉。更高级的功能,如自定义样式和修改样式,由读者自己实现。主要思路: 加载图层字段 –> 加载文本样式 -> 设置文本样式。实现过程: 创建标注设置窗体 -> 创建图层标注的Command -> 添加Command到图层右键菜单。
添加一个Windows窗体,命名为LabelLayerFrm.cs。添加控件如下:
为LabelLayerFrm类添加两个成员变量:
public ILayer pLayer;
private IStyleGalleryItem pStyleGalleryItem;
重载一个构造函数:
public LabelLayerFrm(ILayer layer)
{
InitializeComponent();
pLayer = layer;
}
添加成员函数ReadRegistry,用于从注册表中读取ArcGIS的安装路径。
/// <summary>
/// 读取注册表中的制定软件的路径
/// </summary>
/// <param name="sKey"></param>
/// <returns></returns>
private string ReadRegistry(string sKey)
{
//Open the subkey for reading
Microsoft.Win32.RegistryKey rk = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(sKey, true);
if (rk == null) return "";
// Get the data from a specified item in the key.
return (string)rk.GetValue("InstallDir");
}
添加LabelLayerFrm窗体的Load事件,以加载图层字段到下拉模型,加载文本样式到SymbologyControl控件。
private void LabelLayerFrm_Load(object sender, EventArgs e)
{
//加载图层字段
ITable pTable = pLayer as ITable;
IField pField = null;
for (int i = 0; i < pTable.Fields.FieldCount; i++)
{
pField = pTable.Fields.get_Field(i);
cbbField.Items.Add(pField.AliasName);
}
cbbField.SelectedIndex = 0;
//获得ArcGIS的安装路径
//string sInstall = ReadRegistry("SOFTWARE\\ESRI\\CoreRuntime");
//加载ESRI.ServerStyle 样式文件到SymbologyControl
this.axSymbologyControl.LoadStyleFile("D:\\Program Files\\ArcGIS\\Engine10.0\\Styles\\ESRI.ServerStyle");
this.axSymbologyControl.GetStyleClass(esriSymbologyStyleClass.esriStyleClassTextSymbols).SelectItem(0);//这个地方有错误,SelectItem选择后不能调出涂层文本标注框
//this.axSymbologyControl.GetStyleClass(esriSymbologyStyleClass.esriStyleClassTextSymbols);//改成这句也不行,能调出对话框但是样式选择,后面代码就不能正确执行
//综上问题还是不能解决,楼主能力有限,姑且认为上一行的代码正确,只是为AE的bug
}
添加axSymbologyControl1控件的OnItemSelected事件,以设置选定的样式。
private void axSymbologyControl1_OnItemSelected (object sender, ISymbologyControlEvents_OnItemSelectedEvent e)
{
pStyleGalleryItem = (IStyleGalleryItem)e.styleGalleryItem;
}
添加确定按扭的Click事件,为选定图层中的选定的字段以选定的样式标注。
private void btnOK_Click(object sender, EventArgs e)
{
IGeoFeatureLayer pGeoFeatureLayer = pLayer as IGeoFeatureLayer;
pGeoFeatureLayer.AnnotationProperties.Clear();//必须执行,因为里面有一个默认的
IBasicOverposterLayerProperties pBasic = new BasicOverposterLayerPropertiesClass();
ILabelEngineLayerProperties pLableEngine = new LabelEngineLayerPropertiesClass();
ITextSymbol pTextSymbol = new TextSymbolClass();
pTextSymbol = (ITextSymbol)pStyleGalleryItem.Item;
//你可以在这里修改样式的颜色和字体等属性,本文从略
//pTextSymbol.Color
//pTextSymbol.Font
string pLable = "[" + (string)cbbField .SelectedItem + "]";
pLableEngine.Expression = pLable;
pLableEngine.IsExpressionSimple = true;
pBasic.NumLabelsOption = esriBasicNumLabelsOption.esriOneLabelPerShape;
pLableEngine.BasicOverposterLayerProperties = pBasic;
pLableEngine.Symbol = pTextSymbol;
pGeoFeatureLayer.AnnotationProperties.Add(pLableEngine as IAnnotateLayerProperties);
pGeoFeatureLayer.DisplayAnnotation = true;
}
至此,标注设置窗体已经完成,如果你编译通不过,看看是不是忘了添加相关引用了。
创建一个新类,以ArcGIS的BaseCommand为模板,命名为LabelLayerCmd.cs。注意:在新建Base Command模板时,会弹出一个对话框让我们选择模板适用对象,这时我们要选择MapControl、PageLayoutControl,即选择第二项或者倒数第二项。添加LabelLayerCmd类的成员变量。
private ILayer pLayer = null;
IMapControl3 pMap;
修改默认构造函数如下:
public LabelLayerCmd(ILayer lyr,IMapControl3 map)
{
//
// TODO: Define values for the public properties
//
base.m_category = ""; //localizable text
base.m_caption = "标注"; //localizable text
base.m_message = "标注"; //localizable text
base.m_toolTip = "标注"; //localizable text
base.m_name = "标注"; //unique id, non-localizable (e.g. "MyCategory_MyCommand")
pLayer = lyr;
pMap = map;
try
{
//
// TODO: change bitmap name if necessary
//
string bitmapResourceName = GetType().Name + ".bmp";
base.m_bitmap = new Bitmap(GetType(), bitmapResourceName);
}
catch (Exception ex)
{
System.Diagnostics.Trace.WriteLine(ex.Message, "Invalid Bitmap");
}
}
修改OnClick函数为:
/// <summary>
/// Occurs when this command is clicked
/// </summary>
public override void OnClick()
{
// TODO: Add LabelLayerCmd.OnClick implementation
LabelLayerFrm labelLyrFrm = new LabelLayerFrm(pLayer);
labelLyrFrm.ShowDialog();
pMap.Refresh(esriViewDrawPhase.esriViewGraphics, null, null);
}
回到主窗体类,找到axTOCControl1_OnMouseDown事件响应函数,修改如下代码片断:
//弹出右键菜单
if (item == esriTOCControlItem.esriTOCControlItemMap)
m_menuMap.PopupMenu(e.x, e.y, m_tocControl.hWnd);
if (item == esriTOCControlItem.esriTOCControlItemLayer)
{
m_menuLayer.AddItem(new OpenAttributeTable(layer), -1, 2, true , esriCommandStyles.esriCommandStyleTextOnly);
//动态添加图层标注的Command到图层右键菜单
m_menuLayer.AddItem(new LabelLayerCmd(layer, m_mapControl), -1, 3, false, esriCommandStyles.esriCommandStyleTextOnly);
//弹出图层右键菜单
m_menuLayer.PopupMenu(e.x, e.y, m_tocControl.hWnd);
//移除菜单项
m_menuLayer.Remove(3);
m_menuLayer.Remove(2);
}
至此,已经完成图层文本标注,编译运行吧。