ArcGIS Engine+C#_属性数据表的查询显示
2012 年 06 月 19 日 星期二 14:33
本讲的思路大体如下:首先根据图层属性中的字段创建一个空的 DataTable,然后根据数据
内容一行行填充 DataTable 数据,再将 DataTable 绑定到 DataGridView 控件,最后调用并
显示属性表窗体。
1.创建属性表窗体
新建一个 Windows 窗体,命名为“AttributeTableFrm.cs”。
从工具箱拖一个 DataGridView 控件到窗体,并将其 Dock 属性设置为“Fill”。
添加如下引用:
using ESRI.ArcGIS.Carto;
using ESRI.ArcGIS.Controls;
using ESRI.ArcGIS.esriSystem;
using ESRI.ArcGIS.SystemUI;
using ESRI.ArcGIS.Geometry;
using ESRI.ArcGIS.Geodatabase;
2.创建空 DataTable
首先传入 ILayer,再查询到 ITable,从 ITable 中的 Fileds 中获得每个 Field,再根据 Filed
设置 DataTable 的 DataColumn,由此创建一个只含图层字段的空 DataTable。实现函数如下:
///
/// 根据图层字段创建一个只含字段的空 DataTable
///
///
///
///
private static DataTable CreateDataTableByLayer(ILayer pLayer, string tableName)
{
//创建一个 DataTable 表
DataTable pDataTable = new DataTable(tableName);
//取得 ITable 接口
ITable pTable = pLayer as ITable;
IField pField = null;
DataColumn pDataColumn;
//根据每个字段的属性建立 DataColumn 对象
for (int i = 0; i < pTable.Fields.FieldCount; i++)
{
pField = pTable.Fields.get_Field(i);
//新建一个 DataColumn 并设置其属性
pDataColumn = new DataColumn(pField.Name);
if (pField.Name == pTable.OIDFieldName)
{
pDataColumn.Unique = true;//字段值是否唯一
}
//字段值是否允许为空
pDataColumn.AllowDBNull = pField.IsNullable;
//字段别名
pDataColumn.Caption = pField.AliasName;
//字段数据类型
pDataColumn.DataType = System.Type.GetType(ParseFieldType(pField.Type));
//字段默认值
pDataColumn.DefaultValue = pField.DefaultValue;
//当字段为 String 类型是设置字段长度
if (pField.VarType == 8)
{
pDataColumn.MaxLength = pField.Length;
}
//字段添加到表中
pDataTable.Columns.Add(pDataColumn);
pField = null;
pDataColumn = null;
}
return pDataTable;
}
因为 GeoDatabase 的数据类型与.NET 的数据类型不同,故要进行转换。转换函数如下:
///
/// 将 GeoDatabase 字段类型转换成.Net 相应的数据类型
///
///
字段类型
///
public static string ParseFieldType(esriFieldType fieldType)
{
switch (fieldType)
{
case esriFieldType.esriFieldTypeBlob:
return "System.String";
case esriFieldType.esriFieldTypeDate:
return "System.DateTime";
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";
}
}
3.装载 DataTable 数据
从上一步得到的 DataTable 还没有数据,只有字段信息。因此,我们要通过 ICursor 从 ITable
中逐一取出每一行数据,即 IRow。再创建 DataTable 中相应的 DataRow,根据 IRow 设置
DataRow 信息,再将所有的 DataRow 添加到 DataTable 中,就完成了 DataTable 数据的装载。
为保证效率,一次最多只装载 2000 条数据到 DataGridView。函数代码如下:
///
/// 填充 DataTable 中的数据
///
///
///
///
public static DataTable CreateDataTable(ILayer pLayer, string tableName)
{
//创建空 DataTable
DataTable pDataTable = CreateDataTableByLayer(pLayer, tableName);
//取得图层类型
string shapeType = getShapeType(pLayer);
//创建 DataTable 的行对象
DataRow pDataRow = null;
//从 ILayer 查询到 ITable
ITable pTable = pLayer as ITable;
ICursor pCursor = pTable.Search(null, false);
//取得 ITable 中的行信息
IRow pRow = pCursor.NextRow();
int n = 0;
while (pRow != null)
{
//新建 DataTable 的行对象
pDataRow = pDataTable.NewRow();
for (int i = 0; i < pRow.Fields.FieldCount; i++)
{
//如果字段类型为 esriFieldTypeGeometry,则根据图层类型设置字段值
if (pRow.Fields.get_Field(i).Type == esriFieldType.esriFieldTypeGeometry)
{
pDataRow[i] = shapeType;
}
//当图层类型为 Anotation 时,要素类中会有 esriFieldTypeBlob 类型的数据,
//其存储的是标注内容,如此情况需将对应的字段值设置为 Element
else if (pRow.Fields.get_Field(i).Type == esriFieldType.esriFieldTypeBlob)
{
pDataRow[i] = "Element";
}
else
{
pDataRow[i] = pRow.get_Value(i);
}
}
//添加 DataRow 到 DataTable
pDataTable.Rows.Add(pDataRow);
pDataRow = null;
n++;
//为保证效率,一次只装载最多条记录
if (n == 2000)
{
pRow = null;
}
else
{
pRow = pCursor.NextRow();
}
}
return pDataTable;
}
上面的代码中涉及到一个获取图层类型的函数 getShapeTape,此函数是通过 ILayer 判断图
层类型的,代码如下:
///
/// 获得图层的 Shape 类型
///
///
图层
///
public static string getShapeType(ILayer pLayer)
{
IFeatureLayer pFeatLyr = (IFeatureLayer)pLayer;
switch (pFeatLyr.FeatureClass.ShapeType)
{
case esriGeometryType.esriGeometryPoint:
return "Point";
case esriGeometryType.esriGeometryPolyline:
return "Polyline";
case esriGeometryType.esriGeometryPolygon:
return "Polygon";
default:
return "";
}
}
4.绑定 DataTable 到 DataGridView
通过以上步骤,我们已经得到了一个含有图层属性数据的 DataTable。现定义一个
AttributeTableFrm 类的成员变量:
public DataTable attributeTable;
通过以下函数,我们很容易将其绑定到 DataGridView 控件中。
///
/// 绑定 DataTable 到 DataGridView
///
///
public void CreateAttributeTable(ILayer player)
{
string tableName;
tableName = getValidFeatureClassName(player .Name );
attributeTable = CreateDataTable(player,tableName );
this.dataGridView1 .DataSource = attributeTable ;
this.Text = "属性表[" + tableName + "] " + "记录数:
"+attributeTable.Rows.Count .ToString();
}
因为 DataTable 的表名不允许含有“.”,因此我们用“_”替换。函数如下:
///
/// 替换数据表名中的点
///
///
///
public static string getValidFeatureClassName(string FCname)
{
int dot = FCname.IndexOf(".");
if (dot != -1)
{
return FCname.Replace(".", "_");
}
return FCname;
}
5.调用属性表窗体
通过 1-4 步骤,我们封装了一个 AttributeTableFrm 类,此类能够由 ILayer 显示图层中的
属性数据。那怎么调用 AttributeTableFrm 呢?
前面已经提到,我们是在 TOCControl 选中图层的右键菜单中弹出属性表窗体的,因此我们
需要添加一个菜单项到 TOCControl 中 Layer 的右键菜单。而在第六讲中,我们采用的是 AE
中的 IToolbarMenu 实现右键菜单的,故我们还需自定义一个 Command,实现打开属性表的
功能。
以 ArcGIS 的 Base Command 为模板新建项“OpenAttributeTable.cs”。
注意:新建 Base Command 模板时,会弹出一个对话框让我们选择模板适用对象,这时我们
要选择 MapControl、PageLayoutControl,即选择第二项或者倒数第二项。
添加如下引用:
using ESRI.ArcGIS.Carto;
using ESRI.ArcGIS.Display;
using ESRI.ArcGIS.esriSystem;
添加成员变量:
private ILayer m_pLayer;
修改构造函数为:
public OpenAttributeTable(ILayer pLayer)
{
//
// 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")
m_pLayer = pLayer;
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");
}
}
再在 On_Click 函数中添加如下代码,以创建并打开属性表窗体。
///
/// Occurs when this command is clicked
///
public override void OnClick()
{
// TODO: Add OpenAttributeTable.OnClick implementation
AttributeTableFrm attributeTable = new AttributeTableFrm();
attributeTable.CreateAttributeTable(m_pLayer);
attributeTable.ShowDialog();
}
至此,我们完成了 OpenAttributeTable 命令。显然,我们要在 TOCControl 的 OnMouseDown
事件中调用此命令。
因为,当前选中的图层参数,即 ILayer 是通过 OpenAttributeTable 的构造函数传入的,而
选中的 ILayer 是动态变化的,所以我们无法在窗体初始化的 Form1_Load 事件中就添加
OpenAttributeTable 菜单项到右键菜单。但我们可以在 OnMouseDown 事件中动态添加
OpenAttributeTable 菜单项。
要注意的是,最后我们必须移除添加的 OpenAttributeTable 菜单项,不然每次按下右键都
会添加此菜单项,将造成右键菜单中含有多个 OpenAttributeTable 菜单项。
修改 TOCControl 的 OnMouseDown 事件的部分代码如下:
private void axTOCControl1_OnMouseDown(object sender,
ITOCControlEvents_OnMouseDownEvent e)
{
//……
//弹出右键菜单
if (item == esriTOCControlItem.esriTOCControlItemMap)
m_menuMap.PopupMenu(e.x, e.y, m_tocControl.hWnd);
if (item == esriTOCControlItem.esriTOCControlItemLayer)
{
//动态添加 OpenAttributeTable 菜单项
m_menuLayer.AddItem(new OpenAttributeTable(layer), -1, 2, true,
esriCommandStyles.esriCommandStyleTextOnly);
m_menuLayer.PopupMenu(e.x, e.y, m_tocControl.hWnd);
//移除 OpenAttributeTable 菜单项,以防止重复添加
m_menuLayer.Remove(2);
}
}