Common PostbackManager
Common_PostbackManager_VBNet\PostbackManager_VBNet\PostbackManager.vb
' Copyright 2010 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 PostbackManager_VBNet
  #Region "PostbackManager Implementation"

    <AjaxControlToolkit.ClientScriptResource("PostbackManager_VBNet.PostbackManager", "PostbackManager.js")> _
 Public Class PostbackManager
        Inherits ESRI.ArcGIS.ADF.Web.UI.WebControls.WebControl
#Region "Member Variables"

        ' Stores the argument passed to the server during asynchronous requests
        Private _callbackArgument As String = Nothing
        ' Stores user-specified custom arguments to be passed to the client tier requestProcessed event
        Private _customResults As String = Nothing

        ' Delegate for the RequestReceived events
        Private Event _requestReceivedHandler As PostbackManager_VBNet.RequestReceivedEventHandler

#End Region

#Region "Public Properties - ExposeAdfRequestEvents, CustomResults"

        ''' <summary>
        ''' Enables event handling for requests initiated by Web ADF controls.  When true, all Web ADF requests
        ''' will be re-routed through the PostbackManager.
        ''' </summary>
        <System.ComponentModel.Category("PostbackManager"), System.ComponentModel.DefaultValue(True), System.ComponentModel.Description("Enables event handling for requests initiated by Web ADF controls"), System.Web.UI.PersistenceMode(System.Web.UI.PersistenceMode.Attribute)> _
        Public Property ExposeAdfRequestEvents() As Boolean
            Get
                If (Me.StateManager.GetProperty("ExposeAdfRequestEvents") Is Nothing) Then
                    Return True
                Else
                    Return CBool(Me.StateManager.GetProperty("ExposeAdfRequestEvents"))
                End If
            End Get
            Set(ByVal value As Boolean)
                Me.StateManager.SetProperty("ExposeAdfRequestEvents", Value)
            End Set
        End Property

        ''' <summary>
        ''' Stores properties that are returned to the client via the arguments object passed to the client tier
        ''' requestProcessed event.  Can be used to pass data to the client without manipulating callback results.
        ''' </summary>
        <System.ComponentModel.Browsable(False)> _
        Public Property CustomResults() As String
            Get
                Return _customResults
            End Get
            Set(ByVal value As String)
                _customResults = Value
            End Set
        End Property

#End Region

#Region "ASP.NET WebControl Life Cycle Event Overrides - OnPreRender, RenderContents"

        ' Registers script to create the client tier singleton PostbackManager instance
        Protected Overrides Sub OnPreRender(ByVal e As System.EventArgs)
            MyBase.OnPreRender(e)

            ' Only execute initialization logic during full page postbacks
            If MyBase.IsAsync Then
                Return
            End If

            ' Initialize a dictionary with the properties needed to initialize the client tier
            ' PostbackManager singleton instance
            Dim clientPropertyDictionary As System.Collections.Generic.Dictionary(Of String, Object) = New System.Collections.Generic.Dictionary(Of String, Object)()
            clientPropertyDictionary.Add("uniqueID", Me.UniqueID)
            clientPropertyDictionary.Add("callbackFunctionString", Me.CallbackFunctionString)
            clientPropertyDictionary.Add("exposeAdfRequestEvents", Me.ExposeAdfRequestEvents)

            ' Serialize the properties to JSON
            Dim jsSerializer As System.Web.Script.Serialization.JavaScriptSerializer = New System.Web.Script.Serialization.JavaScriptSerializer()
            Dim clientPropertyJson As String = jsSerializer.Serialize(clientPropertyDictionary)

            ' Construct JavaScript to instantiate the client tier PostbackManager singleton
            Dim scriptKey As String = "ESRI_PostbackManager_Script"
            Dim initializationScript As String = "Sys.Application.add_init(function() {{" & ControlChars.CrLf & "                if (ESRI.ADF.Samples.PostbackManager)" & ControlChars.CrLf & "                    return;" & ControlChars.CrLf & ControlChars.CrLf & "                ESRI.ADF.Samples.PostbackManager = " & ControlChars.CrLf & "                    $create(ESRI.ADF.Samples._PostbackManager, {0}, null, null, $get('{1}'));" & ControlChars.CrLf & "            }});"
            initializationScript = String.Format(initializationScript, clientPropertyJson, Me.ClientID)

            ' Register the script to execute on application startup
            System.Web.UI.ScriptManager.RegisterStartupScript(Me, Me.GetType(), scriptKey, initializationScript, True)
        End Sub

        ' Creates a design-time representation of the control
        Protected Overrides Sub RenderContents(ByVal writer As System.Web.UI.HtmlTextWriter)
            If Me.DesignMode Then
                Me.RenderDesignTimeHtml(writer)
            Else
                MyBase.RenderContents(writer)
            End If
        End Sub

#End Region

#Region "ICallbackEventHandler Overrides - GetCallbackResult, RaiseCallbackEvent"

        ' Initiates callback events on the calling control, raises the RequestReceived event,
        ' and constructs script to invoke the client tier requestProcessed event
        Public Overrides Function GetCallbackResult() As String
            ' Parse the callback arguments into a name-value collection
            Dim callbackArgs As System.Collections.Specialized.NameValueCollection = ESRI.ArcGIS.ADF.Web.UI.WebControls.CallbackUtility.ParseStringIntoNameValueCollection(_callbackArgument)
            Dim callingControlID As String = callbackArgs("ControlID")

            ' Make sure a calling control was specified in the request arguments
            If Not callingControlID Is Nothing Then
                ' Retrieve the calling control
                Dim callingControl As System.Web.UI.Control = PostbackManager_VBNet.PostbackManager.FindControlByClientID(Me.Page, callingControlID)

                ' Fire the RequestReceived event
                Dim requestReceivedArgs As PostbackManager_VBNet.AdfRequestEventArgs = New PostbackManager_VBNet.AdfRequestEventArgs(callingControl, _callbackArgument)
                Me.OnRequestReceived(requestReceivedArgs)

                ' Invoke ICallbackEventHandler members on the calling control if that control is not the 
                ' PostbackManager.  This also copies callback results from the calling control.
                If Not callingControl Is Me Then
                    Me.DoCallerCallback(callingControl, _callbackArgument)
                End If

                ' Convert callback results into a JSON serializable object
                Dim jsSerializer As System.Web.Script.Serialization.JavaScriptSerializer = New System.Web.Script.Serialization.JavaScriptSerializer()
                Dim callbackResults As Object = Nothing
                If Not Me.CallbackResults.ToString() Is Nothing Then
                    callbackResults = jsSerializer.DeserializeObject(Me.CallbackResults.ToString())
                End If

                ' Initialize a dictionary with the results to be returned to the client tier 
                ' requestProcessed event
                Dim resultsDictionary As System.Collections.Generic.Dictionary(Of String, Object) = New System.Collections.Generic.Dictionary(Of String, Object)()
                resultsDictionary.Add("callingControlID", callingControlID)
                resultsDictionary.Add("callbackResults", callbackResults)
                resultsDictionary.Add("customResults", Me.CustomResults)

                ' Serialize the results to JSON
                Dim resultsJson As String = jsSerializer.Serialize(resultsDictionary)

                ' Embed JavaScript to invoke the client tier requestProcessed event in a callback result
                Dim raiseRequestProcessedScript As String = String.Format("ESRI.ADF.Samples.PostbackManager._raiseEvent('requestProcessed', {0});", resultsJson)
                Me.CallbackResults.Add(ESRI.ArcGIS.ADF.Web.UI.WebControls.CallbackResult.CreateJavaScript(raiseRequestProcessedScript))
            End If

            Return Me.CallbackResults.ToString()
        End Function

        ' Retrieves the argument passed to the server
        Public Overrides Sub RaiseCallbackEvent(ByVal eventArgument As String)
            MyBase.RaiseCallbackEvent(eventArgument)
            _callbackArgument = eventArgument
        End Sub

#End Region

#Region "IPostBackEventHandler Overrides - RaisePostBackEvent"

        ' Invokes web tier asynchronous request handling logic
        Public Overrides Sub RaisePostBackEvent(ByVal eventArgument As String)
            Me.RaiseCallbackEvent(eventArgument)
            MyBase.RaisePostBackEvent(eventArgument)
        End Sub

#End Region

#Region "Event Wiring - RequestReceived"

        ''' <summary>
        ''' Occurs when the web request has reached the server, but before the request has been processed
        ''' on the server by the Web ADF control initiating the request.
        ''' </summary>
        Public Custom Event RequestReceived As RequestReceivedEventHandler
            AddHandler(ByVal value As RequestReceivedEventHandler)
                AddHandler _requestReceivedHandler, value
            End AddHandler
            RemoveHandler(ByVal value As RequestReceivedEventHandler)
                RemoveHandler _requestReceivedHandler, value
            End RemoveHandler
            RaiseEvent(ByVal sender As Object, ByVal args As PostbackManager_VBNet.AdfRequestEventArgs)
            End RaiseEvent
        End Event

        ''' <summary>
        ''' Raises the RequestReceived event.
        ''' </summary>
        ''' <param name="args">A RequestProcessingEventArgs object that contains the Web ADF control that initiated the request.</param>
        Protected Overridable Sub OnRequestReceived(ByVal args As PostbackManager_VBNet.AdfRequestEventArgs)
            If Not _requestReceivedHandlerEvent Is Nothing Then
                RaiseEvent _requestReceivedHandler(Me, args)
            End If
        End Sub

#End Region

#Region "Private Methods"

        ' Calls RaiseCallbackEvent and GetCallbackResult on the passed-in control and copies callback results to 
        ' the PostbackManager
        Private Sub DoCallerCallback(ByVal control As System.Web.UI.Control, ByVal callbackArgument As String)
            ' Check which type of Web ADF base control the passed-in control is.  Then invoke the control's 
            ' callback event methods and copy its callback results.
            If TypeOf control Is ESRI.ArcGIS.ADF.Web.UI.WebControls.WebControl Then
                Dim webControl As ESRI.ArcGIS.ADF.Web.UI.WebControls.WebControl = TryCast(control, ESRI.ArcGIS.ADF.Web.UI.WebControls.WebControl)
                webControl.RaiseCallbackEvent(callbackArgument)
                webControl.GetCallbackResult()
                Me.CallbackResults.CopyFrom(webControl.CallbackResults)
            ElseIf TypeOf control Is ESRI.ArcGIS.ADF.Web.UI.WebControls.CompositeControl Then
                Dim compositeControl As ESRI.ArcGIS.ADF.Web.UI.WebControls.CompositeControl = TryCast(control, ESRI.ArcGIS.ADF.Web.UI.WebControls.CompositeControl)
                compositeControl.RaiseCallbackEvent(callbackArgument)
                compositeControl.GetCallbackResult()
                Me.CallbackResults.CopyFrom(compositeControl.CallbackResults)
            End If
        End Sub

        ' Searches the passed-in control and all its child controls, returning that having the passed-in
        ' client ID, if found
        Private Shared Function FindControlByClientID(ByVal rootControl As System.Web.UI.Control, ByVal controlID As String) As System.Web.UI.Control
            ' Return the passed-in control if it has the passed-in ID
            If rootControl.ClientID = controlID Then
                Return rootControl
            End If

            ' Iterate through the control's child controls, recursively calling FindControl on each.
            ' If at any point a control with the passed-in ID is found, return it.
            For Each childControl As System.Web.UI.Control In rootControl.Controls
                Dim foundControl As System.Web.UI.Control = FindControlByClientID(childControl, controlID)
                If Not foundControl Is Nothing Then
                    Return foundControl
                End If
            Next childControl

            Return Nothing
        End Function

        ' Renders the control at design-time
        Private Sub RenderDesignTimeHtml(ByVal writer As System.Web.UI.HtmlTextWriter)
            ' Create a table to format the design-time display
            Dim table As System.Web.UI.WebControls.Table = New System.Web.UI.WebControls.Table()
            table.Font.Name = Me.Font.Name
            table.Font.Size = Me.Font.Size
            table.Style(System.Web.UI.HtmlTextWriterStyle.BackgroundColor) = "white"
            table.BorderColor = System.Drawing.Color.Black
            table.BorderStyle = System.Web.UI.WebControls.BorderStyle.Solid
            table.BorderWidth = 1

            Dim row As System.Web.UI.WebControls.TableRow = New System.Web.UI.WebControls.TableRow()
            table.CellPadding = 4
            table.Rows.Add(row)

            ' Add the control's ID
            Dim cell As System.Web.UI.WebControls.TableCell = New System.Web.UI.WebControls.TableCell()
            row.Cells.Add(cell)
            cell.Style(System.Web.UI.HtmlTextWriterStyle.WhiteSpace) = "nowrap"
            cell.Text = String.Format("{0}<br>", Me.ClientID)

            row = New System.Web.UI.WebControls.TableRow()
            table.Rows.Add(row)

            ' Add the control type
            cell = New System.Web.UI.WebControls.TableCell()
            row.Cells.Add(cell)
            cell.Text = "PostbackManager WebControl"

            table.RenderControl(writer)
        End Sub

#End Region
    End Class

  #End Region

  #Region "Event Delegate and Argument Classes - RequestReceivedEventHandler, AdfRequestEventArgs"

  ''' <summary>Delegate to handle the RequestReceived event.</summary>
  ''' <param name="sender">The PostbackManager Control that initiated the event.</param>
  ''' <param name="args">Arguments that include the ADF Control that initiated the request.</param>
  Public Delegate Sub RequestReceivedEventHandler(ByVal sender As Object, ByVal args As PostbackManager_VBNet.AdfRequestEventArgs)

  ''' <summary>Event arguments for a Web ADF asyncrhonous request.</summary>
  Public Class AdfRequestEventArgs
    Inherits System.EventArgs
    Private _callingControl As System.Web.UI.Control
    Private _requestArguments As String

    ''' <summary>Constructor.</summary>
    ''' <param name="callingControl">The Web ADF control that initiated the request.</param>
    ''' <param name="requestArguments">The arguments passed to the server in the request.</param>
    Public Sub New(ByVal callingControl As System.Web.UI.Control, ByVal requestArguments As String)
      _callingControl = callingControl
      _requestArguments = requestArguments
    End Sub

    ''' <summary>The Web ADF control that initiated the request.</summary>
    Public ReadOnly Property CallingControl() As System.Web.UI.Control
      Get
        Return _callingControl
      End Get
    End Property
    ''' <summary>The arguments passed along with the request.</summary>
    Public ReadOnly Property RequestArguments() As String
      Get
        Return _requestArguments
      End Get
    End Property
  End Class

  #End Region
End Namespace