Search code examples
c#asp.net.netrepeater

asp.net repeater button click event. DataItem is null?


Hi just trying to get a button click event to send data from the specfic row it was pressed on to the click event itself so I can put it onto a url and send it off to another page but the dataitem is null.

I will provide code. The repeater btw is bound correctly in terms of I can see the correct data on it before I press the button.

I have tried rebinding the repeater in the button click event as I noticed it was hitting page load when I press the button.

I haven't done asp.net for a while so I'm a bit rusty....

'''

<asp:Repeater  ID ="DealerContactsRepeater" runat ="server">
         <HeaderTemplate>
        <table ID ="ContactsTable">
            <tr>
                <th></th>
                <th style="display:none;">Dealer Contact ID</th>
                <th>Title</th>
                <th>Fullname</th>
                <th>Is Preferred Contact?</th>
                <th>Position</th>               
                <th style="display:none;">Forename</th>
                <th style="display:none;">Surname</th>
                <th>Phone</th>
                <th style="display:none;">Direct</th>
                <th style="display:none;">Fax</th>
                <th>Mobile</th>
                <th>Email</th>
                <th style="display:none;">Secondary Email</th>
                
            </tr>
    </HeaderTemplate>
    <ItemTemplate>
        <tr>
            <td> <asp:Button ID="btnEdit" runat="server" Text="Edit" OnClick="btnEdit_Click" CommandArgument='<%# Container.ItemIndex %>' /></td>
            <td style="display:none;"><%# Eval("DealerContactId") %></td>
            <td><%# GetTitleText((byte)Eval("NameTitleId")) %></td>
            <td><%# Eval("Forename") %> <%# Eval("Surname") %></td>
             <td><asp:CheckBox ID="CheckBox1" runat="server" Checked='<%# Eval("DecisionMaker") %>' Enabled="false" /></td>
            <td><%# GetPositionText((byte)Eval("DealerContactPositionId")) %></td>
            <td style="display:none;"><%# Eval("Forename") %></td>
            <td style="display:none;"><%# Eval("Surname") %></td>
            <td><%# Eval("PhoneNumber") %></td>
            <td style="display:none;"><%# Eval("DirectDialNumber") %></td>
            <td style="display:none;"><%# Eval("FaxNumber") %></td>
            <td><%# Eval("MobileNumber") %></td>
            <td><%# Eval("EmailAddress") %></td>
            <td style="display:none;""><%# Eval("SecondaryEmail") %></td>
        </tr>
    </ItemTemplate>
    <FooterTemplate>
        </table>
    </FooterTemplate>
    </asp:Repeater>

''' protected void Page_Load(object sender, EventArgs e) { if (!IsPostBack) { BindContactsRepeater(); }

        }

        private void BindContactsRepeater()
        {
            DealerContactsRepeater.DataSource = GetDealerContactInfo();
            DealerContactsRepeater.DataBind();
        }

''' Button click event:

protected void btnEdit_Click(object sender, EventArgs e)
        {
            Button btnEdit = (Button)sender;
            RepeaterItem item = (RepeaterItem)btnEdit.NamingContainer;

            try
            {
                
                DataRowView row = (DataRowView)item.DataItem;
                int dealerContactId = Convert.ToInt32(row["DealerContactId"]);
                ContactTitles title = (ContactTitles)Enum.Parse(typeof(ContactTitles), row["NameTitleId"].ToString());
                bool isChecked = Convert.ToBoolean(row["DecisionMaker"]);
                ContactPositions position = (ContactPositions)Enum.Parse(typeof(ContactPositions), row["DealerContactPositionId"].ToString());
                string forename = row["Forename"].ToString();
                string surname = row["Surname"].ToString();
                string phone = row["PhoneNumber"].ToString();
                string direct = row["DirectDialNumber"].ToString();
                string fax = row["FaxNumber"].ToString();
                string mobile = row["MobileNumber"].ToString();
                string email = row["EmailAddress"].ToString();
                string secondEmail = row["SecondaryEmail"].ToString();

                string editUrl = $"EditPage.aspx?dealerContactId={dealerContactId}&title={(int)title}&isChecked={isChecked}&position={(int)position}&forename={Uri.EscapeDataString(forename)}&surname={Uri.EscapeDataString(surname)}&phone={Uri.EscapeDataString(phone)}&direct={Uri.EscapeDataString(direct)}&fax={Uri.EscapeDataString(fax)}&mobile={Uri.EscapeDataString(mobile)}&email={Uri.EscapeDataString(email)}&secondEmail={Uri.EscapeDataString(secondEmail)}";

                Response.Redirect(editUrl);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }

'''

Any help would be much appreciated.


Solution

  • Unfortunately, unlike desktop, the data binding row is NOT available AFTER the binding is complete. It is however available DURING the data binding event.

    This "fact" can often be put to good use, since then on the row data bound event, then ALL rows of the data source can be used. So a tax calc, or hide/show or whatever can be done during that event.

    However, ONCE the binding is complete, then the data item source is GONE and out of scope.

    So, for a simple button click?

    Well, if you need that information (rows or data that don't exist in the repeater item, then you have to re-pull the data.

    If there is not a lot of rows of data, then the other approach can be to persit the datasource in session(), or perhaps viewstate.

    Viewstate is nice, since it is "per web page", and if a user has 2 of the same page open at the same time, then it will work. But, viewstate travels with the web page - and can increase the size of any button click (viewstate included in post-back - it can cause performance issues).

    Session() is 100% server side, but is per user, and not per page - so some caution here, since that can prevent a user having two browser pages open to the same URL.

    the OTHER big feature missing from a repeater of course is "data keys". Data keys is REALLY nice, since that feature can "manage" the database row PK id for you, but you NEVER have to expose the PK id values to the client side web page and markup. For this reason, I often avoid using a repeater.

    So, then:

    DataRowView row = (DataRowView)item.DataItem;
    

    Nope, you can't use with a button click. You can do above DURING a data bind, such as row data bind event as I noted.

    So, this suggests that you :

    Re-pull the data, or persist the data source.

    But, a re-pull of data? Well, you don't have data keys either (for a repeater!!). You do for listview, gridview.

    So, I bite the bullet, risk less security, and I often say will do this:

                <ItemTemplate>
    
                    <h3><%# Eval("HotelName") %> </h3>
    
                    <asp:Button ID="cmdView" runat="server" Text="View"
                        CommandArgument="<%# Eval("ID") %>"
                        OnClick="cmdView_Click"
                        />
    

    So, then for the button click code, I can do this:

        protected void cmdView_Click(object sender, EventArgs e)
        {
            Button btn = (Button)sender;
            RepeaterItem rRow = (RepeaterItem)btn.NamingContainer;
            int MyRowIndex = rRow.ItemIndex;
            int MyPK = Convert.ToInt32(btn.CommandArgument);
    
            // use find control on rRow to get controls from this row.
    
            Label lFirstName = (Label)rRow.FindControl("FirstName");
            Debug.Print("First Name = " + lFirstName.Text);
    
        }
    

    So, note how we are free to get/grab the repeater row, and free to grab controls + values from those controls.

    However, the data binding source as noted will have gone out of scope.

    If our data source WAS persisted say into session, then I could do this:

            DataTable rstData = (DataTable)Session["rstData"];
    
            DataRow MyDataRow = rstData.Rows[MyRowIndex];
            Debug.Print($"Tax rate = {MyDataRow["TaxRate"].ToString()}");
    

    So, you can shove in your data object (GetDealerContactInfo()) into session, or viewstate, and pull out by row index as per above.