Showing posts with label ASP.Net Learnings. Show all posts
Showing posts with label ASP.Net Learnings. Show all posts

Saturday, 13 August 2011

Is accessing the ClientID pointless until DataBind complete?

I have a tricky situation:

I am using CustomTemplates to render the contents of a Repeater control which is bound to a custom entity collection.

Some such controls are a pair of Input + TextBox controls.

The requirement is that the TextBox should not be enabled until the Input is selected/checked.

To do this, i was planning on defining a client side event and event handler such as:

EnableTextBox(int txtBoxId)

Which would take the ID of the text box control and then enable it.

This function would then be bound to the Input controls 'onclick' event.

(I would also need a DisableTextBox() function bound to the Input controls 'onchange' event to handle the situation when the user choose a different option and therefore the Textbox should be cleared and disabled)

PROBLEM:

In order to do the above, during the DataBind of the Repeater, whilst the control collection is being built, i was hoping to 'hardcode' the ID of the TextBox into the call to the EnableTextBox function within the 'onclick' event of the Input control such that the markup would look like this:

<input onclick="EnableTextBox('ContentPlaceHolder_rptrQuestions_txt136_18') type="checkbox" />

However, because all this is happening during the Repeater.DataBind event, and since ASP.Net prefixes the IDs of controls with their container names, it is not good enough to simply put the server side ID of the TextBox control in the EnableTextBox() function call...

This means that my input element in the final rendering looks like:

<input onclick="EnableTextBox('txt136') type="checkbox" />
 

...and my EnableTextBox function will never find such a TextBox with ID 'txt136' because it doesn't exist!!!

Ah, but what about the ClientID property you say???

Well folks, the problem there is, the ClientID property, up until the DataBind event is complete, simply contains the server side ID value!!! The prefixing appears to occur AFTER the DataBind is complete...thereby rending my approach useless (it would appear!)

So, what is the solution???

ASP.Net CheckBox OnClick / OnClientClick

The short story is: an ASP.Net CheckBox control (or any other control aside from Button) does not have an OnClick or OnClientClick event.

One way to over come this is to use the InputAttributes (for CheckBox, RadioButton controls) or Attributes (for TextBox controls)collection.

Add an entry to this collection using the same syntax as an HTML onclick event handler:

<example code>

You can now define Client Side event handling in Server Side code!

Thursday, 4 August 2011

How To: Define Custom EventArgs for the ServerValidate event fired by a CustomValidator

Question
How can i either:

1) Put a string of my choice in the ServerValidateEventArgs.Value Property
OR
2) Define my own EventArgs class and pass that as an argument to the ServerValidate event of a CustomValidator control.

Context
Building a dynamic data driven Web Survey application.
I have a Repeater control, which uses custom template (implementing ITemplate) to render its contents.
Each row of the Repeater represents a Survey Question. Each Question can potentially have multiple answer options.
Hence, for each Row in the Repeater, there exists a bunch of dynamically generated Input controls (EG RadioButton, CheckBox, TextBox)

Requirement
For some specific Rows in the Repeater, at least one of the inputs needs to contain a value (IE that question is mandatory)

Solution So Far
I have placed a CustomValidator control in each Row of the Repeater and hooked up its ServerValidate event to a handler within the custom template class.

The Road Block
I now need to be able to access the all the Input controls within that Repeater row to Validate whether they have been Checked or not.

The handler of the ServerValidate event currently only gives me access to:
a. A reference to the CustomValidator
b. The ServerValidateEventArgs object - in turn providing the ServerValidateEventArgs.Value property.

However, ASP.Net sets ServerValidateEventArgs.Value by default to be the Value property of the control defined in the CustomValidator.ControlToValidate property. But i am not setting CustomValidator.ControlToValidate to anything, as there is not one particular control i am trying to Validate - i'm trying to validate a group of controls!

Wednesday, 3 August 2011

How Does ASP.Net Retain Repeater State?

I'm working on a project which utilises a Repeater control to serve up a set of complicated, dynamic survey questions.
What baffeled me for some time was:
How do i retrieve the answers to the questions on PostBack?
Seems like a pretty amateur question I know...but the problem was my own assumption:
"I should only be binding the Repeater on initial Page Load (ie !IsPostBack || Not IsPostBack) otherwise the answers to the questions will be over-ridden."
However, to my ultimate suprise, this is not the case.
In fact, i the retrieve the set of questions from the Database and bind them to the Repeater on each and every PostBack...(not efficient I know, should probably retrieve from Session on all but initilal PageLoads).
Either way, ASP.Net somehow manages this and doesn't wipe the state of the Repeater clean...the answers are maintained !
I want to know: how does it do this???
The obvious guess is ViewState - but even still, how does it know not to splat the state after i've re-assigned the DataSource and called DataBind()???
What if i actually wanted to Reset the whole thing and actually destroy the state, start from scratch again?
What am i missing here?

Monday, 1 August 2011

Server Validation of Repeater Items

Why does adding two CSS classes in code-behind, using CssClass attributes onld add one???

Adding a space in the middle of the CssClass property string results in the the string being Right Trimmed and only the first string being set as the css class property on the rendered HTML element:

        pnlCntrl.CssClass = "alt repeaterRow"

However, using the assignment addition operator, gives the correct results:

        pnlCntrl.CssClass = "alt"
        pnlCntrl.CssClass += " repeaterRow"

What .Net Site Performance Tools Are There?

If I'm building a site in ASP.Net, what tools are there to help determine performance bottlenecks?

  1. ANTS Performance Profiler
  2. Use Response.Write at the beginning and end of each method and include timings to see how long each method takes
  3. Add Trace=True directive on each ASPX page to get a breakdown of the time elapsed in each page's life-cycle.

Thursday, 21 July 2011

Response.Redirect VS Server.Transfer

This article is the best, succinct, explanation of the differences in using Response.Redirect VS Server.Transfer

Tuesday, 19 July 2011

Session Management

Tasked with upgrading a legacy survey application, i noticed they don't have any defensive programming surrounding their access to HttpContext.Current.Session.

So i changed it from direct access calls such as:

System.Web.HttpContext.Current.Session("KEY")

To utilize a TryParse pattern like this:

    ''' <summary>
    ''' Safely retrieves a the value of the given Key from Session
    ''' </summary>
    ''' <returns>False if Session is Nothing or if the given Key is not in Session, True if successful</returns>
    Public Shared Function TryGetSessionValue(ByVal Key As String, ByRef out_result As Object) As Boolean

        If Not System.Web.HttpContext.Current.Session Is Nothing Then
            If Not System.Web.HttpContext.Current.Session(Key) Is Nothing Then
                out_result = System.Web.HttpContext.Current.Session(Key)
            Else
                Return False
                'Throw New Exception(String.Format(Constants.ERR_MISSING_SESSION_KEY, Key))
            End If
        Else
            Return False
            'Throw New Exception(Constants.ERR_INVALID_SESSION)
        End If

        Return True
    End Function

You've probably noticed the commented out Throw statements - before utilizing the TryParse pattern, i was planning on having this method Throw exceptions so the Application crashes if Session is not valid. But this was a bit harsh...

Instead I now leave it up to the caller to decide their missing Session value means - eg is it Critical, LogOnly or Ignore levels of severity...

Friday, 15 July 2011

Dynamic Repeater ItemTemplate: Retrieving your entity on DataBinding

I am building a survey application.

The collection of questions will be bound to a repeater.

Each item in the Repeater will need to have its content dynamically constructed based on in-code logic ... for example the type of question and the answer options.

So i read this article on how to Create Web Server Control Templates Dynamically.

This was a good start.

The only major difference for me was that i wasn't binding directly to a table retrieved from a database containing columns but instead a custom EntitySpaces entity (could be any other in-house business object though).

The thing i struggled to find examples of was how to retrieve the entity during the data binding, so that you can actually tailor the contents of the Template based on the properties of the entity.

In short, the way to do it is to make use of the DataItem property of the RepeaterItem class (this article gave it away for me)

This is how its done:

 Public Class SurveyQuestionTemplate
    Implements ITemplate

    Dim templateType As ListItemType
    Dim question As SurveyQuestion

    Sub New(ByVal type As ListItemType)
        templateType = type
    End Sub

    Sub InstantiateIn(ByVal container As Control) Implements ITemplate.InstantiateIn
        Dim lc As New Literal()

        Select Case templateType

            Case ListItemType.Header
                lc.Text = "<div id='header'>Survey Questions</div>"

            Case ListItemType.Item
                ' Instead of rendering static HTML, create dynamic controls within a handler to the DataBinding event 
                AddHandler lc.DataBinding, AddressOf SurveyQuestionTemplate_DataBinding

            Case ListItemType.AlternatingItem
                ' Instead of rendering static HTML, create dynamic controls within a handler to the DataBinding event 
                AddHandler lc.DataBinding, AddressOf SurveyQuestionTemplate_DataBinding

            Case ListItemType.Footer
                lc.Text = "<div id='footer'>End Survey Questions</div>"
        End Select

        container.Controls.Add(lc)
    End Sub

    Private Sub SurveyQuestionTemplate_DataBinding(ByVal sender As Object, ByVal e As System.EventArgs)
        'Get a reference to the template item
        Dim litCntrl As Literal
        litCntrl = CType(sender, Literal)

        'Get the naming container
        Dim container As RepeaterItem
        container = CType(litCntrl.NamingContainer, RepeaterItem)

        'Set the dynamic content you want
        litCntrl.Text = GenerateRowContent(container)
    End Sub

    Function GenerateRowContent(container As RepeaterItem) As String
        Dim html As String = ""

        'Retrieve the Question entity
        question = CType(container.DataItem, SurveyQuestion)
       
        'I now have my Question entity which i can use and inspect as necessary
        html = GenerateHTMLForQuestion(question)
        Return html
    End Function
End Class