CustomLayerBase.vb
Dynamic Display layer sample
CustomLayerBase.vb
' Copyright 2006 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 Microsoft.VisualBasic
Imports System
Imports System.Collections
Imports System.Data
Imports System.Runtime.InteropServices
Imports System.Windows.Forms

Imports ESRI.ArcGIS.Carto
Imports ESRI.ArcGIS.Geodatabase
Imports ESRI.ArcGIS.esriSystem
Imports ESRI.ArcGIS.Geometry
Imports ESRI.ArcGIS.Display



  ''' <summary>
  ''' Summary description for clsCustomLayer.
  ''' </summary>
Public MustInherit Class DynamicLayerBase : Inherits Control : Implements ILayer, IDynamicLayer, ILayerExtensions, IGeoDataset, IPersistVariant, ILayerGeneralProperties, IEnumerable, IDisposable
#Region "Class members"
  ''' <summary>
  ''' Keep the layer's extent. Returned by the ILayer::Extent property
  ''' </summary>
  ''' <remarks>The extent should be spatial-referenced to the DateFrame's spatial reference.
  ''' </remarks>
  Protected m_extent As IEnvelope = Nothing

  ''' <summary>
  ''' Store the layer's underlying data spatial reference. Returned by IGeoDataset::SpatialReference.
  ''' </summary>
  ''' <remarks>This spatial reference should not be reprojected. In your inheriting 
  ''' class you will need to have another parameter that will keep the DataFrame's spatial reference
  ''' that would use to reproject the geometries and the extent of the layer.</remarks>
  Protected m_spatialRef As ISpatialReference = Nothing

  ''' <summary>
  ''' Layer's name. Returned by ILayer::Name property
  ''' </summary>
  Protected m_sName As String

  ''' <summary>
  ''' Flag which determines whether the layers is visible. Returned by ILayer::Visible
  ''' </summary>
  ''' <remarks>You should use this member in your inherited class in the Draw method.</remarks>
  Protected m_visible As Boolean

  ''' <summary>
  ''' determines whether the layers is cached
  ''' </summary>
  Protected m_IsCached As Boolean

  ''' <summary>
  ''' Flag thich determine whether the layer is valid (connected to its data source, has valid information etc.).
  ''' Returned by ILAyer::Valid.
  ''' </summary>
  ''' <remarks>You can use this flag to determine for example whether the layer can be available or not.</remarks>
  Protected m_bValid As Boolean = True

  ''' <summary>
  ''' Keep the maximum scale value at which the layer will display
  ''' </summary>
  Protected m_MaximumScale As Double

  ''' <summary>
  ''' Keep the minimum scale value at which the layer will display
  ''' </summary>
  Protected m_MinimumScale As Double

  ''' <summary>
  ''' determines whether the layers is supposed to show its MapTips
  ''' </summary>
  Protected m_ShowTips As Boolean

  ''' <summary>
  ''' the layer dirty flag which determine whether its display list need to be recreate
  ''' </summary>
  Protected m_bIsImmediateDirty As Boolean = False

  ''' <summary>
  ''' the layer dirty flag which determine whether its display list need to be recreate
  ''' </summary>
  Protected m_bIsCompiledDirty As Boolean = False

  ''' <summary>
  ''' The rate in which the DynamicDisplay recompile its display lists
  ''' </summary>
  Protected m_nDynamicRecompileRate As Integer = -1

  ''' <summary>
  ''' The layer's UID
  ''' </summary>
  Protected m_uid As UID

  ' <summary>
  ' An arraylist to store the layer's extensions.
  ' </summary>
  Protected m_extensions As ArrayList = Nothing

  ''' <summary>
  ''' The data structure for the custom layer
  ''' </summary>
  ''' <remarks>By default, the DataTable gets initialized in the BaseClass Ctor with an ID column
  ''' which is AutoIncremented. The BaseClass also support enumerator which is actually en enumerator of
  ''' the table as well as a direct indexer.</remarks>
  Protected m_table As DataTable
#End Region

#Region "class constructor"

  ''' <summary>
  ''' Default constructor
  ''' </summary>
  Public Sub New()
    m_sName = String.Empty
    m_visible = True
    m_IsCached = False
    m_MaximumScale = 0
    m_MinimumScale = 0
    m_table = New DataTable("RECORDS")

    m_table.Columns.Add("ID", GetType(Long))

    m_extent = New EnvelopeClass()
    m_extent.SetEmpty()

    m_uid = New UID()

    m_extensions = New ArrayList()

    'make sure tha the control got created and that it has a valid handle
    Me.CreateHandle()
    Me.CreateControl()
  End Sub
#End Region

#Region "IGeoDataset Members"

  ''' <summary>
  '''The layers geodataset extent which is a union of the extents of all
  ''' the items of the layer
  ''' </summary>
  ''' <remarks>In your inheriting class, consider the following code to calculate the layer's extent:
  ''' <code>
  ''' public override IEnvelope Extent
  '''{
  '''  get
  '''  {
  '''    m_extent  = GetLayerExtent();
  '''    if (null == m_extent )
  '''      return null;
  '''
  '''    IEnvelope env = ((IClone)m_extent ).Clone() as IEnvelope;
  '''    if(null != m_layerSpatialRef)
  '''       env.Project(m_layerSpatialRef);
  ''' 
  '''    return env;
  '''  }
  '''}
  '''    private IEnvelope GetLayerExtent()
  '''{
  '''  if (null == base.m_spRef)
  '''  {
  '''    base.m_spRef = CreateGeographicSpatialReference();
  '''  }
  '''
  '''  IEnvelope env = new EnvelopeClass();
  '''  env.SpatialReference = base.m_spRef;
  '''  IPoint point = new PointClass();
  '''  point.SpatialReference = m_spRef;
  '''  foreach (DataRow r in m_table.Rows)
  '''  {
  '''    point.Y = Convert.ToDouble(r["Y"]);
  '''    point.X = Convert.ToDouble(r["X"]);
  '''
  '''    env.Union(point.Envelope);
  '''  }
  ''' 
  '''  return env;
  '''} 
  ''' </code>
  ''' </remarks>    
  Public Overridable ReadOnly Property Extent() As IEnvelope Implements IGeoDataset.Extent
    Get
      Return m_extent
    End Get
  End Property

  ''' <summary>
  ''' The spatial reference of the underlying data.
  ''' </summary>
  ''' <remarks>The property must return the underlying data spatial reference and
  ''' must not reporoject it into the layer's spatial reference </remarks>

  Public Overridable ReadOnly Property SpatialReference1() As ISpatialReference Implements IGeoDataset.SpatialReference ''ILayer.SpatialReference,
    Get
      Return m_spatialRef
    End Get
  End Property

#End Region

#Region "IPersistVariant Members"

  ''' <summary>
  ''' The ID of the object.
  ''' </summary>
  Public Overridable ReadOnly Property ID() As UID Implements IPersistVariant.ID
    Get
      ' TODO:  Add clsCustomLayer.ID getter implementation
      Return Nothing
    End Get
  End Property

  ''' <summary>
  ''' Loads the object properties from the stream.
  ''' </summary>
  ''' <param name="Stream"></param>
  ''' <remarks>The Load method must read the data from the stream in the same order the data was 
  ''' written to the stream in the Save method. 
  ''' Streams are sequential; you mut ensure that your data is saved and loaded in the correct order, 
  ''' so that the correct data is written to the correct member.
  ''' </remarks>
  Public Overridable Sub Load(ByVal Stream As IVariantStream) Implements IPersistVariant.Load
    m_extensions = CType(Stream.Read(), ArrayList)
  End Sub

  ''' <summary>
  ''' Saves the object properties to the stream.
  ''' </summary>
  ''' <param name="Stream"></param>
  Public Overridable Sub Save(ByVal Stream As IVariantStream) Implements IPersistVariant.Save
    Stream.Write(m_extensions)
  End Sub

#End Region

#Region "ILayer Members"

#Region "Properties"

  ''' <summary>
  ''' Indicates if the layer shows map tips.
  ''' </summary>
  ''' <remarks>Indicates whether or not map tips are shown for the layer. 
  ''' If set to True, then map tips will be shown for the layer. 
  ''' You can determine the text that will be shown via TipText. 
  '''</remarks>
  Public Overridable Property ShowTips() As Boolean Implements ILayer.ShowTips
    Get
      Return m_ShowTips
    End Get
    Set(ByVal value As Boolean)
      m_ShowTips = value
    End Set
  End Property

  ''' <summary>
  ''' The default area of interest for the layer. Returns the spatial-referenced extent of the layer.
  ''' </summary>
  Public Overridable ReadOnly Property AreaOfInterest() As IEnvelope Implements ILayer.AreaOfInterest
    Get
      Return m_extent
    End Get
  End Property

  ''' <summary>
  ''' Indicates if the layer is currently visible.
  ''' </summary>
  Public Overridable Overloads Property Visible() As Boolean Implements ILayer.Visible
    Get
      Return m_visible
    End Get
    Set(ByVal value As Boolean)
      m_visible = value
    End Set
  End Property


  ''' <summary>
  ''' Indicates if the layer needs its own display cache.
  ''' </summary>
  ''' <remarks>This property indicates whether or not the layer requires its own display cache. 
  ''' If this property is True, then the Map will use a separate display cache for the layer so 
  ''' that it can be refreshed indpendently of other layers.</remarks>
  Public Overridable Property Cached() As Boolean Implements ILayer.Cached
    Get
      Return m_IsCached
    End Get
    Set(ByVal value As Boolean)
      m_IsCached = value
    End Set
  End Property

  ''' <summary>
  ''' Minimum scale (representative fraction) at which the layer will display.
  ''' </summary>
  ''' <remarks>Specifies the minimum scale at which the layer will be displayed. 
  ''' This means that if you zoom out beyond this scale, the layer will not display. 
  ''' For example, specify 1000 to have the layer not display when zoomed out beyond 1:1000.</remarks>
  Public Overridable Property MinimumScale() As Double Implements ILayer.MinimumScale
    Get
      Return m_MinimumScale
    End Get
    Set(ByVal value As Double)
      m_MinimumScale = value
    End Set
  End Property

  ''' <summary>
  ''' Indicates if the layer is currently valid.
  ''' </summary>
  ''' <remarks>The valid property indicates if the layer is currently valid.
  ''' Layers that reference feature classes are valid when they hold a reference to a valid feature class.
  ''' The property does not however validate the integrity of the feature classes reference to the database.
  ''' Therefore, in rare situations if a datasource is removed after a layer is initialized, 
  ''' the layer will report itself as valid but query attempts to the data source will error due to the lack 
  ''' of underlying data.</remarks>
  Public Overridable ReadOnly Property Valid() As Boolean Implements ILayer.Valid
    Get
      Return m_bValid
    End Get
  End Property

  ''' <summary>
  ''' The Layer name.
  ''' </summary>
  Public Overridable Overloads Property Name() As String Implements ILayer.Name
    Get
      Return m_sName
    End Get
    Set(ByVal value As String)
      m_sName = value
    End Set
  End Property

  ''' <summary>
  ''' Maximum scale (representative fraction) at which the layer will display.
  ''' </summary>
  ''' <remarks>Specifies the maximum scale at which the layer will be displayed. 
  ''' This means that if you zoom in beyond this scale, the layer will not display. 
  ''' For example, specify 500 to have the layer not display when zoomed in beyond 1:500.</remarks>
  Public Overridable Property MaximumScale() As Double Implements ILayer.MaximumScale
    Get
      Return m_MaximumScale
    End Get
    Set(ByVal value As Double)
      m_MaximumScale = value
    End Set
  End Property

  ''' <summary>
  ''' Supported draw phases.
  ''' </summary>
  ''' <remarks>Indicates the draw phases supported by the layer (esriDPGeography, esriDPAnnotation, 
  ''' esriDPSelection, or any combination of the three). 
  ''' The supported draw phases are defined by esriDrawPhase. 
  ''' When multiple draw phases are supported, the sum of the constants is used. 
  ''' For example, if SupportedDrawPhases = 3 then the layer supports drawing in the geography and annotation phases.</remarks>
  Public ReadOnly Property SupportedDrawPhases() As Integer Implements ILayer.SupportedDrawPhases
    Get
      Return CInt(esriDrawPhase.esriDPGeography)
    End Get
  End Property

  ''' <summary>
  ''' Spatial reference for the layer.
  ''' </summary>
  '''<remarks>This property is only used for map display, setting this property does not 
  '''change the spatial reference of the layer's underlying data. 
  '''The ArcGIS framework uses this property to pass the spatial reference from the map 
  '''to the layer in order to support on-the-fly projection.</remarks> 
  Private WriteOnly Property SpatialReference() As ISpatialReference Implements ESRI.ArcGIS.Carto.ILayer.SpatialReference
    Set(ByVal value As ISpatialReference)
      m_spatialRef = value
    End Set
  End Property

  ''' <summary>
  ''' Map tip text at the specified location. 
  ''' </summary>
  ''' <param name="X"></param>
  ''' <param name="Y"></param>
  ''' <param name="Tolerance"></param>
  ''' <returns>The text string that gets displayed as a map tip if ShowTips = true.</returns>
  Public Overridable ReadOnly Property TipText(ByVal X As Double, ByVal Y As Double, ByVal Tolerance As Double) As String Implements ILayer.TipText
    Get
      Return Nothing
    End Get
  End Property

#End Region

#Region "Methods"
  ''' <summary>
  ''' Draws the layer to the specified display for the given draw phase.
  ''' </summary>
  ''' <param name="drawPhase"></param>
  ''' <param name="Display"></param>
  ''' <param name="trackCancel"></param>
  ''' <remarks>This method draws the layer to the Display for the specified DrawPhase. 
  ''' Use the TrackCancel object to allow the drawing of the layer to be interrupted by the user.
  ''' In order to implement you inheriting class, you must override this method</remarks>
  Public Sub Draw(ByVal drawPhase As esriDrawPhase, ByVal Display As IDisplay, ByVal trackCancel As ITrackCancel) Implements ILayer.Draw

  End Sub
#End Region
#End Region

#Region "IDynamicLayer Members"

  ''' <summary>
  ''' The dynamic layer draw method
  ''' </summary>
  ''' <param name="DynamicDrawPhase">the current drawphase of the drawing</param>
  ''' <param name="Display">the ActiveView's display</param>
  ''' <param name="DynamicDisplay">the ActiveView's dynamic display</param>
  ''' <remarks>This method is set as abstract, which means that the inherited class must override it</remarks>
  Public MustOverride Sub DrawDynamicLayer(ByVal DynamicDrawPhase As esriDynamicDrawPhase, ByVal Display As IDisplay, ByVal DynamicDisplay As IDynamicDisplay) Implements IDynamicLayer.DrawDynamicLayer

  ''' <summary>
  ''' set the dirty flag of the layer
  ''' </summary>
  ''' <param name="DynamicDrawPhase"></param>
  Public Property DynamicLayerDirty(ByVal DynamicDrawPhase As ESRI.ArcGIS.Display.esriDynamicDrawPhase) As Boolean Implements ESRI.ArcGIS.Carto.IDynamicLayer.DynamicLayerDirty
    Get
      Select Case DynamicDrawPhase
        Case esriDynamicDrawPhase.esriDDPCompiled
          Return m_bIsCompiledDirty
        Case esriDynamicDrawPhase.esriDDPImmediate
          Return m_bIsImmediateDirty
      End Select
      Return False
    End Get
    Set(ByVal value As Boolean)
      Select Case DynamicDrawPhase
        Case esriDynamicDrawPhase.esriDDPCompiled
          m_bIsCompiledDirty = value
        Case esriDynamicDrawPhase.esriDDPImmediate
          m_bIsImmediateDirty = value
      End Select
    End Set
  End Property

  ''' <summary>
  ''' Return the rate in which display lists gets recompiled. This is only relevant
  ''' for items that gets drawn using the 'esriDynamicDrawPhase.esriDDPCompiled' drawphase.
  ''' </summary>
  Public ReadOnly Property DynamicRecompileRate() As Integer Implements IDynamicLayer.DynamicRecompileRate
    Get
      Return m_nDynamicRecompileRate
    End Get
  End Property

#End Region

#Region "ILayerGeneralProperties Members"

  ''' <summary>
  ''' Last maximum scale setting used by layer.
  ''' </summary>
  Public Overridable ReadOnly Property LastMaximumScale() As Double Implements ILayerGeneralProperties.LastMaximumScale
    Get
      Return 0
    End Get
  End Property

  ''' <summary>
  ''' Last minimum scale setting used by layer.
  ''' </summary>
  Public Overridable ReadOnly Property LastMinimumScale() As Double Implements ILayerGeneralProperties.LastMinimumScale
    Get
      Return 0
    End Get
  End Property

  ''' <summary>
  ''' Description for the layer.
  ''' </summary>
  Public Overridable Property LayerDescription() As String Implements ILayerGeneralProperties.LayerDescription
    Get
      Return Nothing
    End Get
    Set(ByVal value As String)

    End Set
  End Property
#End Region

#Region "ILayerExtensions Members"
  ''' <summary>
  ''' Removes the specified extension.
  ''' </summary>
  ''' <param name="Index"></param>
  'Public Overridable Sub RemoveExtension(ByVal Index As Integer)
  Public Sub RemoveExtension(ByVal Index As Integer) Implements ESRI.ArcGIS.Carto.ILayerExtensions.RemoveExtension
    If Index < 0 Or Index > m_extensions.Count - 1 Then
      Return
    End If

    m_extensions.RemoveAt(Index)
  End Sub

  ''' <summary>
  ''' Number of extensions.
  ''' </summary>
  Public ReadOnly Property ExtensionCount() As Integer Implements ESRI.ArcGIS.Carto.ILayerExtensions.ExtensionCount
    Get
      Return m_extensions.Count
    End Get
  End Property

  ''' <summary>
  ''' Adds a new extension.
  ''' </summary>
  ''' <param name="ext"></param>
  Public Sub AddExtension(ByVal ext As Object) Implements ESRI.ArcGIS.Carto.ILayerExtensions.AddExtension
    If ext Is Nothing Then
      Return
    End If

    m_extensions.Add(ext)
  End Sub

  ''' <summary>
  ''' The extension at the specified index. 
  ''' </summary>
  ''' <param name="Index"></param>
  Public ReadOnly Property Extension(ByVal Index As Integer) As Object Implements ESRI.ArcGIS.Carto.ILayerExtensions.Extension
    Get
      If Index < 0 Or Index > m_extensions.Count - 1 Then
        Return Nothing
      End If

      Return m_extensions(Index)
    End Get
  End Property
#End Region

#Region "Data Structure basic functionality"
  ''' <summary>
  ''' Create a new item (does not add it to the layer)
  ''' </summary>
  ''' <returns></returns>
  Public Overridable Function NewItem() As DataRow
    Return m_table.NewRow()
  End Function

  ''' <summary>
  ''' Add an item to the layer
  ''' </summary>
  ''' <param name="row"></param>
  Public Overridable Sub AddItem(ByVal row As DataRow)
    m_table.Rows.Add(row)
  End Sub

  ''' <summary>
  ''' Add an item to the Layer
  ''' </summary>
  ''' <param name="values"></param>
  Public Overridable Sub AddItem(ByVal values As Object())
    m_table.Rows.Add(values)
  End Sub

  ''' <summary>
  ''' Query for items in the layer
  ''' </summary>
  ''' <param name="queryFilter">WHERE clause</param>
  ''' <returns></returns>
  Public Overridable Overloads Function [Select](ByVal queryFilter As String) As DataRow()
    Return m_table.Select(queryFilter)
  End Function

  ''' <summary>
  ''' Remove all items in the layer
  ''' </summary>
  Public Overridable Sub Clear()
    m_table.Rows.Clear()
  End Sub

  ''' <summary>
  ''' Remove the item from the layer
  ''' </summary>
  ''' <param name="row">The row to remove</param>
  Public Overridable Sub RemoveItem(ByVal row As DataRow)
    m_table.Rows.Remove(row)
  End Sub

  'indexer. Pass an index and return a record
  Default Public ReadOnly Property Item(ByVal index As Integer) As DataRow
    Get
      Return m_table.Rows(index)
    End Get
  End Property

  ''' <summary>
  ''' return the number of geometries in the layer
  ''' </summary>
  Public ReadOnly Property NumOfRecords() As Integer
    Get
      Return m_table.Rows.Count
    End Get
  End Property
#End Region

#Region "IEnumerable Members"

  ''' <summary>
  ''' Allow users to directly enumerate through the layer's records
  ''' </summary>
  ''' <returns></returns>
  Public Function GetEnumerator() As IEnumerator Implements IEnumerable.GetEnumerator
    Return m_table.Rows.GetEnumerator()
  End Function

#End Region

#Region "IDisposable Members"

  ''' <summary>
  ''' Dispose the layer
  ''' </summary>
  Public Shadows Sub Dispose() Implements IDisposable.Dispose
    m_extent = Nothing
    m_spatialRef = Nothing

    MyBase.Dispose()
  End Sub

#End Region

End Class