views:

1009

answers:

4

Hello everyone,

Ive tried searching for hours now and cannot find out why my code (aka, me.) is failing

Basically... I have a listview control which I'm passing a datatable of products (ID, Name, Description and Price columns), and im trying to make it so that when the "checkout" button is pressed, it parses through all the controls on the page, finds all the controls with the correct ID's and adds the items values to the cart.

ive checked all my ID's in the source code and they match up to the ones being requested by the FindControl method.

the error getting thrown back is:

Object reference not set to an instance of an object.
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code. 
Line 21:         For I = 1 To counter
Line 22:             Dim cartItem As New Core.Types.CartItem
Line 23:             cartItem.Name = CType(productsContainer.FindControl("product" + I.ToString()), HtmlGenericControl).InnerText
Line 24:             cartItem.Quantity = Convert.ToInt32(CType(productsContainer.FindControl("quantity" + I.ToString()), HtmlSelect).Value)
Line 25:             cartItem.Price = Convert.ToDecimal(CType(productsContainer.FindControl("price" + I.ToString()), HtmlGenericControl).InnerText.Remove(0, 1))

my .aspx code:

                <div class="productsContainer" id="productsContainer" runat="server">
                <asp:ListView runat="server" ID="lsvProducts">
                    <LayoutTemplate>
                        <ul class="lsvProducts">
                            <li class="highlight">
                                <div class="productName">
                                    Product
                                </div>
                                <div class="productQuantity">
                                    Number of Licenses
                                </div>
                                <div class="productPrice">
                                    Price
                                </div>
                            </li>
                            <asp:PlaceHolder ID="itemPlaceHolder" runat="server"></asp:PlaceHolder>
                        </ul>
                    </LayoutTemplate>
                    <ItemTemplate>
                        <li>
                        <div style="display: none;">
                            <%=setCurrent()%>
                        </div>
                        <input type="hidden" id='productID<%#Eval("ID")%>' />
                            <div class="productName" id='product<%=currentItem%>'>
                                <%#Eval("Name")%>
                            </div>
                            <div class="productQuantity">

                            <select id='quantity<%=currentItem%>'>
                                <option selected="selected"
                                value="0">0</option>
                                <option value="1">1</option>
                                <option value="2">2</option>
                                <option value="3">3</option>
                                <option value="4">4</option>
                                <option value="5">5</option>
                                <option value="6">6</option>
                                <option value="7">7</option>
                                <option value="8">8</option>
                                <option value="9">9</option>
                                <option value="10">10</option>
                            </select>
                            </div>
                            <div class="productPrice" id='price<%=currentItem%>'>
                                <%#"$" + Convert.ToDouble(Eval("Price")).ToString()%>
                            </div>
                        </li>
                    </ItemTemplate>
                </asp:ListView>
            </div>
            <div class="clearer">
                &nbsp;</div>
            <div class="purchaseButton">
                <asp:Button ID="btnAddCart" runat="server" Text="Add to Cart" />
            </div>
        </div>

and my code behind:

    Dim counter As Int32
Public currentItem As Int32

Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
    'get all active products to display in the listing
    Dim query As String = "SELECT * FROM Products WHERE Active = 1"
    Dim dt As DataTable = DAL.Data.GetDataTable(query, "MainDB")
    counter = dt.Rows.Count
    lsvProducts.DataSource = dt
    lsvProducts.DataBind()
End Sub

Protected Sub btnAddCart_Click(ByVal sender As Object, ByVal e As EventArgs) Handles btnAddCart.Click
    'create a new instance of the cart
    Dim cart As New Core.Types.Cart

    'foreach item in the listing, find its details and add it to the shopping cart
    For I = 1 To counter
        Dim cartItem As New Core.Types.CartItem
        cartItem.Name = CType(productsContainer.FindControl("product" + I.ToString()), HtmlGenericControl).InnerText
        cartItem.Quantity = Convert.ToInt32(CType(productsContainer.FindControl("quantity" + I.ToString()), HtmlSelect).Value)
        cartItem.Price = Convert.ToDecimal(CType(productsContainer.FindControl("price" + I.ToString()), HtmlGenericControl).InnerText.Remove(0, 1))
        cartItem.ID = Convert.ToInt32(CType(productsContainer.FindControl("productID" + I.ToString()), HtmlGenericControl).InnerText)
        cart.AddItem(cartItem)
    Next

    If (cart.isEmpty) Then
        'empty cart, go nowhere. show a message saying the carts empty and to choose something.
    Else
        Response.Redirect("~/Checkout.aspx")
    End If
End Sub

Public Function setCurrent()
    currentItem = currentItem + 1
    Return currentItem
End Function

Please help... this is driving me insane!

Thanks in advance :)

+4  A: 

If you're in a datagrid/ repeater/ listview, to use the "FindControl" method, you'll have to iterate through the data items in the list view, then for each item peform the find control method. e.g. in C#:

foreach(RepeaterItem item in Repeater1.Items)
{
    Literal lit = (Literal)item.FindControl("controlId");
}

I'm not sure thats the exact syntax but you get what I mean. You can't just use the find control method on the listview Id - the Ids of server controls in each item get re-written because you're looping through a collection...

Cheers, Sean

seanxe
+5  A: 

FindControl only looks in the current naming container. If you wish to find controls from a different naming container, you should have your own (recursive) implementation. For example:

 private Control FindControlRecursive(Control parent, string id)
 {
  Control controlFound = parent.FindControl(id);
  int i = 0;
  while (controlFound == null && i < parent.Controls.Count)
  {
   controlFound = FindControlRecursive(parent.Controls[i++], id);
  }
  return controlFound;
 }

After that use FindControlRecursive(productsContainer, "product" + I.ToString())

lingvomir
+1. Thanks for the code.
Jeremy
+1  A: 

It looks like your nested controls are just basic Html controls? I'm not sure they'll be registered with ASP.NET unless you have runat="server" to register them as server-side controls.

It's been a while since I've done heavy ASP.NET dev, but in my prior experience we always used server-side controls and had no problems.

cdeweese
A: 

The other thing I noticed was that if your ContentPlaceHolder for a child page is nested inside a LoggedInTemplate inside a LoginView on a masterpage, then you can forget about using FindControl to grab the handle on a control inside the child page.

arunabhdas