Tim AtLee
Tim AtLee

Reputation: 1009

How to dynamically create controls in asp.net aspx file

I am having difficulty adding a Button control to a specific spot in an .aspx page. I think I can create the button, but I don't know how to add it to the page.

Code is as follows:

                <%
                    var cpus = productItems.FindAll(t => t.Type == "cpu");
                    foreach (var cpu in cpus)
                    { %>
                <div class="row product cpu">
                    <div class="col-md-3">
                        <img class="center-block" src="Content/images/processor.jpg" />
                        <span class="price"><%= cpu.Price %></span>
                        <span class="addtocart">
                            <% Button b = new Button();
                                b.ID = "Button" + cpu.ID;
                                b.CommandArgument = cpu.ID.ToString();
                                b.Text = "Add to Cart";
                                b.OnClientClick = "Addtocart_Click";
                            %>
                        </span>
                        <br />
                    </div>
                </div>
                <% } %>

I can also create the Button as part of the productItems collection, but still presents the problem of how to render the button on the page.

I'm sure there's a better way to do this, just not sure where to look.

Thanks in advance.

Upvotes: 1

Views: 2418

Answers (3)

Sunil
Sunil

Reputation: 21406

In your situation, since you are using inline code, the below approach is the most optimal.

I have place a placeholder control to your original markup, that has an id of placeHolder1 at the location where the button needs to appear. This is what gives you total control over where the button will appear in the rendered page.

  1. You need to use a placeholder control, which is a standard ASP.Net control for adding controls dynamically at run-time.
  2. You place the placeholder control at the location in your page markup where you would like the dynamically created button to appear.
  3. Then, just use the line of code placeHolder1.Controls.Add(b); to add your button control.

Dynamically add a button at a certain location in your page

<%
   var cpus = productItems.FindAll(t => t.Type == "cpu");
   foreach (var cpu in cpus)
   { %>
<div class="row product cpu">
   <div class="col-md-3">
      <img class="center-block" src="Content/images/processor.jpg" />
      <span class="price"><%= cpu.Price %></span>
      <span class="addtocart">
         <% Button b = new Button();
            b.ID = "Button" + cpu.ID;
            b.CommandArgument = cpu.ID.ToString();
            b.Text = "Add to Cart";
            b.OnClientClick = "Addtocart_Click";
            placeHolder1.Controls.Add(b);
            %>
         <asp:PlaceHolder ID="placeHolder1" runat="server"></asp:PlaceHolder>
      </span>
      <br />
   </div>
</div>
<% } %>

Upvotes: 0

Scott Baldric
Scott Baldric

Reputation: 465

In WebForms, you can make use of listing controls that have a concept of a DataSource (Some listing of objects) and a template which renders how each of those objects appear. In general, you should use these whenever you have a list of items that you want to render on the site.

In this particular case, you will probably want to make use of the ListView control. This allows you to define a layout template and an item template.

Your aspx markup would look like the following:

<asp:ListView ID="lvCpus" OnItemDataBound="lvCpus_ItemDataBound" runat="server">
    <LayoutTemplate>
        <div class="row product cpu">
            <div runat="server" id="itemPlaceholder"></div>
        </div>
    </LayoutTemplate>
    <ItemTemplate>
        <div runat="server" class="col-md-3">
            <img class="center-block" src="Content/images/processor.jpg" />
            <span class="price"><%# Eval("Price") %></span>
            <span class="addtocart">
                <asp:Button ID="addToCart" Text="Add To Cart" runat="server" />
            </span>
        </div>
    </ItemTemplate>
</asp:ListView>

This defines a ListView control and creates a LayoutTemplate that matches your container. Internally it has a div that must have the id itemPlaceholder which is used to populate the various items that are bound to this control.

The ItemTemplate portion defines what you expect each individual item to look like. In this case, it's a column that contains a CPU for purchase.

Notice that the button is defined as a regular ASP Web Control, but none of the dynamic data is set. That's because if you try to assign a property like CommandArgument with an evaluated item, the server tag will not be well-formed and you'll get the YSOD. To work around this, you need to specify an OnItemDataBound function for the ListView that is called when you bind data to this Web Control. In my case, it's called lvCpus_ItemDataBound.

The ItemDataBound method in this case will look like the following:

protected void lvCpus_ItemDataBound(object sender, ListViewItemEventArgs e)
{
    if (e.Item.ItemType == ListViewItemType.DataItem)
    {
        var cpu = e.Item.DataItem as Cpu;

        if (cpu == null)
        {
            return;
        }

        var btn = e.Item.FindControl("addToCart") as Button;

        if (btn == null)
        {
            return;
        }

        btn.CommandArgument = cpu.Id.ToString();
        // Set other server-side properties required from code.
    }
}

When you bind a data source, it has 0 or more items in it. For every item in the data source, this method is called and will let you specify server-side appropriate values that can't be expressed directly in the template.

In our case, we specify the CommandArgument from the Cpu class, but other values could be specified as well.

Finally, we need to make sure we can fill the list view with data. So in Page_Load perhaps, we can bind data to this ListView like the following:

protected void Page_Load(object sender, EventArgs e)
{
    lvCpus.DataSource = GetCpus();
    lvCpus.DataBind();
}

private IEnumerable<Cpu> GetCpus()
{
    yield return new Cpu { Id = 1, Price = 5 };
    yield return new Cpu { Id = 2, Price = 10 };
    yield return new Cpu { Id = 3, Price = 15 };
    yield return new Cpu { Id = 4, Price = 15 };
    yield return new Cpu { Id = 5, Price = 20 };
}

We first set the List View's data source to the CPU list that you have and then call the DataBind() method on the ListView. This triggers the OnItemDataBound function to begin filling in the data, and at the end you are left with, in this case, 5 CPUs displayed on the site.

Upvotes: 1

programtreasures
programtreasures

Reputation: 4298

I have added span as runatserver and add control(i.e. button) into it

Try below code,

            <div class="row product cpu">
                <div class="col-md-3">
                    <img class="center-block" src="Content/images/processor.jpg" />
                    <span class="price"><%= cpu.Price %></span>
                    <span class="addtocart" id="buttonContainer" runat="server">
                        <% Button b = new Button();
                            b.ID = "Button" + cpu.ID;
                            b.CommandArgument = cpu.ID.ToString();
                            b.Text = "Add to Cart";
                            b.OnClientClick = "Addtocart_Click";
                            buttonContainer.Controls.Add(b);
                        %>
                    </span>
                    <br />
                </div>
            </div>

Upvotes: 0

Related Questions