内容摘要:
本文提供利用ArcEngine10.7&VS2017设计Windows窗体实现对Mxd地图文档属性表的常用操作,显示图层属性,包括查看属性表+删除图层,同时解决“未找到类型或命名空间名”等问题的报错,往期文章:
ArcEngine入门开发第1篇:ArcEngine10.7&VS2017环境配置
本文内容接上篇。
一、开发环境
1.语言:C#
2.编程环境:Visual Studio2017
3.ArcEngine版本:10.7
二、菜单栏设计
在“工具箱”中选择“菜单和工具栏”中的“ContextMenuStrip”控件,双击该控件,将其添加至Form1窗体中,输入两个选项“查看属性表”和“删除该图层”。
点击Form1窗体中的TOCControl控件,在右端显示的属性窗口中选择其ContextMenuStrip属性为contextMenuStrip1,表示将该控件和我们刚刚添加的ContextMenuStrip控件进行绑定,在此基础上添加事件代码(后文会提到),右键TOCControl上的图层时会显示查看属性表和删除该图层的选项。
三、AttributesTable窗体设计
在解决方案资源管理器中右键该项目,选择添加→Windows窗体,将窗体名称设置为AttributesTable.cs,点击“添加”按钮,该窗体用于显示图层的属性表。
为AttributesTable窗体添加控件,在工具箱的公共控件中找到“Label”控件,将其拖动至窗体左上角位置,该控件用于显示属性表名称。在工具箱中搜索“data”,显示如图所示的“DataGridView”控件,将其拖动到窗体中,在属性窗口中将其Dock属性设置为Bottom,拉长控件使其充满整个窗体。
接下来双击AttributesTable窗体添加代码,在 public partial class Form1 : Form内添加所需变量:
string name;
public ILayer Layer;
public IFeatureLayer pFeatureLayer;
AxMapControl axMapControl;
IMap pMap;
ILayer layer;
本节添加代码后系统会显示很多错误提示,在后文我们会逐一解决。
将原构造函数修改为如下代码:
public AttributesTable(IFeatureLayer pGlobalFeatureLayer, AxMapControl axMapControl1)
{
InitializeComponent();
//窗体出现位置
this.StartPosition = FormStartPosition.Manual;
this.Location = new System.Drawing.Point(this.Left = 700, this.Top = 24);
pFeatureLayer = pGlobalFeatureLayer;
axMapControl = axMapControl1;
pMap = axMapControl.Map;
}
在如下函数中添加代码:
private void AttributesTable_Load(object sender, EventArgs e)
{
Itable2Dtable(pFeatureLayer);
}
在public partial class Form1 : Form中实现Itable2Dtable函数:
public void Itable2Dtable(IFeatureLayer pFeatureLayer)
{
if (pFeatureLayer != null && pFeatureLayer.FeatureClass != null)
{
label1.Text = pFeatureLayer.Name.ToString() + "的属性表";
name = pFeatureLayer.Name.ToString();
IFields pFields = pFeatureLayer.FeatureClass.Fields;
// 设置 DataGridView 的列数和列标题
dataGridView1.ColumnCount = pFields.FieldCount;
for (int i = 0; i < pFields.FieldCount; i++)
{
string fldName = pFields.get_Field(i).Name;
dataGridView1.Columns[i].Name = fldName;
dataGridView1.Columns[i].ValueType =
System.Type.GetType(ParseFieldType(pFields.get_Field(i).Type));
}
// 使用 FeatureCursor 获取特征
IFeatureCursor pFeatureCursor = pFeatureLayer.FeatureClass.Search(null, false);
IFeature pFeature;
// 处理每一个特征
while ((pFeature = pFeatureCursor.NextFeature()) != null)
{
string[] fldValue = new string[pFields.FieldCount];
// 获取特征的每一个字段值
for (int i = 0; i < pFields.FieldCount; i++)
{
string fldName = pFields.get_Field(i).Name;
if (fldName == pFeatureLayer.FeatureClass.ShapeFieldName)
{
fldValue[i] = Convert.ToString(pFeature.Shape.GeometryType);
}
else
{
fldValue[i] = Convert.ToString(pFeature.get_Value(i));
}
}
// 将字段值添加到 DataGridView 的行中
dataGridView1.Rows.Add(fldValue);
}
}
else
{
// 处理 pFeatureLayer 或者 pFeatureLayer.FeatureClass 为空的情况
MessageBox.Show("Feature layer or its feature class is null.");
}
}
在Itable2Dtable函数中调用了ParseFieldType函数,因此需要在public partial class Form1 : Form中实现ParseFieldType函数:
public static string ParseFieldType(esriFieldType fieldType)
{
switch (fieldType)
{
case esriFieldType.esriFieldTypeBlob:
return "System String";
case esriFieldType.esriFieldTypeDate:
return "System DataTime";
case esriFieldType.esriFieldTypeDouble:
return "System.Double";
case esriFieldType.esriFieldTypeGeometry:
return "System.String";
case esriFieldType.esriFieldTypeGlobalID:
return "System.String";
case esriFieldType.esriFieldTypeGUID:
return "System.String";
case esriFieldType.esriFieldTypeInteger:
return "System.Int32";
case esriFieldType.esriFieldTypeOID:
return "System.String";
case esriFieldType.esriFieldTypeRaster:
return "System.String";
case esriFieldType.esriFieldTypeSingle:
return "System.Single";
case esriFieldType.esriFieldTypeSmallInteger:
return "System.Int32";
case esriFieldType.esriFieldTypeString:
return "System.String";
default:
return "System.String";
}
}
设置dataGridView1的RowHeaderMouseClick事件,该事件表示用户在行标题边界内部单击时发生。返回AttributesTable窗体,点击DataGridView控件,在其属性窗口中点击第四个图标⚡,显示事件,点击第二个图标按字母排序,找到“RowHeaderMouseClick”事件,双击该事件进入AttributesTable.cs。
在dataGridView1的RowHeaderMouseClick事件中添加如下代码:
private void dataGridView1_RowHeaderMouseClick(object sender, DataGridViewCellMouseEventArgs e)
{
IQueryFilter pQuery = new QueryFilter();
IFeatureCursor pFeatCursor;
pMap.ClearSelection();
int count = this.dataGridView1.SelectedRows.Count;
string val;
string col;
col = this.dataGridView1.Columns[0].Name;
if (count == 1)
{
val = this.dataGridView1.SelectedRows[0].Cells[col].Value.ToString();
pQuery.WhereClause = col + "=" + val;
}
else
{
int i;
string str;
for (i = 0; i < count - 1; i++)
{
val = this.dataGridView1.SelectedRows[i].Cells[col].Value.ToString();
str = col + "= " + val + "OR";
pQuery.WhereClause += str;
}
val = this.dataGridView1.SelectedRows[0].Cells[col].Value.ToString();
str = col + "" + val;
pQuery.WhereClause += str;
}
try
{
ZoomToSelectedFeature(pFeatureLayer, pQuery);
pFeatCursor = pFeatureLayer.Search(pQuery, true);
IFeatureClass fcls = pFeatureLayer.FeatureClass;
IFeatureCursor featureCursor = fcls.Search(pQuery, true);
FlashPolygons(featureCursor);
}
catch (Exception ex)
{
MessageBox.Show("有错误");
}
}
在public partial class Form1 : Form中实现GetRGB函数获取颜色:
private IRgbColor GetRGB(int r, int g, int b)
{
IRgbColor pColor;
pColor = new RgbColor();
pColor.Red = r;
pColor.Green = g;
pColor.Blue = b;
return pColor;
}
在public partial class Form1 : Form中实现FlashPolygons函数使得选中要素闪烁:
private void FlashPolygons(IFeatureCursor featureCursor)
{
IArray geoArray = new ESRI.ArcGIS.esriSystem.Array();
IFeature feature = null;
while ((feature = featureCursor.NextFeature()) != null)
{
geoArray.Add(feature.ShapeCopy);
}
HookHelper m_pHookHelper = new HookHelper();
m_pHookHelper.Hook = axMapControl.Object;
IHookActions hookActions = (IHookActions)m_pHookHelper;
hookActions.DoActionOnMultiple(geoArray, esriHookActions.esriHookActionsPan);
Application.DoEvents();
m_pHookHelper.ActiveView.ScreenDisplay.UpdateWindow();
hookActions.DoActionOnMultiple(geoArray, esriHookActions.esriHookActionsFlash);
}
在public partial class Form1 : Form中实现ZoomToSelectedFeature函数进行视图转换:
private void ZoomToSelectedFeature(IFeatureLayer pFeatureLyr, IQueryFilter pQueryFilter)
{
IFeatureSelection pFeatSelection;
ISimpleMarkerSymbol pMarkerSymbol;
ISimpleLineSymbol pLineSymbol;
ISimpleFillSymbol pFillSymb;
IFeatureLayer pLayer;
IFeatureClass pFeatureCls;
for (int i = 0; i < axMapControl.LayerCount; i++)
{
ILayer lyr = axMapControl.Map.get_Layer(i);
if (lyr.Name == name)
{
layer = lyr;
pLayer = (IFeatureLayer)layer;
pFeatureCls = pLayer.FeatureClass;
switch (pFeatureCls.ShapeType)
{
case esriGeometryType.esriGeometryPoint:
pMarkerSymbol = new SimpleMarkerSymbol();
pMarkerSymbol.Style = esriSimpleMarkerStyle.esriSMSCircle;
pMarkerSymbol.Color = GetRGB(0, 255, 255);
pMarkerSymbol.Angle = 30;
pMarkerSymbol.Size = 5;
pMarkerSymbol.Outline = true;
pMarkerSymbol.OutlineSize = 1;
pMarkerSymbol.OutlineColor = GetRGB(50, 50, 50);
pFeatSelection = (IFeatureSelection)layer;
pFeatSelection.SelectFeatures(pQueryFilter, esriSelectionResultEnum.esriSelectionResultNew, false);
pFeatSelection.SetSelectionSymbol = true;
pFeatSelection.SelectionSymbol = (ISymbol)pMarkerSymbol;
ISelectionSet pSelectionSet = pFeatSelection.SelectionSet;
IEnumGeometry pEnumGeom = new EnumFeatureGeometry();
IEnumGeometryBind pEnumGeomBind = pEnumGeom as IEnumGeometryBind;
pEnumGeomBind.BindGeometrySource(null, pSelectionSet);
IGeometryFactory pGeomFactory = new GeometryEnvironmentClass();
IGeometry pGeom = pGeomFactory.CreateGeometryFromEnumerator(pEnumGeom);
axMapControl.ActiveView.Extent = pGeom.Envelope;
axMapControl.ActiveView.Refresh();
break;
case esriGeometryType.esriGeometryPolyline:
pLineSymbol = new SimpleLineSymbol();
pLineSymbol.Style = esriSimpleLineStyle.esriSLSDot; pLineSymbol.Color = GetRGB(0, 255, 255);
pLineSymbol.Width = 3;
pFeatSelection = (IFeatureSelection)layer;
pFeatSelection.SelectFeatures(pQueryFilter, esriSelectionResultEnum.esriSelectionResultNew, false);
pFeatSelection.SetSelectionSymbol = true;
pFeatSelection.SelectionSymbol = (ISymbol)pLineSymbol;
ISelectionSet pSelectionSet1 = pFeatSelection.SelectionSet;
IEnumGeometry pEnumGeom1 = new EnumFeatureGeometry();
IEnumGeometryBind pEnumGeomBind1 = pEnumGeom1 as IEnumGeometryBind;
pEnumGeomBind1.BindGeometrySource(null, pSelectionSet1);
IGeometryFactory pGeomFactory1 = new GeometryEnvironmentClass();
IGeometry pGeom1 = pGeomFactory1.CreateGeometryFromEnumerator(pEnumGeom1);
axMapControl.ActiveView.Extent = pGeom1.Envelope;
axMapControl.ActiveView.Refresh();
break;
case esriGeometryType.esriGeometryPolygon:
pFillSymb = new SimpleFillSymbol();
pFillSymb.Color = GetRGB(0, 25, 255);
pFillSymb.Style = esriSimpleFillStyle.esriSFSSolid;
pFeatSelection = (IFeatureSelection)layer;
pFeatSelection.SelectFeatures(pQueryFilter, esriSelectionResultEnum.esriSelectionResultNew, false);
pFeatSelection.SetSelectionSymbol = true;
pFeatSelection.SelectionSymbol = (ISymbol)pFillSymb;
ISelectionSet pSelectionSet2 = pFeatSelection.SelectionSet;
IEnumGeometry pEnumGeom2 = new EnumFeatureGeometry();
IEnumGeometryBind pEnumGeomBind2 = pEnumGeom2 as IEnumGeometryBind;
pEnumGeomBind2.BindGeometrySource(null, pSelectionSet2);
IGeometryFactory pGeomFactory2 = new GeometryEnvironmentClass();
IGeometry pGeom2 = pGeomFactory2.CreateGeometryFromEnumerator(pEnumGeom2);
axMapControl.ActiveView.Extent = pGeom2.Envelope;
axMapControl.ActiveView.Refresh();
break;
}
break;
}
}
}
此时AttributesTable窗体的全部函数添加完毕,此时系统会提示很多错误,这是由于缺少对部分ArcGIS指令的引用,莫慌!!在AttributesTable.cs中引用部分添加如下代码可解决部分问题:
using ESRI.ArcGIS.Carto;
using ESRI.ArcGIS.Framework;
using ESRI.ArcGIS.ArcMapUI;
using ESRI.ArcGIS.Geodatabase;
using ESRI.ArcGIS.Geometry;
using ESRI.ArcGIS.Controls;
using ESRI.ArcGIS.Display;
using ESRI.ArcGIS.esriSystem;
若仍然显示报错不存在类型或命名空间名,则右键解决方案资源管理器中“引用”,选择“添加引用”,在打开的引用管理器对话框中点击“扩展”,找到上述所有的引用并选中,点击“确定”按钮即可解决。
此时仍有错误提示如下。
在解决方案资源管理器中点击“引用”,找到“ESRI.ArcGIS.Geometry”引用,右键选择“属性”,在属性窗口中将其嵌入互操作类型设置为“False”即可解决。
四、Form1主窗体设计
点击回到Form1.cs[设计],在属性窗口的下拉框中找到contextMenuStrip1,点击该选项后窗体中显示设置过的ContextMenuStrip控件。
双击“查看属性表”按钮进入Form1.cs,在如下函数中添加代码,使得点击“查看属性表”时唤起AttributesTable窗体:
private void 查看属性表ToolStripMenuItem_Click(object sender, EventArgs e)
{
AttributesTable Ft = new AttributesTable(pGlobeLayer as IFeatureLayer, axMapControl1);
Ft.Show();
}
双击“删除该图层”按钮进入Form1.cs,在如下函数中添加代码:
private void 删除该图层ToolStripMenuItem_Click(object sender, EventArgs e)
{
if (axMapControl1.LayerCount > 0)
{
if (pGlobeLayer != null)
{
axMapControl1.Map.DeleteLayer(pGlobeLayer);
}
}
}
此时错误提示如下。
在public partial class Form1 : Form中添加如下变量声明即可解决:
private ILayer pGlobeLayer = null;
最后一步,为TOCControl控件添加OnMouseDown事件的代码,在Form1窗体中点击TOCControl控件,在其属性窗口中点击第四个图标⚡,显示事件,点击第二个图标按字母排序,找到“OnMouseDown”事件,双击该事件进入Form1.cs。
在如下函数中添加代码:
private void axTOCControl1_OnMouseDown(object sender, ESRI.ArcGIS.Controls.ITOCControlEvents_OnMouseDownEvent e)
{
if (axMapControl1.LayerCount > 0)
{
esriTOCControlItem pItem = new esriTOCControlItem();
//pGlobeLayer为全局变量
pGlobeLayer = new FeatureLayer();
IBasicMap pBasicMap = new MapClass();
object pOther = new object();
object pIndex = new object();
//获取点击的位置
axTOCControl1.HitTest(e.x, e.y, ref pItem, ref pBasicMap, ref pGlobeLayer, ref pOther, ref pIndex);
//点击的是Layer的话,则弹出上下文菜单
if (e.button == 2 && pItem == esriTOCControlItem.esriTOCControlItemLayer)
{
contextMenuStrip1.Show(axTOCControl1, e.x, e.y);
}
}
}
此时系统显示错误提示如下。
这是由于缺少对部分ArcGIS指令的引用,在Form1.cs引用部分添加如下代码可解决:
using ESRI.ArcGIS.Controls;
若仍然显示报错不存在类型或命名空间名,则右键解决方案资源管理器中“引用”,选择“添加引用”,在打开的引用管理器对话框中点击“扩展”,找到“ESRI.ArcGIS.Controls”引用并选中,点击“确定”按钮即可解决。
运行结果如下。
选中属性表中的某一行会在地图上高亮显示该要素。
欢迎交流🌹🌹