views:

377

answers:

2

In a user control, I've got a Repeater inside of an UpdatePanel (which id displayed inside of a ModalPopupExtender. The Repeater is databound using an array list of MyDTO objects. There are two buttons for each Item in the list. Upon binding the ImageURL and CommandArgument are set.

This code works fine the first time around but the CommandArgument is wrong thereafter. It seems like the display is updated correctly but the DTO isn't and the CommandArgument sent is the one that has just been removed.

Can anybody spot any problems with the code?

Edit : I've just added a CollapsiblePanelExtender to the code. When I now delete an item and expand the panel, the item that was previously deleted (and gone from the display) has come back. It seems that the Repeater hasn't been rebuilt correctly under the bonnet.

ASCX

<asp:UpdatePanel ID="ViewDataDetail" runat="server" ChildrenAsTriggers="true">
    <Triggers>
        <asp:PostBackTrigger ControlID="ViewDataCloseButton" />
        <asp:AsyncPostBackTrigger ControlID="DataRepeater" />
    </Triggers>
    <ContentTemplate>
        <table width="100%" id="DataResults">
        <asp:Repeater ID="DataRepeater" runat="server" OnItemCommand="DataRepeater_ItemCommand" OnItemDataBound="DataRepeater_ItemDataBound">
        <HeaderTemplate>
            <tr>
            <th><b>Name</b></th>
            <th><b>&nbsp;</b></th>
            </tr>
        </HeaderTemplate>
            <ItemTemplate>
            <tr>
                <td>
                <b><%#((MyDTO)Container.DataItem).Name%></b>
                </td>
                <td>
                <asp:ImageButton CausesValidation="false" ID="DeleteData" CommandName="Delete" runat="server" />
                <asp:ImageButton CausesValidation="false" ID="RunData" CommandName="Run" runat="server" />
                </td>
            </tr>
            <tr>
                <td colspan="2">
                <table>
                    <tr>
                    <td>Description : </td>
                    <td><%#((MyDTO)Container.DataItem).Description%></td>
                    </tr>
                    <tr>
                    <td>Search Text : </td>
                    <td><%#((MyDTO)Container.DataItem).Text%></td>
                    </tr>
                </table>
                </td>
            </tr>
            </ItemTemplate>
        </asp:Repeater>
        </table>
    </ContentTemplate>
</asp:UpdatePanel>

Code-Behind

    public DeleteData DeleteDataDelegate;
    public RetrieveData PopulateDataDelegate;
    public delegate ArrayList RetrieveData();
    public delegate void DeleteData(String sData);


 protected void Page_Load(object sender, EventArgs e)
    {
        //load the initial data..
        if (!Page.IsPostBack)
        {
            if (PopulateDataDelegate != null)
            {
                this.DataRepeater.DataSource = this.PopulateDataDelegate();
                this.DataRepeater.DataBind();
            }
        }
    }

    protected void DataRepeater_ItemCommand(object source, RepeaterCommandEventArgs e)
    {
        if (e.CommandName == "Delete")
        {
            if (DeleteDataDelegate != null)
            {
                DeleteDataDelegate((String)e.CommandArgument);
                BindDataToRepeater();
            }
        }
        else if (e.CommandName == "Run")
        {
            String sRunning = (String)e.CommandArgument;
            this.ViewDataModalPopupExtender.Hide();
        }
    }

    protected void DataRepeater_ItemDataBound(object source, RepeaterItemEventArgs e)
    {
        RepeaterItem item = e.Item;
        if (item != null && item.DataItem != null)
        {
            MyDTO oQuery = (MyDTO)item.DataItem;

            ImageButton oDeleteControl = (ImageButton) item.FindControl("DeleteData");
            ImageButton oRunControl = (ImageButton)item.FindControl("RunData");

            if (oDeleteControl != null && oRunControl !=null)
            {
                oRunControl.ImageUrl = "button_expand.gif";
                oRunControl.CommandArgument = "MyID";
                if (oQuery !=null)
                { 
                  //do something
                }
                oDeleteControl.ImageUrl = "btn_remove.gif";
                oDeleteControl.CommandArgument = "MyID";
            }
        }
    }

    public void BindDataToRepeater()
    {
        this.DataRepeater.DataSource = this.PopulateDataDelegate();
        this.DataRepeater.DataBind();
    }

    public void ShowModal(object sender, EventArgs e)
    {
        BindDataToRepeater();
        this.ViewDataModalPopupExtender.Show();
    }
A: 

After many days of messing around with this I've not found a proper fix for the problem but do have a workable work-around.

The CollapsiblePanelExtender is set to NOT postback automatically which fixes the issue of the deleted data re-appearing when the extender is opened. The other issue, I believe, is related.

It seems that the ViewState for the Repeater is out of sync with the data. e.CommandArgument is not always correct and seems to reference the previous data. I made an attempt to fix it by storing the ArrayList of MyDTO objects in the ViewState when opening the Modal dialog and using the ID retrieved from e.Item.ItemIndex to find the correct element to delete. This didn't work correctly, the ArrayList pulled out of the ViewState was out of sync.

Storing the ArrayList in the session makes it all work which leads me to believe that I'm doing something fundamentally wrong or there is a subtle bug in the version of the toolkit that i'm using (we're still on VS2005 so are stuck with an older version of the toolkit)

Apologies if this makes no sense, contact me if you want clarification on anything.

Gordon Carpenter-Thompson
+2  A: 

Thanks for reminding me why I stopped using ASP.NET controls. This is the exact type of nightmare that has made too many projects go way over budget and schedule.

My advise to you is to think of the simplest way to implement this. You can try to bend over backwards in order to get this to work the ASP.NET way or take the shortest route. All you're doing is generating HTML, it should never be that difficult.

The most likely cause of your problem is that the ViewState is stored in the page which doesn't get updated on a partial postback. So with every change in the update panel you'll postback the initial viewstate of the page.

Try replacing the repeater with a simple for-loop (and ignore the people who start complaining you shouldn't mix markup and code). Replace your databinding statements with <%= %>. That eliminates the view state all together and should remove any removed row from re-appearing.

Marnix van Valen