1. 程式人生 > >ArcGIS Engine + C# 例項開發教程:第八講 屬性資料表的查詢顯示

ArcGIS Engine + C# 例項開發教程:第八講 屬性資料表的查詢顯示

在上一講中,我們完成了圖層符號選擇器的製作。這一講中,我們將實現圖層屬性資料表的查詢顯示。

在ArcMap中,單擊圖層右鍵選單中的“Open Attribute Table”命令,便可彈出屬性資料表。本講將完成類似的功能,效果如下:

屬性資料表的查詢顯示

圖1

資料表顯示,我們用了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”。

注意:新建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函式中新增如下程式碼,以建立並開啟屬性表窗體。

/// <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);
	}
}

編譯執行

按下F5,編譯執行程式,相信你已經實現了開篇處展示的屬性表效果了吧!

以上程式碼在Windows XP SP3 + VS2005 + AE9.2環境下編譯通過。

下一講中,我將給大家帶來的是圖層文字標註的實現,敬請關注3SDN.Net!