Layer Attributes
LayerAttributesForm.vb
' Copyright 2011 ESRI
' 
' All rights reserved under the copyright laws of the United States
' and applicable international laws, treaties, and conventions.
' 
' You may freely redistribute and use this sample code, with or
' without modification, provided you include the original copyright
' notice and use restrictions.
' 
' See the use restrictions.
' 

Imports System.Collections.Generic
Imports System.ComponentModel
Imports System.Collections
Imports System.Data
Imports System.Drawing
Imports System.Linq
Imports System.Text
Imports System.Windows.Forms

Imports ESRI.ArcGISExplorer
Imports ESRI.ArcGISExplorer.Application
Imports ESRI.ArcGISExplorer.Mapping
Imports ESRI.ArcGISExplorer.Geometry
Imports ESRI.ArcGISExplorer.Data

Public Partial Class LayerAttributesForm
  Inherits Form
  Private _tableAdapter As TableBindingAdapter = Nothing
  Private _mapDisplay As MapDisplay = ESRI.ArcGISExplorer.Application.Application.ActiveMapDisplay
  Private _fl As FeatureLayer = Nothing
  Private _idColumnName As String = String.Empty

  ' <summary>
  ' Initializes a new instance of the LayerAttributesForm class.
  ' </summary>
  ' <param name="fl">A FeatureLayer</param>
  Public Sub New(ByVal fl As FeatureLayer)
    InitializeComponent()

    _fl = fl
    _idColumnName = _fl.Table.Columns.ObjectIDColumnName

    '**********************
    'Important code - steps
    '1. Create a new TableBindingAdapter using the Table associated with the specified feature layer.
    '2. Call the Fill method to populate the adapter with data from the Table.
    '3. Set the DataSource property on the bindingSource1 component.
    '4. Bind the bindingSource1 component to the dataGridView1 and bindingNavigator1 objects.
    _tableAdapter = New TableBindingAdapter(_fl.Table)
    _tableAdapter.Fill()
    bindingSource1.DataSource = _tableAdapter
    dataGridView1.DataSource = bindingSource1
    bindingNavigator1.BindingSource = bindingSource1
    '***********************

    Me.Text = [String].Format("{0} Attributes", _fl.Name)
    btnZoomTo.ToolTipText = "Zoom to feature"
    btnClearGraphicForRow.ToolTipText = "Clear graphic for feature"
    btnShowGraphicForRow.ToolTipText = "Show graphic for feature"

    btnClearAllGraphicsForTable.ToolTipText = String.Format("Remove all {0} graphics", _fl.Name)
  End Sub

#Region "public properties"
  ' <summary>
  ' Gets the feature layer for which the attribute data is displayed.
  ' </summary>
  ' <value>A FeatureLayer object representing spatial, vector data.</value>
  Public ReadOnly Property FeatureLayer() As FeatureLayer
    Get
      Return _fl
    End Get
  End Property
#End Region

#Region "button implementation"

  ' <summary>
  ' Handles the Click event of the btnZoomTo control. Zooms to the geometry stored in the Shape column
  ' of a particular Row.
  ' </summary>
  Private Sub btnZoomTo_Click(ByVal sender As Object, ByVal e As EventArgs)
    Dim idColName As String = _fl.Table.Columns.ObjectIDColumnName
    Dim objectID As Integer = CInt(dataGridView1.CurrentRow.Cells(idColName).Value)

    Dim row As Row = _fl.Table.GetRow(objectID)

    If row IsNot Nothing Then
      Dim geom As Geometry = row.Geometry

      If (geom IsNot Nothing) AndAlso (geom.IsEmpty = False) Then
        'Zoom if there is a valid geometry that is not empty.
        _mapDisplay.ZoomTo(geom)
      End If
    End If
  End Sub

  ' <summary>
  ' Handles the Click event of the btnShowGraphicForRow control. Creates a graphic from the geometry stored 
  ' in the Shape column of a particular Row and adds it to the map.
  ' </summary>
  ' <remarks>Note that graphics are temporary - they will not be saved in the Map Document (.nmf) and
  ' additionally they will be removed when the LayerAttributesForm is closed.</remarks>
  Private Sub btnShowGraphicForRow_Click(ByVal sender As Object, ByVal e As EventArgs)
    Dim idColName As String = _fl.Table.Columns.ObjectIDColumnName
    Dim objectID As Integer = CInt(dataGridView1.CurrentRow.Cells(idColName).Value)

    Dim row As Row = _fl.Table.GetRow(objectID)
    'Get the Geometry for the selected Row
    Dim geometry As Geometry = row.Geometry

    Dim sym As Symbol
    'Create a new symbol for the Point, Line and Polygon geometry types. Note that other types are not supported.
    Select Case geometry.GeometryType
      Case GeometryType.Point
        sym = Symbol.Marker.Flag.Red
        Exit Select
      Case GeometryType.Polygon
        sym = Symbol.Fill.Outline.Blue
        Exit Select
      Case GeometryType.Polyline
        sym = Symbol.Line.Solid.Green
        Exit Select
      Case Else
        'currently only create symbols for points, lines and polygons
        sym = Nothing
        Exit Select
    End Select

    If sym IsNot Nothing Then
      'Create a new graphic
      Dim gr As New Graphic(geometry, sym)

      '"Tag" the graphic so that it can be found later and removed
      Dim identifier As New FeatureLayerAndObjectId()
      identifier.FeatureLayer = _fl
      identifier.ObjectId = objectID
      gr.Tag = identifier

      'add the grpahic to the GraphicsCollection
      _mapDisplay.Graphics.Add(gr)
      'update the buttons enabled status if required
      SetButtonEnabledState()
    End If
  End Sub


  ' <summary>
  ' Handles the Click event of the btnClearGraphicForRow control. Removes a graphic for a particular Row
  ' </summary>
  Private Sub btnClearGraphicForRow_Click(ByVal sender As Object, ByVal e As EventArgs)
   
    Dim clickedOID As Integer = CInt(dataGridView1.CurrentRow.Cells(_idColumnName).Value)
    Dim graphicToDelete As Graphic = Nothing

    For Each graphic As Graphic In _mapDisplay.Graphics

      Dim graphicTag As Object = graphic.Tag

      If Not graphicTag Is Nothing AndAlso TypeOf graphicTag Is FeatureLayerAndObjectId Then

        Dim customTag As FeatureLayerAndObjectId = DirectCast(graphicTag, FeatureLayerAndObjectId)
        Dim foundLayer As FeatureLayer = customTag.FeatureLayer
        Dim foundOID As Integer = customTag.ObjectId

        If (foundLayer Is _fl) AndAlso (foundOID = clickedOID) Then
          graphicToDelete = graphic
          Exit For
        End If

      End If
    Next

    'if a graphic was found in the Grpahics collection for this Row then remove it
    If graphicToDelete IsNot Nothing Then
      _mapDisplay.Graphics.Remove(graphicToDelete)
    End If

    'update the buttons enabled status if required
    SetButtonEnabledState()
  End Sub

  ' <summary>
  ' Handles the Click event of the btnClearAllGraphicsForTable control. Clears all graphics which
  ' have been added to the map for this feature layer
  ' </summary>
  Private Sub btnClearAllGraphicsForTable_Click(ByVal sender As Object, ByVal e As EventArgs)

    Dim graphicsToDelete As New List(Of Graphic)()

    For Each graphic As Graphic In _mapDisplay.Graphics

      Dim graphicTag As Object = graphic.Tag

      If Not graphicTag Is Nothing AndAlso TypeOf graphicTag Is FeatureLayerAndObjectId Then
        Dim customTag As FeatureLayerAndObjectId = DirectCast(graphicTag, FeatureLayerAndObjectId)
        Dim foundLayer As FeatureLayer = customTag.FeatureLayer

        If (foundLayer Is _fl) Then
          graphicsToDelete.Add(graphic)
        End If
      End If
    Next

    'Delete any graphics associated with this feature layer
    For Each featureGraphic As Graphic In graphicsToDelete
      _mapDisplay.Graphics.Remove(featureGraphic)
    Next

    'update the buttons enabled status if required
    SetButtonEnabledState()
  End Sub
#End Region

#Region "other event handlers"

  ' <summary>
  ' Handles the FormClosing event and ensures that all graphics are removed for the 
  ' feature layer when the form is closed.
  ' </summary>
  Private Sub AttributesForm_FormClosing(ByVal sender As Object, ByVal e As FormClosingEventArgs)
    btnClearAllGraphicsForTable_Click(Nothing, Nothing)
  End Sub

  ' <summary>
  ' Handles the SelectionChanged event within dataGridView1 control by checking to see if the 
  ' button enabled state should be changed.
  ' </summary>
  Private Sub dataGridView1_SelectionChanged(ByVal sender As Object, ByVal e As EventArgs)
    SetButtonEnabledState()
  End Sub
#End Region

#Region "private methods"

  ' <summary>
  ' Determines whether a graphic has already been added to the Map for the selected Row of data.
  ' </summary>
  ' <returns>true if a graphic is found, otherwise false.</returns>
  Private Function IsGraphicShownForRow() As Boolean

    Dim clickedOID As Integer = CInt(dataGridView1.CurrentRow.Cells(_idColumnName).Value)
    Dim foundGraphicForRow As Boolean = False

    For Each graphic As Graphic In _mapDisplay.Graphics

      Dim graphicTag As Object = graphic.Tag

      If Not graphicTag Is Nothing AndAlso TypeOf graphicTag Is FeatureLayerAndObjectId Then

        Dim customTag As FeatureLayerAndObjectId = DirectCast(graphicTag, FeatureLayerAndObjectId)
        Dim foundLayer As FeatureLayer = customTag.FeatureLayer
        Dim foundOID As Integer = customTag.ObjectId

        If (foundLayer Is _fl) AndAlso (foundOID = clickedOID) Then
          foundGraphicForRow = True
          Exit For
        End If

      End If
    Next
    Return foundGraphicForRow
  End Function

  ' <summary>
  ' Determines whether any graphics have been added to the map for this Table
  ' </summary>
  ' <returns>true if any graphics have been added to the map for this Table, otehrwise false</returns>
  Private Function HasMapGotGraphicsForTable() As Boolean
    Dim foundGraphics As Boolean = False

    For Each graphic As Graphic In _mapDisplay.Graphics

      Dim graphicTag As Object = graphic.Tag

      If Not graphicTag Is Nothing AndAlso TypeOf graphicTag Is FeatureLayerAndObjectId Then
        Dim customTag As FeatureLayerAndObjectId = DirectCast(graphicTag, FeatureLayerAndObjectId)
        Dim foundLayer As FeatureLayer = customTag.FeatureLayer

        If (foundLayer Is _fl) Then
          foundGraphics = True
          Exit For
        End If
      End If
    Next
    Return foundGraphics
  End Function

  ' <summary>
  ' Sets the enabled state for the custom buttons which have been added to the binding navigator
  ' </summary>
  Private Sub SetButtonEnabledState()
    Dim selectedRows As DataGridViewSelectedRowCollection = dataGridView1.SelectedRows

    Dim enableAddGraphicBtn As Boolean = False
    Dim enableRemoveGraphicBtn As Boolean = False
    Dim enableZoomToBtn As Boolean = False

    'only enable the buttons after making necessary checks
    If selectedRows IsNot Nothing Then
      enableZoomToBtn = True

      Dim graphicExistsForRow As Boolean = IsGraphicShownForRow()
      enableAddGraphicBtn = Not graphicExistsForRow
      enableRemoveGraphicBtn = graphicExistsForRow

      btnShowGraphicForRow.Enabled = enableAddGraphicBtn
      btnClearGraphicForRow.Enabled = enableRemoveGraphicBtn
      btnZoomTo.Enabled = enableZoomToBtn
    End If

    'doesn't matter whether there is a selection
    btnClearAllGraphicsForTable.Enabled = HasMapGotGraphicsForTable()
  End Sub
#End Region
End Class

' <summary>
'Helper class used for tagging graphics, allowing then to be easily identified and removed 
'</summary>
Public Class FeatureLayerAndObjectId

  Private _featureLayer As FeatureLayer
  Private _ObjectId As Integer

  Public Property FeatureLayer() As FeatureLayer
    Get
      Return _featureLayer
    End Get
    Set(ByVal value As FeatureLayer)
      _featureLayer = value
    End Set
  End Property

  Public Property ObjectId() As Integer
    Get
      Return _ObjectId
    End Get
    Set(ByVal value As Integer)
      _ObjectId = value
    End Set
  End Property
End Class