KamalPatel.net
Back to Downloads/Articles

RunAt="server" (Macro)

By Kamal Patel
May 15, 2001

Background

When we drop a control such as a textbox or a label in the design area, ASP .Net automatically adds the runat=server making it a server side control. However, if we drop a customized control that we created, the runat=server needs to be added explicitly. Also when adding the controls through code, we have to type runat=server for each control. This article demonstrates how we could write a macro for VS .Net that will use the currently open document and add the runat=server in those elements that we specify. If the runat=server is already defined then the macro simply ignores those elements.

Runat= Server

In order to check if controls on our page are server side controls, we have to determine if they contain the string "runat=server" inside them. If they contain the string, then we know that these are server side controls and if not, then we have to add the string "runat=server" at the right position. This program checks if specific controls are server-side controls or not and if not then it makes them server side controls by adding runat=server.

The program is a macro written using VB and is composed of two methods. It contains a publicly exposed method AddRunAtServer() and a private method UpdateDocument(). UpdateDocument() receives an element as a parameter and makes all those elements in the currently open document as server-side controls. AddRunAtServer() is the method that makes multiple calls to UpdateDocument() for each element that we wish to check. AddRunAtServer() is also responsible for creating the Undo context object (discussed later) and handling errors.

The first step is to determine which elements (controls) should be checked by this macro. By default, the macro checks all the elements that begin with <asp:, <form, <script, and <body. You may easily extend this functionality by adding your own elements simply by updating the AddRunAtServer() method. If you created a library say MyLib that contains multiple controls, you will use <MyLib:MyControl .../> syntax in your ASP .Net pages. In order to make sure that all the controls from this library are server side controls you will update the AddRunAtServer() method by adding: UpdateDocument("<MyLib:")

Creating an Undo context object allows the user to undo all the changes made by this program simply by CTRL+Z or selecting undo from the edit menu. The code fragment below explains each line of code as we move through the macro. 

'This macro checks the specified elements if they have runat=server in
'them and if not then automatically adds runat=server in them
Sub AddRunAtServer()
      'Create an Undo context object so all the changes can be
      'undone by CTRL+Z
      Dim oUnDo As UndoContext = DTE.UndoContext
      oUnDo.Open("Comment Line")         
       
      'Supress the User Interface. This will make it run faster
      'and make all the changes appear once
      DTE.SuppressUI = True
       
      Try
      'Make a call to UpdateDocument()
            UpdateDocument("<asp:")
            UpdateDocument("<form")
            UpdateDocument("<script")
            UpdateDocument("<body")
           
            'Finally Close the undo
            oUnDo.Close()
      Catch oException As system.Exception
            Dim lcErrMsg As String
            lcErrMsg = "An error occured while running this program." _
                              & " Please make sure that you are specifying the" _
                              & " correct parameters."
            MsgBox(lcErrMsg)
           
             'Undo the changes made to the document
            oUnDo.SetAborted()
            DTE.SuppressUI = False
      Finally
            'Rest the Supress UI
            DTE.SuppressUI = False
      End Try
End Sub
   
'This method is used internally to do the actual work for adding
'runat=server for a specified element type
Private Sub UpdateDocument(ByVal tcStringToSearch As String)
       
      'Get a reference to the currently open document
      Dim oDoc As TextDocument
      oDoc = DTE.ActiveDocument.Object("TextDocument")
       
      'Create editpoints for starting and ending positions of the doc
      Dim lnStartPos As EditPoint = oDoc.StartPoint.CreateEditPoint
      Dim lnEndPos As EditPoint = oDoc.EndPoint.CreateEditPoint
       
      'This is the string that we will search and a placeholder string
      Dim lcSearchStr As String = tcStringToSearch
      Dim lcString As String
       
      'Define the private variables used in this process
      Dim lnStrPos As Integer = 0
      Dim lnRunAtPos As Integer = 0
      Dim lnClosingPos As Integer = 0
      Dim lnEmptySpot As Integer = 0
       
      Do While True
      'Get the string and remove all the carriage returns as they
      'are ignored by the EditPoint object
            lcString = LCase(lnStartPos.GetText(lnEndPos))
            lcString = Replace(lcString, Chr(13), "")
           
            'Get the first position of item we are looking for
            lnStrPos = InStr(lcString, lcSearchStr)
           
            If lnStrPos = 0 Then
                'If we do not find the item, exit
                 Exit Do
            Else
                'We found the item that we were looking for
               
                'Shorten the string starting from the new position
                lcString = lcString.Remove(0, lnStrPos _
                                                                              + Len(lcSearchStr))
               
                'Now move the EditPoint to that position as well
                lnStartPos.CharRight(lnStrPos + Len(lcSearchStr))
               
                'Now we have the subsized string, let us check for the
                'first occurance of > is more than the runat
                lnClosingPos = InStr(lcString, ">")
                lnRunAtPos = InStr(lcString, "runat")
               
                'The closing tag's position always HAS to be more
                ' than the runat's position
                If lnRunAtPos = 0 Or lnRunAtPos > lnClosingPos Then
                    'At this point we found that Runat=server is
                    ' missing in this element/object
                   
                    'Locate the first blank spot to make the insertion.
                    lnEmptySpot = InStr(lcString, " ")
                   
                    'Make sure that the blank spot is within the
                      'boundries
                    If lnEmptySpot > lnClosingPos Then
                        'Special handling required
                        'In this case we want to place just before
                        ' the closing position i.e. ">"
                        'However, it is possible that the closing is
                        ' done using />
                        If lcString.Substring(lnClosingPos - 2, 1) = _
                                                      "/" Then
                            lnStartPos.CharRight(lnClosingPos - 2)
                            lnStartPos.Insert(" ")
                        Else
                            lnStartPos.CharRight(lnClosingPos - 1)
                            lnStartPos.Insert(" ")
                        End If
                    Else
                        lnStartPos.CharRight(lnEmptySpot)
                    End If
                   
                    'Once the blank spot is determined and the
                    ' EditPoint is positioned, Make the insertion
                    lnStartPos.Insert("runat=server ")
                End If
            End If
        Loop
    End Sub