1、需求
GIS的数据有空间信息和属性信息,基于ArcEngine开发GIS系统的时候,空间信息用MapControl可以直接展示,但是属性信息却没有相关的控件直接进行展示(或许有接口,我没有找到,而且arcobject自带的接口可能很难满足客户奇奇怪怪的需求)。参考ArcMap中的属性表的功能仿制了一个自定义控件。
2、界面设计
这个是自定义控件的主界面,有两个GridControl组成,之所以用两个GridControl,是为了避免在大量数据的情况下一个GridControl反复刷新造成卡顿。自定义控件的最下方是一个radio控件组和自定义分页控件(这个控件在之前的博文中有写:自定义分页控件)。另外,还支持全选、当前页面选择、取消选择、排序功能
3、代码实现
代码比较多,就按功能把核心代码贴上吧!
3.1 属性和私有变量定义
这里定义了一个私有事件,用来捕获地图选择集的改变。
#region 定义事件
private IActiveViewEvents_Event pActiveViewEvents;
#endregion
#region 定义属性
public IMapControl4 BindingMapControl
{
get;private set;
}
/// <summary>
/// 属性表页面标签,0表示所有属性表,1表示选择要素的属性表
/// </summary>
public int PageTag
{
get; private set;
}
/// <summary>
/// 所有属性的表控件
/// </summary>
public GridControl AllDataGrid
{
get { return gridAllAttribute; }
}
/// <summary>
/// 选择属性的表控件
/// </summary>
public GridControl SelectionDataGrid
{
get { return gridSelectAttribute; }
}
/// <summary>
/// 所有属性的数据表
/// </summary>
public DataTable AllAttTable
{
get { return allRowsTable; }
}
/// <summary>
/// 选择属性的数据表
/// </summary>
public DataTable SelectionAttTable
{
get { return selectionRowsTable; }
}
#endregion
#region 私有变量定义
private IQueryFilter queryFilter;
private ISpatialFilter spatialFilter;
private DataTable allRowsTable;
private DataTable selectionRowsTable;
private IFeatureLayer featureLayer;
private List<int> selectIDList = new List<int>();
private string oidFieldName;
#endregion
3.2 控件初始化
大概思路是先创建含有属性字段的空表,再遍历每一个Feature填充表格,最后绑定到控件,其中,用一个临时的列表来存储选择的要素。这里的所有属性的表中还加了“选中要素”一列来标记选择的状态。注册ActiveViewEvents_SelectionChanged(地图选择集改变事件)
public CtrlAttrubuteGrid(IMapControl4 pMapControl, ref IFeatureLayer pFeatureLayer, IQueryFilter pQueryFilter)
{
try
{
InitializeComponent();
if (pFeatureLayer == null)
{
return;
}
queryFilter = pQueryFilter;
spatialFilter = pQueryFilter as ISpatialFilter;
featureLayer = pFeatureLayer;
oidFieldName = (featureLayer.FeatureClass as ITable).OIDFieldName;
selectionRowsTable = GetSelectionRowsTable(featureLayer);
allRowsTable = CteateCtrlAttributeTable(featureLayer);
allRowsTable.DefaultView.Sort = oidFieldName + " ASC";
allRowsTable = allRowsTable.DefaultView.ToTable();
selectionRowsTable.DefaultView.Sort = oidFieldName + " ASC";
selectionRowsTable = selectionRowsTable.DefaultView.ToTable();
allRowsTable.PrimaryKey = new DataColumn[] { allRowsTable.Columns[oidFieldName] };
selectionRowsTable.PrimaryKey = new DataColumn[] { selectionRowsTable.Columns[oidFieldName] };
gridAllAttribute.DataSource = allRowsTable;
gridSelectAttribute.DataSource = selectionRowsTable;
gviewSelect.Columns[0].Visible = false;
gridAllAttribute.Visible = true;
gridAllAttribute.Dock = DockStyle.Fill;
gridAllAttribute.Refresh();
gridAllAttribute.RefreshDataSource();
gridSelectAttribute.Visible = false;
ctrlGridNavigation1.InitCtrl(allRowsTable, gridAllAttribute, new int[] { 50, 100, 500 });
for (int i = 1; i < gviewAll.Columns.Count; i++)
{
gviewAll.Columns[i].OptionsColumn.AllowEdit = false;
}
gviewAll.BestFitColumns();
PageTag = 0;
BindingMapControl = pMapControl;
pActiveViewEvents = BindingMapControl.Map as IActiveViewEvents_Event;
pActiveViewEvents.SelectionChanged += new IActiveViewEvents_SelectionChangedEventHandler(ActiveViewEvents_SelectionChanged);
}
catch (Exception ex)
{
XtraMessageBox.Show("属性表初始化失败");
}
}
/// <summary>
/// 获取要素图层中选择要素的属性表
/// </summary>
/// <param name="pFeatureLayer"></param>
/// <returns></returns>
private DataTable GetSelectionRowsTable(IFeatureLayer pFeatureLayer)
{
selectIDList = new List<int>();
DataTable pDataTable = CreateDataTableByLayer(pFeatureLayer, pFeatureLayer.Name);
using (ComReleaser comReleaser = new ComReleaser())
{
string oidFieldName = pFeatureLayer.FeatureClass.OIDFieldName;
ICursor pCursor = null;
DataRow pDataRow = null;
comReleaser.ManageLifetime(pCursor);
string shapeType = GetShapeType(pFeatureLayer);
IFeatureSelection pFeatureSelection = featureLayer as IFeatureSelection;
ISelectionSet pSelectionSet = pFeatureSelection.SelectionSet;
if (pSelectionSet.Count == 0)
return pDataTable;
pSelectionSet.Search(null, false, out pCursor);
IRow pRow = pCursor.NextRow();
comReleaser.ManageLifetime(pRow);
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["Shape"] = shapeType;
}
//当图层类型为Anotation时,要素类中会有esriFieldTypeBlob类型的数据,
//其存储的是标注内容,如此情况需将对应的字段值设置为Element
else if (pRow.Fields.get_Field(i).Type == esriFieldType.esriFieldTypeBlob)
{
pDataRow[i] = "Element";
}
else
{
string o = pRow.get_Value(i).ToString();
pDataRow[pRow.Fields.Field[i].Name] = pRow.get_Value(i);
}
}
pDataRow[0] = true;
selectIDList.Add(Convert.ToInt32(pDataRow[oidFieldName]));
pDataTable.Rows.Add(pDataRow);
pDataRow = null;
n++;
pRow = pCursor.NextRow();
}
}
return pDataTable;
}
/// <summary>
/// 创建属性表
/// </summary>
/// <param name="_featureLayer">待创建属性表的要素图层</param>
/// <returns>返回创建的字段表</returns>
private DataTable CteateCtrlAttributeTable(IFeatureLayer _featureLayer)
{
DataTable pDataTable = CreateDataTableByLayer(_featureLayer, _featureLayer.Name);
string shapeType = GetShapeType(_featureLayer);
DataRow pDataRow = null;
ITable pTable = _featureLayer as ITable;
ICursor pCursor = pTable.Search(queryFilter, false);
IRow pRow = pCursor.NextRow();
int n = 0;
while (pRow != null)
{
using (ComReleaser comReleaser = new ComReleaser())
{
comReleaser.ManageLifetime(pRow);
//新建DataTable的行对象
pDataRow = pDataTable.NewRow();
for (int i = 0; i < pRow.Fields.FieldCount; i++)
{
//如果字段类型为esriFieldTypeGeometry,则根据图层类型设置字段值
if (pRow.Fields.get_Field(i).Type == esriFieldType.esriFieldTypeGeometry)
{
pDataRow["Shape"] = shapeType;
}
//当图层类型为Anotation时,要素类中会有esriFieldTypeBlob类型的数据,
//其存储的是标注内容,如此情况需将对应的字段值设置为Element
else if (pRow.Fields.get_Field(i).Type == esriFieldType.esriFieldTypeBlob)
{
pDataRow[i] = "Element";
}
else
{
string o = pRow.get_Value(i).ToString();
pDataRow[pRow.Fields.Field[i].Name] = pRow.get_Value(i);
}
}
//设置行选中状态
if (selectIDList.Contains(Convert.ToInt32(pDataRow[pTable.OIDFieldName])))
{
pDataRow["选中要素"] = true;
}
else
{
pDataRow["选中要素"] = false;
}
pDataTable.Rows.Add(pDataRow);
pDataRow = null;
n++;
pRow = pCursor.NextRow();
}
}
//Marshal.ReleaseComObject(pTable);
return pDataTable;
}
/// <summary>
/// 根据图层字段创建一个只含字段的空DataTable
/// </summary>
/// <param name="pLayer"></param>
/// <param name="tableName"></param>
/// <returns>返回创建好的空表</returns>
private DataTable CreateDataTableByLayer(ILayer pLayer, string tableName, bool canSelect = false)
{
DataTable pDataTable = new DataTable(tableName);
ITable pTable = pLayer as ITable;
IField pField = null;
DataColumn pDataColumn;
pDataColumn = new DataColumn("选中要素", typeof(bool));
pDataColumn.Caption = "选中要素";
pDataColumn.DefaultValue = false;
pDataTable.Columns.Add(pDataColumn);
int oidIndex = pTable.Fields.FindField(pTable.OIDFieldName);
int shapeIndex = pTable.FindField("Shape");
IField oidField = pTable.Fields.get_Field(oidIndex);
IField shapeField = pTable.Fields.get_Field(shapeIndex);
DataColumn oidDataColumn = new DataColumn(pTable.OIDFieldName);
oidDataColumn.Unique = true;
oidDataColumn.AllowDBNull = oidField.IsNullable;
oidDataColumn.Caption = oidField.AliasName;
oidDataColumn.DataType = System.Type.GetType(ParseFieldType(oidField.Type));
oidDataColumn.DefaultValue = oidField.DefaultValue;
pDataTable.Columns.Add(oidDataColumn);
pDataColumn = new DataColumn("Shape");
pDataColumn.AllowDBNull = shapeField.IsNullable;
pDataColumn.Caption = shapeField.AliasName;
pDataColumn.DataType = System.Type.GetType(ParseFieldType(shapeField.Type));
pDataColumn.DefaultValue = shapeField.DefaultValue;
pDataTable.Columns.Add(pDataColumn);
for (int i = 0; i < pTable.Fields.FieldCount; i++)
{
pField = pTable.Fields.get_Field(i);
if (pField.Name == pTable.OIDFieldName || pField.Name.ToUpper() == "SHAPE")
{
pField = null;
pDataColumn = null;
continue;
}
pDataColumn = new DataColumn(pField.Name);
pDataColumn.AllowDBNull = pField.IsNullable;
pDataColumn.Caption = pField.AliasName;
pDataColumn.DataType = System.Type.GetType(ParseFieldType(pField.Type));
pDataColumn.DefaultValue = pField.DefaultValue;
if (pField.VarType == 8)
{
pDataColumn.MaxLength = pField.Length;
}
pDataTable.Columns.Add(pDataColumn);
pField = null;
pDataColumn = null;
}
return pDataTable;
}
/// <summary>
/// 获得图层的Shape类型
/// </summary>
/// <param name="pLayer">图层</param>
/// <returns>返回图形的几何类型</returns>
private 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 "";
}
}
3.3 勾选属性(选择、取消选择)
//改变所有属性的勾选状态(勾选即选中对应的要素)
private void gviewAll_CellValueChanging(object sender, DevExpress.XtraGrid.Views.Base.CellValueChangedEventArgs e)
{
if (e.Column.FieldName == "选中要素")
{
try
{
int rowIndex = e.RowHandle;
object o = e.Value;
DataRow selectDataRow = allRowsTable.Rows.Find(gviewAll.GetRowCellValue(e.RowHandle, gviewAll.Columns[oidFieldName]));
selectDataRow["选中要素"] = o;
gridAllAttribute.RefreshDataSource();
int oid = Convert.ToInt32(selectDataRow[oidFieldName]);
IQueryFilter queryFilter = new QueryFilterClass();
queryFilter.WhereClause = featureLayer.FeatureClass.OIDFieldName + "=" + oid;
if ((bool)o)
{
(featureLayer as IFeatureSelection).SelectFeatures(queryFilter, esriSelectionResultEnum.esriSelectionResultAdd, false);
}
else
{
(featureLayer as IFeatureSelection).SelectFeatures(queryFilter, esriSelectionResultEnum.esriSelectionResultSubtract, false);
}
BindingMapControl.Refresh();
}
catch (Exception ex)
{
XtraMessageBox.Show("选取要素失败");
}
}
}
3.4 捕获地图选择集改变事件
//地图刷新
private void ActiveViewEvents_SelectionChanged()
{
UpdateCtrlDataSource();
}
/// <summary>
/// 更新所有属性表(需要在已经初始化DataTable的情况下调用)
/// </summary>
private void UpdateCtrlDataSource()
{
if (allRowsTable == null || selectionRowsTable == null)
return;
try
{
if (gridAllAttribute.Visible == true)
{
ICursor pCursor = null;
int selectOid;
string where = "";
selectIDList = new List<int>();
DataColumn dataColumn = allRowsTable.Columns["选中要素"];
allRowsTable.Columns.Remove(dataColumn);
dataColumn.DefaultValue = false;
allRowsTable.Columns.Add(dataColumn);
IFeatureSelection pFeatureSelection = featureLayer as IFeatureSelection;
ISelectionSet pSelectionSet = pFeatureSelection.SelectionSet;
pSelectionSet.Search(null, false, out pCursor);
IRow pRow = pCursor.NextRow();
while (pRow != null)
{
selectOid = Convert.ToInt32(pRow.get_Value(pRow.Fields.FindField(oidFieldName)));
selectIDList.Add(selectOid);
pRow = pCursor.NextRow();
}
if (selectIDList.Count > 0)
{
foreach (int i in selectIDList)
{
where += oidFieldName + "=" + i + " or ";
}
where = where.Substring(0, where.Length - 3);
DataRow[] selectRows = allRowsTable.Select(where);
foreach (DataRow dataRow in selectRows)
{
dataRow["选中要素"] = true;
}
}
gridAllAttribute.DataSource = allRowsTable;
gviewAll.RefreshData();
ctrlGridNavigation1.InitCtrl(allRowsTable, gridAllAttribute, new int[] { 50, 100, 500 });
}
if (gridSelectAttribute.Visible == true)
{
RefreshSelectionData();
gviewSelect.RefreshData();
ctrlGridNavigation1.InitCtrl(selectionRowsTable, gridSelectAttribute, new int[] { 50, 100, 500 });
}
}
catch (Exception ex)
{
XtraMessageBox.Show("属性表更新失败");
}
}
/// <summary>
/// 刷新选择项属性表
/// </summary>
public void RefreshSelectionData()
{
try
{
selectionRowsTable = GetSelectionRowsTable(featureLayer);
selectionRowsTable.DefaultView.Sort = oidFieldName + " ASC";
selectionRowsTable = selectionRowsTable.DefaultView.ToTable();
selectionRowsTable.PrimaryKey = new DataColumn[] { selectionRowsTable.Columns[oidFieldName] };
}
catch (Exception ex)
{
XtraMessageBox.Show("属性表刷新错误");
}
}
3.4 调用控件
注意,在控件初始化时应该传入的地图控件参数应该是IMapControl4,而不是AxMapControl。在上传的代码中有完整的调用示例。
CtrlAttrubuteGrid ctrlAttrubuteGrid = new CtrlAttrubuteGrid(mapControl, ref featureLayer, queryFilter);
this.Controls.Add(ctrlAttrubuteGrid);