C# AE開發 TOCControl 右鍵打開圖層屬性表
致謝!
在ArcMap中,單擊圖層右鍵菜單中的“Open Attribute Table”命令,便可彈出屬性數據表。本講將完成類似的功能。
數據表顯示,我們用了DataGridView控件。DataGridView 控件提供一種強大而靈活的以表格形式顯示數據的方式。可以使用 DataGridView 控件來顯示少量數據的只讀視圖,也可以對其進行縮放以顯示特大數據集的可編輯視圖。我們可以很方便地把一個DataTable作為數據源綁定到DataGridView控件中。
本講的思路大體如下:首先根據圖層屬性中的字段創建一個空的DataTable,然后根據數據內容一行行填充DataTable數據,再將DataTable綁定到DataGridView控件,最后調用並顯示屬性表窗體。
創建屬性表窗體
新建一個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;
創建空DataTable
首先傳入ILayer,再查詢到ITable,從ITable中的Fileds中獲得每個Field,再根據Filed設置DataTable的DataColumn,由此創建一個只含圖層字段的空DataTable。實現函數如下:
/// <summary>
/// 根據圖層字段創建一個只含字段的空DataTable
/// </summary>
/// <param name="pLayer"></param>
/// <param name="tableName"></param>
/// <returns></returns>
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的數據類型不同,故要進行轉換。轉換函數如下:
/// <summary>
/// 將GeoDatabase字段類型轉換成.Net相應的數據類型
/// </summary>
/// <param name="fieldType">字段類型</param>
/// <returns></returns>
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";
}
}
裝載DataTable數據
從上一步得到的DataTable還沒有數據,只有字段信息。因此,我們要通過ICursor從ITable中逐一取出每一行數據,即IRow。再創建DataTable中相應的DataRow,根據IRow設置DataRow信息,再將所有的DataRow添加到DataTable中,就完成了DataTable數據的裝載。
為保證效率,一次最多只裝載2000條數據到DataGridView。函數代碼如下:
/// <summary>
/// 填充DataTable中的數據
/// </summary>
/// <param name="pLayer"></param>
/// <param name="tableName"></param>
/// <returns></returns>
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判斷圖層類型的,代碼如下:
/// <summary>
/// 獲得圖層的Shape類型
/// </summary>
/// <param name="pLayer">圖層</param>
/// <returns></returns>
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 "";
}
}
綁定DataTable到DataGridView
通過以上步驟,我們已經得到了一個含有圖層屬性數據的DataTable。現定義一個AttributeTableFrm類的成員變量:
public DataTable attributeTable;
通過以下函數,我們很容易將其綁定到DataGridView控件中:
/// <summary>
/// 綁定DataTable到DataGridView
/// </summary>
/// <param name="player"></param>
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的表名不允許含有“.”,因此我們用“_”替換。函數如下:
/// <summary>
/// 替換數據表名中的點
/// </summary>
/// <param name="FCname"></param>
/// <returns></returns>
public static string getValidFeatureClassName(string FCname)
{
int dot = FCname.IndexOf(".");
if (dot != -1)
{
return FCname.Replace(".", "_");
}
return FCname;
}
調用屬性表窗體
通過1-4步驟,我們封裝了一個AttributeTableFrm類,此類能夠由ILayer顯示圖層中的屬性表數據。那怎么調用AttributeTableFrm呢?
前面已經提到,我們是在TOCControl選中圖層的右鍵菜單中彈出屬性表窗體的,因此我們需要添加一個菜單項到TOCControl中Layer的右鍵菜單。而在第六講中,我們采用的是AE中的IToolbarMenu實現右鍵菜單的,故我們還需自定義一個Command,實現打開屬性表的功能。
以ArcGIS的Base Command為模板新建項“OpenAttributeTable.cs”。
添加如下引用:
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函數中添加如下代碼,以創建並打開屬性表窗體。
/// <summary>
/// Occurs when this command is clicked
/// </summary>
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);
}
}
//////////////////////////////////////////華麗麗的分割線///////////////////////////////////////////////////////
1 //實現控件的右鍵菜單項 2 private void axTOCControl1_OnMouseDown(object sender, ITOCControlEvents_OnMouseDownEvent e) 3 { 4 if (e.button != 2) return; 5 esriTOCControlItem pItem = esriTOCControlItem.esriTOCControlItemNone; 6 IBasicMap pMap = null; 7 ILayer pLayer = null; 8 object unk=null,data=null; 9 //內容表控件中的這個方法是根據鼠標的點擊位置返回內容表中的項 10 axTOCControl1.HitTest(e.x,e.y,ref pItem,ref pMap,ref pLayer,ref unk,ref data); 11 //下面是判斷鼠標選中的項是什么類型的項目(通過esriTOCControlItem的枚舉值我們可以知道,針對不同的類型項可以添加不同的右鍵菜單(圖層、Map對象、圖例對象)) 12 if (pItem == esriTOCControlItem.esriTOCControlItemLayer) 13 { 14 /*第一種方法:使用contextMenuStrip控件 15 //下面的這個point是獲得鼠標的點擊位置。 16 System.Drawing.Point point = new System.Drawing.Point(); 17 //設置這個點的位置為鼠標的點擊位置 18 point.X = e.x; 19 point.Y = e.y; 20 //顯示右鍵菜單 21 contextMenuStrip1.Show(axTOCControl1, point); 22 show方法的兩個參數:一個是右鍵菜單要關聯的控件(或者是要顯示右鍵菜單的控件)。另一個是:鼠標的點擊位置 23 //該位置也是右鍵菜單四個角中,左上的這個位置*/ 24 //第二種方法:使用ToolbarMenu 25 IToolbarMenu pToolbarMenu = new ToolbarMenuClass();//創建工具條菜單類實例,相當與創建了一個菜單 26 //給菜單添加項目 27 pToolbarMenu.AddItem(new ControlsMapViewMenuClass () ,-1,-1,false,esriCommandStyles.esriCommandStyleIconAndText); 28 //設置菜單所關聯的控件 29 pToolbarMenu.SetHook(axTOCControl1); 30 //根據鼠標的點擊位置,彈出菜單 31 pToolbarMenu.PopupMenu(e.x,e.y,axTOCControl1.hWnd); 32 } 33 }
第二位博主的代碼
//添加一個上下文菜單,添加一個新的Form窗體,在這個新的窗體上添加GridView控件,並在TOCControl控件的OnMouseDown事件下添加如下代碼(pGlobalFeatureLayer是我定義的一個全局變量): private void axTOCControl1_OnMouseDown(object sender, ESRI.ArcGIS.Controls.ITOCControlEvents_OnMouseDownEvent e) { if (axMapControl1.LayerCount > 0) { esriTOCControlItem pItem = new esriTOCControlItem(); pGlobalFeatureLayer = new FeatureLayerClass(); IBasicMap pBasicMap = new MapClass(); object pOther = new object(); object pIndex = new object(); axTOCControl1.HitTest(e.x, e.y, ref pItem, ref pBasicMap, ref pGlobalFeatureLayer, ref pOther, ref pIndex); } if (e.button == 2) { context.Show(axTOCControl1, e.x, e.y); } } //在上下文菜單的打開屬性表的Click事件中添加如下代碼: private void 打開屬性表ToolStripMenuItem_Click(object sender, EventArgs e) { FormTable Ft = new FormTable(pGlobalFeatureLayer as IFeatureLayer); Ft.Show(); } //在新的窗體中添加一個將屬性表顯示到GridView控件中的函數,如下: public void Itable2Dtable() { IFields pFields; pFields = pFeatureLayer.FeatureClass.Fields; dtGridView.ColumnCount = pFields.FieldCount; for (int i = 0; i < pFields.FieldCount;i++ ) { string fldName = pFields.get_Field(i).Name; dtGridView.Columns[i].Name = fldName; dtGridView.Columns[i].ValueType = System.Type.GetType(ParseFieldType(pFields.get_Field(i).Type)); } IFeatureCursor pFeatureCursor; pFeatureCursor = pFeatureLayer.FeatureClass.Search(null, false); IFeature pFeature; pFeature = pFeatureCursor.NextFeature(); while (pFeature != null) { string[] fldValue = new string[pFields.FieldCount]; for (int i = 0; i < pFields.FieldCount; i++) { string fldName; 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)); } dtGridView.Rows.Add(fldValue); pFeature = pFeatureCursor.NextFeature(); } }
其他可參考 http://blog.csdn.net/zhushuaibing/article/details/40114309
2 private void axTOCControl1_OnMouseDown(object sender, ITOCControlEvents_OnMouseDownEvent e)
3 {
4 if (e.button != 2) return;
5 esriTOCControlItem pItem = esriTOCControlItem.esriTOCControlItemNone;
6 IBasicMap pMap = null;
7 ILayer pLayer = null;
8 object unk=null,data=null;
9 //內容表控件中的這個方法是根據鼠標的點擊位置返回內容表中的項
10 axTOCControl1.HitTest(e.x,e.y,ref pItem,ref pMap,ref pLayer,ref unk,ref data);
11 //下面是判斷鼠標選中的項是什么類型的項目(通過esriTOCControlItem的枚舉值我們可以知道,針對不同的類型項可以添加不同的右鍵菜單(圖層、Map對象、圖例對象))
12 if (pItem == esriTOCControlItem.esriTOCControlItemLayer)
13 {
14 /*第一種方法:使用contextMenuStrip控件
15 //下面的這個point是獲得鼠標的點擊位置。
16 System.Drawing.Point point = new System.Drawing.Point();
17 //設置這個點的位置為鼠標的點擊位置
18 point.X = e.x;
19 point.Y = e.y;
20 //顯示右鍵菜單
21 contextMenuStrip1.Show(axTOCControl1, point);
22 show方法的兩個參數:一個是右鍵菜單要關聯的控件(或者是要顯示右鍵菜單的控件)。另一個是:鼠標的點擊位置
23 //該位置也是右鍵菜單四個角中,左上的這個位置*/
24
25
26
27 //第二種方法:使用ToolbarMenu
28 IToolbarMenu pToolbarMenu = new ToolbarMenuClass();//創建工具條菜單類實例,相當與創建了一個菜單
29 //給菜單添加項目
30 pToolbarMenu.AddItem(new ControlsMapViewMenuClass () ,-1,-1,false,esriCommandStyles.esriCommandStyleIconAndText);
31 //設置菜單所關聯的控件
32 pToolbarMenu.SetHook(axTOCControl1);
33 //根據鼠標的點擊位置,彈出菜單
34 pToolbarMenu.PopupMenu(e.x,e.y,axTOCControl1.hWnd);
35