views:

190

answers:

1

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.

+2  A: 

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"]);
    }
}
Rex M
Thanks for the reply. I will try this out now.
J M
@JM this does relate specifically to the main page only, not the dialog of course... the main page needs to trap the return of the dialog and handle that. That may be a separate question, but if you want to post it I'll try to help you out there too.
Rex M
I added some logging and looking at the results it seems that both OnInit and CreateChildControls (for the page and the dialog) are called before the gird click event fires. I've updated the question with the output of my logging.
J M
@JM yes, CreateChildControls will definitely fire before your click event and before PageLoad. The point of my answer is to insert the dynamic controls on the *next* postback in CreateChildControls, not on the one where the click event is fired.
Rex M
@Rex - If teh dynamic controls are only inserted in the next postback, I don't quite understand how my dialog will get correctly displayed the first time a user clicks in the grid?
J M
@JM the dynamic controls should be added from the event handler - it's OK if it happens very late in the lifecycle. They only need to be added early on the *next* postback, after the current one which caused them to first be added.
Rex M
Hi again Rex for your reply. I've run out of time with this and have had to change strategy. I think the current implementation has other issues causing side effects. I am going to accept this as the answer and then put together a scaled down test of what I am trying to do. If I can't get the test working, I'll post the test code. Meanwhile, thanks very much for your help.
J M