Common Custom data source
Common_CustomDataSource_VBNet\REXMLDataSource_VBNet\MapFunctionality.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 Microsoft.VisualBasic
Imports System
Namespace REXMLDataSource_VBNet
    ' Along with IGISDataSource and IGISResource, IGISFunctionality is one of the three required
    ' interfaces for any Web ADF Data Source implementation.  This interface is responsible for
    ' providing members to functionally interact with the underlying data.  Essentially, an 
    ' IGISFunctionality implementation can be thought of as describing what can be done with the 
    ' data.
    '
    ' This particular implementation inherits from IMapFunctionality, which implements 
    ' IGISFunctionality.  IMapFunctionality provides methods and properties that are the foundation
    ' of map operations like zoom and pan.
    Public Class MapFunctionality
        Implements ESRI.ArcGIS.ADF.Web.DataSources.IMapFunctionality
#Region "Instance Variables"

        Private m_displaySettings As ESRI.ArcGIS.ADF.Web.DisplaySettings = Nothing
        Private m_webControl As System.Web.UI.WebControls.WebControl = Nothing
        Private m_maintainsState As Boolean = False
        Private m_spatialReference As ESRI.ArcGIS.ADF.Web.SpatialReference.SpatialReference = Nothing
        Private m_name As String = String.Empty
        Private m_gisResource As ESRI.ArcGIS.ADF.Web.DataSources.IGISResource = Nothing
        Private m_initialized As Boolean = False
        Private m_graphicsDataSet As ESRI.ArcGIS.ADF.Web.Display.Graphics.GraphicsDataSet = Nothing

#End Region

#Region "Constructor"

        ' Constructor that initializes functionality name and the resource with which the
        ' functionality is associated
        Public Sub New(ByVal name As String, ByVal resource As REXMLDataSource_VBNet.MapResource)
            m_name = name
            m_gisResource = resource
        End Sub

#End Region

#Region "REXML MapFunctionality Members"

        ' Allows access to the MapResource object associated with the functionality
        Public ReadOnly Property MapResource() As REXMLDataSource_VBNet.MapResource
            Get
                Return TryCast(m_gisResource, REXMLDataSource_VBNet.MapResource)
            End Get
        End Property

        ' Key for storing and retrieving object state to and from the underlying data source's state table
        Private ReadOnly Property FunctionalityStateKey() As String
            Get
                Dim szResource As String = CType(m_gisResource, Object).GetType().ToString() & ":" & m_gisResource.Name
                Dim szThis As String = Me.GetType().ToString() & ":" & m_name
                Return (szResource & "," & szThis)
            End Get
        End Property

        ' Allows read access to the GraphicsDataSet containing the REXML data
        Public ReadOnly Property GraphicsDataSet() As ESRI.ArcGIS.ADF.Web.Display.Graphics.GraphicsDataSet
            Get
                ' Check whether the functionality maintains state
                If (Not m_maintainsState) Then
                    ' Get a reference to the MapResource associated with the functionality
                    Dim mapResource As REXMLDataSource_VBNet.MapResource = TryCast(m_gisResource, REXMLDataSource_VBNet.MapResource)
                    ' If the MapResource's GraphicsDataSet is null, return null.  Otherwise,
                    ' return the dataset.
                    If mapResource Is Nothing Then
                        Return Nothing
                    Else
                        Return mapResource.Graphics
                    End If
                Else
                    ' check whether the functionality instance's graphics dataset is null
                    If Not m_graphicsDataSet Is Nothing Then
                        ' return the instance graphics dataset
                        Return m_graphicsDataSet
                    Else
                        ' Get a reference to the MapResource associated with the functionality
                        Dim mapResource As REXMLDataSource_VBNet.MapResource = TryCast(m_gisResource, REXMLDataSource_VBNet.MapResource)

                        ' Make sure neither the MapResource nor the MapResource's graphics dataset
                        ' are null
                        If Not mapResource Is Nothing AndAlso Not mapResource.Graphics Is Nothing Then
                            ' Initialize the instance's graphics dataset as a clone of the associated
                            ' MapResource's
                            m_graphicsDataSet = mapResource.Graphics.Clone()
                        End If

                        ' return the functionality instance's graphics dataset
                        Return m_graphicsDataSet
                    End If
                End If
            End Get
        End Property

        ' Returns the extent of the Web ADF Map Control associated with the functionality
        Public Function GetMapExtent() As ESRI.ArcGIS.ADF.Web.Geometry.Envelope
            ' Attempt to get a reference to the Map Control associated with the instance by casting
            ' the instance's WebControl member
            Dim adfMap As ESRI.ArcGIS.ADF.Web.UI.WebControls.Map = TryCast(m_webControl, ESRI.ArcGIS.ADF.Web.UI.WebControls.Map)

            ' If a reference was successfully established, return the contents of the control's Extent
            ' property.  Otherwise, return null.
            If adfMap Is Nothing Then
                Return Nothing
            Else
                Return adfMap.Extent
            End If
        End Function

        ' Returns the screen width and height in pixels of the Web ADF Map Control associated
        ' with the functionality
        Public Sub GetMapDimensions(<System.Runtime.InteropServices.Out()> ByRef width As Integer, <System.Runtime.InteropServices.Out()> ByRef height As Integer)
            width = -1
            height = -1

            ' Attempt to get a reference to the Map Control associated with the instance by casting
            ' the instance's WebControl member
            Dim adfMap As ESRI.ArcGIS.ADF.Web.UI.WebControls.Map = TryCast(m_webControl, ESRI.ArcGIS.ADF.Web.UI.WebControls.Map)

            ' Make sure a reference to the Map Control was successfully established
            If adfMap Is Nothing Then
                Return
            End If

            ' Make sure the Map Control has a valid TilingScheme for the REXML resource
            If adfMap.GetTilingScheme(m_gisResource.Name) Is Nothing Then
                Return
            End If

            ' Set the width and height output parameters
            width = adfMap.GetTilingScheme(m_gisResource.Name).ViewWidth
            height = adfMap.GetTilingScheme(m_gisResource.Name).ViewHeight
        End Sub

        ' Allows internal retrieval by layer ID of a layer referenced by the functionality
        Private Function getLayer(ByVal layerID As String) As ESRI.ArcGIS.ADF.Web.Display.Graphics.GraphicsLayer
            ' Cast the table in the functionality's graphics dataset at the passed-in layer ID
            ' to a Web ADF GraphicsLayer and return
            Dim adfGraphicsLayer As ESRI.ArcGIS.ADF.Web.Display.Graphics.GraphicsLayer = CType(m_graphicsDataSet.Tables(layerID), ESRI.ArcGIS.ADF.Web.Display.Graphics.GraphicsLayer)
            Return adfGraphicsLayer
        End Function

#End Region

#Region "IMapFunctionality Members"

#Region "IMapFunctionality Properties"

        ' Allows read/write access to the flag indicating whether the functionality maintains state
        Public Property MaintainsState() As Boolean Implements ESRI.ArcGIS.ADF.Web.DataSources.IMapFunctionality.MaintainsState
            Get
                Return m_maintainsState
            End Get
            Set(ByVal value As Boolean)
                m_maintainsState = Value
            End Set
        End Property

        ' Allows read/write access to the DisplaySettings of the MapResource instance associated with
        ' the functionality
        Public Property DisplaySettings() As ESRI.ArcGIS.ADF.Web.DisplaySettings Implements ESRI.ArcGIS.ADF.Web.DataSources.IMapFunctionality.DisplaySettings
            Get
                ' Get a reference to the functionality's associated MapResource
                Dim rexmlMapResource As REXMLDataSource_VBNet.MapResource = TryCast(m_gisResource, REXMLDataSource_VBNet.MapResource)

                ' Check the flag indicating whether the functionality maintains state.  If not, return
                ' the associated MapResource's DisplaySettings
                If m_maintainsState Then
                    ' Check whether the functionality instance's DisplaySettings object has been initialized
                    If m_displaySettings Is Nothing Then
                        ' Set the instance's DisplaySettings object to a clone of the associated
                        ' MapResource's DisplaySettings
                        m_displaySettings = CType(rexmlMapResource.DisplaySettings.Clone(), ESRI.ArcGIS.ADF.Web.DisplaySettings)
                    End If
                    ' Return the functionality instance's DisplaySettings
                    Return m_displaySettings
                Else
                    Return rexmlMapResource.DisplaySettings
                End If
            End Get
            Set(ByVal value As ESRI.ArcGIS.ADF.Web.DisplaySettings)
                ' Get a reference to the functionality's associated MapResource
                Dim rexmlMapResource As REXMLDataSource_VBNet.MapResource = TryCast(m_gisResource, REXMLDataSource_VBNet.MapResource)

                ' Check whether the functionality maintains state.  If so, set the functionality's
                ' DisplaySettings object to the passed-in value.  If not, set the associated MapResource's
                ' DisplaySettings object to the passed-in value.
                If m_maintainsState Then
                    m_displaySettings = Value
                Else
                    rexmlMapResource.DisplaySettings = Value
                End If
            End Set
        End Property

        ' Not implemented here, but required to be stubbed out for implementations of IMapFunctionality.
        Public ReadOnly Property Units() As ESRI.ArcGIS.ADF.Web.DataSources.Units Implements ESRI.ArcGIS.ADF.Web.DataSources.IMapFunctionality.Units
            Get
                Throw New System.NotImplementedException()
            End Get
        End Property

        ' Allows read access to the IDs of the layers referenced by the functionality's graphics dataset
        Public ReadOnly Property LayerIDs() As Object()
            Get
                ' Make sure neither the functionality's graphics dataset nor the graphics dataset's
                ' tables collection are null
                If m_graphicsDataSet Is Nothing OrElse m_graphicsDataSet.Tables Is Nothing Then
                    Return Nothing
                End If

                ' Dimension the layer ID array with the number of tables in the functionality's 
                ' graphics dataset
                'INSTANT VB NOTE: The local variable layerIDs was renamed since Visual Basic will not allow local variables with the same name as their method:
                Dim layerIDs_Renamed As Object() = New Object(m_graphicsDataSet.Tables.Count - 1) {}

                ' Iterate through the tables in the functionality's graphics dataset, adding the
                ' name of each to the layer ID array
                Dim i As Integer = 0
                Do While i < m_graphicsDataSet.Tables.Count
                    layerIDs_Renamed(i) = m_graphicsDataSet.Tables(i).TableName
                    i += 1
                Loop

                Return layerIDs_Renamed
            End Get
        End Property

        ' Allows read/write access to the functionality's spatial reference
        Public Property SpatialReference() As ESRI.ArcGIS.ADF.Web.SpatialReference.SpatialReference Implements ESRI.ArcGIS.ADF.Web.DataSources.IMapFunctionality.SpatialReference
            Get
                Return m_spatialReference
            End Get
            Set(ByVal value As ESRI.ArcGIS.ADF.Web.SpatialReference.SpatialReference)
                m_spatialReference = Value
            End Set
        End Property

        ' This implementation does not support rotation, but the property must be stubbed out
        ' in order to implement IMapFunctionality
        Public ReadOnly Property Rotation() As Double Implements ESRI.ArcGIS.ADF.Web.DataSources.IMapFunctionality.Rotation
            Get
                Return Double.NaN
            End Get
        End Property
#End Region

#Region "IMapFunctionality Methods"

        ' Applies the ImageDescriptor of the functionality's display settings to its graphics dataset
        Public Sub ApplyStateToDataSourceObjects() Implements ESRI.ArcGIS.ADF.Web.DataSources.IMapFunctionality.ApplyStateToDataSourceObjects
            ' Make sure the functionality's graphics dataset is not null
            If Not m_graphicsDataSet Is Nothing Then
                ' If the functionality's display settings object is not null, apply its ImageDescriptor
                ' to the graphics dataset.  This is actually applying the ImageDescriptor to the
                ' underlying data source, since the graphics dataset references that of the data source.
                If Not m_displaySettings Is Nothing Then
                    m_graphicsDataSet.ImageDescriptor = m_displaySettings.ImageDescriptor
                End If
            End If
        End Sub

        ' Retrieves the ImageDescriptor of the functionality's graphic dataset and applies it to the
        ' functionality's display settings
        Public Sub GetStateFromDataSourceObjects() Implements ESRI.ArcGIS.ADF.Web.DataSources.IMapFunctionality.GetStateFromDataSourceObjects
            ' Make sure the instance's graphics dataset is not null
            If Not m_graphicsDataSet Is Nothing Then
                ' If the instance's display settings object is not null, set its ImageDescriptor to
                ' that of the instance's graphics dataset.  This is actually retrieiving the 
                ' ImageDescriptor from the underlying data source, since the graphics dataset references
                ' that of the data source.
                If Not m_displaySettings Is Nothing Then
                    m_displaySettings.ImageDescriptor = m_graphicsDataSet.ImageDescriptor
                End If
            End If
        End Sub

        ' Retrieves the names and IDs of the layers referenced by the functionality instance
        Public Sub GetLayers(<System.Runtime.InteropServices.Out()> ByRef layerIDs As String(), <System.Runtime.InteropServices.Out()> ByRef layerNames As String()) Implements ESRI.ArcGIS.ADF.Web.DataSources.IMapFunctionality.GetLayers
            layerNames = Nothing
            layerIDs = layerNames

            ' Make sure neither the instance's graphics dataset nor the graphics dataset's tables
            ' collection are null
            If m_graphicsDataSet Is Nothing OrElse m_graphicsDataSet.Tables Is Nothing Then
                Return
            End If

            ' Dimension the layer ID and names arrays with the number of tables in the graphics
            ' dataset's tables collection
            layerIDs = New String(m_graphicsDataSet.Tables.Count - 1) {}
            layerNames = New String(m_graphicsDataSet.Tables.Count - 1) {}

            ' Iterate through the graphics dataset's tables collection, storing the name of each
            ' in both the layer ID and names arrays.  Note that for graphics layers, the layer ID
            ' is the same as the layer name.
            Dim i As Integer = 0
            Do While i < m_graphicsDataSet.Tables.Count
                layerIDs(i) = m_graphicsDataSet.Tables(i).TableName
                layerNames(i) = m_graphicsDataSet.Tables(i).TableName
                i += 1
            Loop
            Return
        End Sub

        ' Not meaningfully implemented here, but stubbed out in order to implement IMapFunctionality
        Public Sub GetVisibleScale(ByVal layerid As String, <System.Runtime.InteropServices.Out()> ByRef minscale As Double, <System.Runtime.InteropServices.Out()> ByRef maxscale As Double) Implements ESRI.ArcGIS.ADF.Web.DataSources.IMapFunctionality.GetVisibleScale
            ' At a minimum, required for MapTips
            minscale = Double.NaN
            maxscale = Double.NaN
        End Sub

        ' Not implemented
        Public Function GetScale(ByVal extent As ESRI.ArcGIS.ADF.Web.Geometry.Envelope, ByVal mapWidth As Integer, ByVal mapHeight As Integer) As Double Implements ESRI.ArcGIS.ADF.Web.DataSources.IMapFunctionality.GetScale
            Throw New System.NotImplementedException()
        End Function

        ' Not meaningfully implemented
        Public Function GetCopyrightText() As System.Collections.Generic.Dictionary(Of String, String) Implements ESRI.ArcGIS.ADF.Web.DataSources.IMapFunctionality.GetCopyrightText
            Return New System.Collections.Generic.Dictionary(Of String, String)()
        End Function

        ' Returns a MapImage containing a map of the layers referenced by the functionality (in MIME
        ' data or as a url) at the passed-in extent
        Public Function DrawExtent(ByVal extentToDraw As ESRI.ArcGIS.ADF.Web.Geometry.Envelope) As ESRI.ArcGIS.ADF.Web.MapImage Implements ESRI.ArcGIS.ADF.Web.DataSources.IMapFunctionality.DrawExtent
            ' Make sure the functionality instance's graphics dataset is not null
            If Not m_graphicsDataSet Is Nothing Then
                ' Update the state of the underlying data source
                ApplyStateToDataSourceObjects()
                ' Return the MapImage generated by the graphics dataset's DrawExtent method
                Return m_graphicsDataSet.DrawExtent(extentToDraw)
            Else
                Return Nothing
            End If
        End Function

        ' Returns the visibility of the layer with the passed-in ID
        Public Function GetLayerVisibility(ByVal layerID As String) As Boolean Implements ESRI.ArcGIS.ADF.Web.DataSources.IMapFunctionality.GetLayerVisibility
            Return getLayer(layerID).Visible
        End Function

        ' Sets the visibility of the layer with the passed-in ID to the passed-in boolean
        Public Sub SetLayerVisibility(ByVal layerID As String, ByVal visible As Boolean) Implements ESRI.ArcGIS.ADF.Web.DataSources.IMapFunctionality.SetLayerVisibility
            getLayer(layerID).Visible = visible
        End Sub

#End Region

#End Region

#Region "IGISFunctionality Members"

#Region "IGISFunctionality Properties"

        ' Allows read/write access to the functionality instance's name
        Public Property Name() As String Implements ESRI.ArcGIS.ADF.Web.DataSources.IMapFunctionality.Name
            Get
                Return m_name
            End Get
            Set(ByVal value As String)
                m_name = Value
            End Set
        End Property

        ' Allows read/write access to the GISResource associated with the functionality instance
        Public Property Resource() As ESRI.ArcGIS.ADF.Web.DataSources.IGISResource Implements ESRI.ArcGIS.ADF.Web.DataSources.IMapFunctionality.Resource
            Get
                Return m_gisResource
            End Get
            Set(ByVal value As ESRI.ArcGIS.ADF.Web.DataSources.IGISResource)
                m_gisResource = Value
            End Set
        End Property

        ' Allows read access to the instance member indicating whether the functionality has been initialized
        Public ReadOnly Property Initialized() As Boolean Implements ESRI.ArcGIS.ADF.Web.DataSources.IMapFunctionality.Initialized
            Get
                Return m_initialized
            End Get
        End Property

        ' Allows retrieving or setting the WebControl associated with the functionality instance
        Public Property WebControl() As System.Web.UI.WebControls.WebControl Implements ESRI.ArcGIS.ADF.Web.DataSources.IMapFunctionality.WebControl
            Get
                Return m_webControl
            End Get
            Set(ByVal value As System.Web.UI.WebControls.WebControl)
                m_webControl = Value
            End Set
        End Property

#End Region

#Region "IGISFunctionality Methods"

        ' Loads the state of the functionality object from the data source's state table
        Public Sub LoadState() Implements ESRI.ArcGIS.ADF.Web.DataSources.IMapFunctionality.LoadState
            ' make sure references to the functionality's associated GISResource, the GISResource's
            ' underlying GISDataSource, and the GISDataSource's state table are all valid
            If m_gisResource Is Nothing OrElse m_gisResource.DataSource Is Nothing OrElse m_gisResource.DataSource.State Is Nothing Then
                Throw New System.Exception("The Resource associated with this functionality is not valid.")
            End If

            ' Some of the functionality instance's properties need to be explicitly stored in the state
            ' table, while others do not.  An overview of these properties and the reasons for storing or
            ' not storing them is as follows:

            ' These properties are shared with and managed by the associated MapResource:
            ' - Display Settings
            ' - DataSource objects: GraphicsDataSet

            ' These properties are shared with and managed by the associated DataSource objects:
            ' - DisplaySettings.ImageDescriptor

            ' These properties are dynamic and therefore do not need to be stored in state:
            ' - LayerIDs
            ' - Scale
            ' - Rotation
            ' - Extent

            ' These properties are non-dynamic and exclusive to this instance, so they need to be 
            ' stored in state:
            ' - WebControl
            ' - MaintainsState
            ' - SpatialReference

            ' Retrieve the functionality's state from the underlying data source's state table.  The
            ' object state will be stored in the table at the index indicated by the private property
            ' FunctionalityStateKey
            Dim functionalityState As Object = m_gisResource.DataSource.State(FunctionalityStateKey)
            ' Check whether the state was found
            If Not functionalityState Is Nothing Then
                ' Cast the state object to a REXML MapFunctionality
                Dim rexmlMapFunctionality As REXMLDataSource_VBNet.MapFunctionality = TryCast(functionalityState, REXMLDataSource_VBNet.MapFunctionality)

                ' Get the properties stored in functionality state (as noted above) from stored state and
                ' assign them to the appropriate instance variables
                m_maintainsState = rexmlMapFunctionality.MaintainsState
                m_webControl = rexmlMapFunctionality.WebControl
                m_spatialReference = rexmlMapFunctionality.SpatialReference

                ' If maintainsState, retrieve this functionality's own copies of the properties that are
                ' shared with the associated MapResource instance and assign them to the appropriate
                ' instance variables
                If m_maintainsState Then
                    m_displaySettings = rexmlMapFunctionality.DisplaySettings
                    m_graphicsDataSet = rexmlMapFunctionality.GraphicsDataSet
                End If
            End If

            ' Load the properties that are shared with the instance's associated DataSource objects. 
            ' This will initialize the relevant instance variables with values from those objects.
            GetStateFromDataSourceObjects()

            ' Any necessary logic for handling dynamic properties (listed above) would go here.
        End Sub

        ' Stores the functionality's state in the underlying data source's state table.
        Public Sub SaveState() Implements ESRI.ArcGIS.ADF.Web.DataSources.IMapFunctionality.SaveState
            ' make sure references to the functionality's associated GISResource, the GISResource's
            ' underlying GISDataSource, and the GISDataSource's state table are all valid
            If m_gisResource Is Nothing Then
                Return
            End If
            If m_gisResource.DataSource Is Nothing Then
                Return
            End If
            If m_gisResource.DataSource.State Is Nothing Then
                Return
            End If

            ' Update the state of the underlying data source objects with relevant properties of the
            ' functionality instance
            ApplyStateToDataSourceObjects()

            ' Store the functionality instance in the data source's state table
            m_gisResource.DataSource.State(FunctionalityStateKey) = Me
        End Sub

        ' Set the flag indicating whether the functionality is intitialized to true.  Any necessary
        ' start-up logic (i.e. instance variable initialization) should go here.
        Public Sub Initialize() Implements ESRI.ArcGIS.ADF.Web.DataSources.IMapFunctionality.Initialize
            m_graphicsDataSet = Me.GraphicsDataSet
            m_initialized = True
        End Sub

        ' Set the flag indicating whether the functionality is intitialized to false.  Any necessary
        ' disposal logic (e.g. releasing object references) should go here.  Note that, if there is
        ' additional logic here, users of this class will have to EXPLCITLY call dispose.  It is not
        ' invoked by other Web ADF components or the Page life-cycle.
        Public Sub Dispose() Implements ESRI.ArcGIS.ADF.Web.DataSources.IMapFunctionality.Dispose
            m_initialized = False
        End Sub

        ' Retrieves boolean indicating whether the operation defined by the passed-in string is
        ' supported by this implementation.  Here, only GetScale is explicitly not implemented.
        Public Function Supports(ByVal operation As String) As Boolean Implements ESRI.ArcGIS.ADF.Web.DataSources.IMapFunctionality.Supports
            If operation = "GetScale" Then
                Return False
            End If
            Return True
        End Function

#End Region

#End Region

    End Class
End Namespace