Web controls lifecycle problems: A page, a grid and a dialog (containing a dynamic control whose type depends on the selected row in the grid)

StackOverflow https://stackoverflow.com/questions/1662329

Question

I have a web page with a GridView. The GridView contains a list of Question objects. Each Question has a QuestionType. When the user clicks on the Question control, an Ajax modal popup appears containing a Dialog custom web control. The Dialog web control is designed to encapsulate a child custom web control together with OK and Cancel buttons. In this scenario, the child control is a Question custom control that represents the selected Question. The exact type of the Question control is not known until a row in the GridView is clicked.

To sum up the page hierarchy:

  • Page
  • GridView containing questions (declared in the .aspx markup, fires an event when a row is clicked)
  • Panel linked to AJAX modal popup extender (declared in the .aspx markup)
  • Dialog control (declared in the .aspx markup)
  • Question control (created dynamically within the Dialog control, type not known until Question selection is made)
  • OK button (created dynamically within the Dialog control)
  • Cancel button (created dynamically within the Dialog control)

When the user clicks the OK button in the Dialog, I want to be able to get at the contents of the Dialog from the containing page. (There is code in the various Question controls that validates the answer and a property in the Dialog that exposes the answer to the containing page.)

I have been having large amounts of trouble in getting this to work.

I have been googling for hours and all articles about custom web controls state that you need to create child controls within the OnInit override. However, in my scenario, the Dialog OnInit method is called before GridView fires its row clicked event, meaning I can't create the Question control because I don't know the target type.

I can make it at least show the dynamic Question control if I move code that creates it to a later point in the lifecycle of the Dialog e.g. RenderContents. However, this is not correct according to all the articles and useless anyway because the control is still not available when the OK button in the Dialog is clicked (as the dialog is hidden and RenderContents will never be called).

So, I'm asking the ASP.NET guys on here how this is supposed to be done? I'm not looking for the code; just strategy. What is the correct way to set this up? The aim is a dialog that contains a dynamically created control whose type depends on the selected row in the GirdView and that can be accessed by the containing page after a postback.

Any tips would be greatly appreciated.

UPDATE

I added logging and the order of events firing after the grid is clicked is as follows:

  • Page ctor
  • Dialog ctor
  • Dialog OnInit
  • Page OnInit
  • Page CreateChildControls
  • Dialog CreateChildControls
  • Page OnLoad
  • Dialog OnLoad
  • Page OnContactorComplianceQuestionSelected
  • Dialog RenderContents

I only know what type of control the Dialog is supposed to contain after Page OnContactorComplianceQuestionSelected.

Was it helpful?

Solution

Don't create dynamic controls in OnInit - that is way too early in the lifecycle and will take you down an unpleasant rabbit hole. Instead:

  • In your Click event handler (late in the lifecycle) store in the ViewState or Session the QuestionID or whatever other information you need to determine which controls to create. Then add the controls. So when the page comes back to the user, the controls will be present.

  • In CreateChildControls check to see if the ViewState contains a QuestionID and if so, re-add the controls from there. So when the user re-submits the page, this time around in the lifecycle, you can reconstitute the control tree as it should be.

So something like:

private void AddQuestionControls(int questionID)
{
    //create and add question controls
}

void Handle_Click(object sender, EventArgs e)
{
    //determine question ID
    ViewState["QuestionID"] = questionID;
    AddQuestionControls(questionID);
}

override void CreateChildControls()
{
    if(ViewState["QuestionID"] != null)
    {
        AddQuestionControls(Convert.ToInt32(ViewState["QuestionID"]);
    }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top