How to programmatically add triggers to an ASP.NET UpdatePanel

asp.netasp.net-ajaxupdatepanel

I am trying to write a quote generator. For each product, there are a set of options. I want to dynamically add a drop down list for each option, and then have their SelectedIndexChanged events all wired up to update the quote cost.

I am not having any trouble adding the DropDownList controls to my UpdatePanel, but I can't seem to wire up the events.

After the page loads, the drop downs are there, with their data, but changing them does not call the SelectedIndexChanged event handler, nor does the QuoteUpdatePanel update.
I have something like this:

Edit: Since programmatically adding AsyncPostBackTrigger controls is not supported, I've change my code to this, but I still don't get the event:

Edit 2: Tried adding a PlaceHolder to add the drop down lists to (instead directly into the ContentTemplateContainer, still no events firing.

QuotePanel.ASCX

<asp:ScriptManager ID="ScriptManager" runat="server" />

<asp:UpdatePanel ID="QuoteUpdatePanel" runat="server" ChildrenAsTriggers="true">
    <ContentTemplate>
        Cost: <asp:Label ID="QuoteCostLabel" runat="server" />
        <fieldset id="standard-options">
            <legend>Standard Options</legend>
            <asp:UpdatePanel ID="StandardOptionsUpdatePanel" runat="server" ChildrenAsTriggers="true" UpdateMode="Conditional">
                <ContentTemplate>
                <asp:PlaceHolder ID="StandardOptionsPlaceHolder" runat="server" />                   
                </ContentTemplate>
            </asp:UpdatePanel>
        </fieldset>
    </ContentTemplate>
</asp:UpdatePanel>

The code to add the dropdowns and the event they are to be wire up for:

protected void PopluateUpdatePanel(IEnumerable<IQuoteProperty> standardOptions)
{
    foreach (IQuoteProperty standardOp in standardOptions)
    {
        QuotePropertyDropDownList<IQuoteProperty> dropDownList = new QuotePropertyDropDownList<IQuoteProperty>(standardOp);
        dropDownList.SelectedIndexChanged += QuotePropertyDropDown_SelectedIndexChanged;
        dropDownList.ID = standardOp.GetType().Name + "DropDownList";
        dropDownList.CssClass = "quote-property-dropdownlist";

        Label propertyLabel = new Label() {Text = standardOp.Title, CssClass = "quote-property-label"};

        StandardOptionsPlaceHolder.Controls.Add(propertyLabel);
        StandardOptionsPlaceHolder.Controls.Add(dropDownList);

        _standardOptionsDropDownLists.Add(dropDownList);

        ScriptManager.RegisterAsyncPostBackControl(dropDownList);

    }

}

void QuotePropertyDropDown_SelectedIndexChanged(object sender, EventArgs e)
{
    QuoteCostLabel.Text = QuoteCost.ToString();
    StandardOptionsUpdatePanel.Update();
}

Best Answer

AFAIK, adding async triggers to the UpdatePanel control programmatically is working.

The workaround is to add them in Page_Init event and set trigger's ControlID property to the control unique id value:

AsyncPostBackTrigger trigger = new AsyncPostBackTrigger();
// unique id instead of client id
trigger.ControlID = yourDropDownControl.UniqueID;  
trigger.EventName = "SelectedIndexChanged"; 
QuoteUpdatePanel.Triggers.Add(trigger);

Seems made this work. I created similar structure of page/control above. So there are user control QuotePropertyControl and Default page which holds this control.

I added dropDownList.AutoPostBack = true property and was able to catch an async postback from the dropdown. So, guessing the problem was in this property.

One more thing: it's really doesn't matter how to register async trigger; both variants ScriptManager.RegisterAsyncPostBackControl and through AsyncPostBackTrigger worked like a charm (until the init event of the page).

Here is how I did it:

QuotePropertyControl.ascx.cs

private string[] data = { "a", "b", "c", "d", "e" };

public void PopluateUpdatePanel(IEnumerable<string> standardOptions)
{
    foreach (string standardOp in standardOptions)
    {
        DropDownList dropDownList = new DropDownList();
        dropDownList.SelectedIndexChanged +=
            QuotePropertyDropDown_SelectedIndexChanged;
        dropDownList.ID = standardOp + "DropDownList";
        dropDownList.CssClass = "quote-property-dropdownlist";
        dropDownList.AutoPostBack = true;
        dropDownList.DataSource = data;
        dropDownList.DataBind();

        Label propertyLabel = new Label() { Text = standardOp };

        StandardOptionsPlaceHolder.Controls.Add(propertyLabel);
        StandardOptionsPlaceHolder.Controls.Add(dropDownList);

        ScriptManager.GetCurrent(Page)
            .RegisterAsyncPostBackControl(dropDownList);
    }
}

protected void QuotePropertyDropDown_SelectedIndexChanged(
    object sender,
    EventArgs e
    )
{
    StandardOptionsUpdatePanel.Update();
}

QuotePropertyControl.ascx

<asp:UpdatePanel ID="QuoteUpdatePanel" runat="server" ChildrenAsTriggers="true">
    <ContentTemplate>
        Cost:
        <asp:Label ID="QuoteCostLabel" runat="server" />
        <fieldset id="standard-options">
            <legend>Standard Options</legend>
            <asp:UpdatePanel ID="StandardOptionsUpdatePanel" 
                runat="server" 
                ChildrenAsTriggers="true" 
                UpdateMode="Conditional">
                <ContentTemplate>
                    <asp:PlaceHolder ID="StandardOptionsPlaceHolder" 
                    runat="server" />
                </ContentTemplate>
            </asp:UpdatePanel>
        </fieldset>
    </ContentTemplate>
</asp:UpdatePanel>

Default.aspx.cs

string[] names = { "ab", "bc", "ef" };

protected void Page_Init(object sender, EventArgs e)
{
    ctlQuoteProperty.PopluateUpdatePanel(names);
}

Default.aspx

<%@ Register Src="~/QuotePropertyControl.ascx" 
             TagPrefix="uc" 
             TagName="QuoteProperty" %>

<form id="form1" runat="server">
<div>
    <asp:ScriptManager ID="ScriptManager" runat="server" />
    <uc:QuoteProperty runat="server"
        ID="ctlQuoteProperty">
    </uc:QuoteProperty>
</div>
</form>