How to create an array of User Controls programmatically - c#

How do I programmatically create an array of web user controls?
I created a web user control. I already know how to implement one of them coding in the aspx file, however I would like to know if it is possible to do that from the code behind in either the Page_Init() or the Page_Load() event.
I already know the initialization on the aspx page.
<%# Register TagPrefix="uc" TagName="myUsercontrol1" Src="/Controls/myUsercontrol1.ascx" %>
In the traditional way, I would do something like:
<div id="divMyusercontrols" runat="server">
<uc:myUsercontrol1 id="ctlTheControl" runat="server" />
</div>
What I was wanting to do, which does not work, as I tried, is:
In the aspx file:
<div id="divMyusercontrols" runat="server">
</div>
In the code behind in let us say the Page_Init() event the following:
String strControl = "<uc:myUsercontrol1 id="ctlTheControl{0}" runat="server" />";
String strHtml = null;
for (int i = 0; i < 5; i++)
strHtml += String.Format(strControl, i.ToString());
this.divMyusercontrols.InnerHTML = strHtml;
This code sadly does not work. I realize that I can simply do that manually, but I do not know the actual count. I will know that ahead of time.
UPDATE (to show answer #3 proper code):
The C# for the aspx file is the following. I have a call to a static C# code behind, which returns the count. I then set a property to indicate which item to show and then the for-loop. Cool!
<%int iCount = MyProject.Items;%>
<%for (int iIndex = 0; iIndex < iCount; iIndex++)%>
<%{ %>
<div runat="server">
<uc:myUsercontrol1 id="ctlItem<%=iIndex %>" runat="server" />
</div>
<%}%>
SOLUTION (2013-02-12):
I tried out all three answers below, sadly after I marked one as a solution. The winning one is a modified version of the Page_LoadControl(). Here is the code that works, and yes, I ran the code. Everything works.
// Load an array of controls onto a predefined panel.
for (int iIndex = 0; iIndex < 10; iIndex++)
{
// Load the control.
MyProject.Controls.MyControl ctlItem = (MyProject.Controls.MyControl)Page.LoadControl(#"/Controls/MyControl.ascx");
// Initialize the control.
ctlItem.MyIndex = iIndex;
// Add the control to the panel.
this.pnlItems.Controls.Add(ctlItem);
}
Here is the fixed aspx code.
<div id="divItems" runat="server" class="divItems">
<dx:ASPxPanel ID="pnlItems" runat="server" Width="200px">
</dx:ASPxPanel>
</div>
I tried doing, MyProject.Controls.MyControl ctlItem = new MyProject.Controls.MyControl(), however that does not work. I got a null excemption. Loading the control worked.
The answer, which I too hastilly marked as a solution, does not work. When ran the designer complained. Here is the code from the designer.
/// <summary>
/// ctlPurchases<%=iIndex %> control.
/// </summary>
/// <remarks>
/// Auto-generated field.
/// To modify move field declaration from designer file to code-behind file.
/// </remarks>
protected global::MyProject.Controls.MyControl ctlItem<%=iIndex %>;
The designer was unhappy about the <%...%> part. This solution has other problems. The cleanest one is using the Page_LoadControl.

Can you use an ASP.NET Panel and use Page.LoadControl()?
for (int i = 0; i < 10; i++)
{
pnlContent.Controls.Add(Page.LoadControl("ucDemo.ascx"));
}
This is in C# BTW.
Or if these UserControls will be data bound, you could try using a Repeater control.

When I want multiple copies of my user control I usually follow the pattern below
<%for (Counta = 1; Counta <= 10; Counta++) {%>
<div id="divMyusercontrols<%=Counta%>" runat="server">
<uc:myUsercontrol1 id="ctlTheControl<%=Counta%>" runat="server" />
</div>
<%}%>
The code above would give me 10 UserControls nested inside 10 div elements
UPDATE
Please note that I have used an on-line tool to convert the code from VB to c# So how accurate it is I don't know. Yet I don know it works for me in VB.
The VB Code is below for comparison
<%for Counta = 1 To 10%>
<div id="divMyusercontrols<%=Counta%>" runat="server">
<uc:myUsercontrol1 id="ctlTheControl<%=Counta%>" runat="server" />
</div>
<%Next%>

I think you can do in Page_Load:
HtmlGenericControl control = FindControl("divMyusercontrols")
var myControl = new MyControl();
for(int i=0; i<numberOfUserControls; i++)
{
myControl.ID = "myUniqueID" + i;
}
myControl.Controls.Add(myControl);

Related

Dynamically add div in .aspx page

I'm looking for a JavaScript-free way to dynamically add div elements inside a certain element of an aspx web page according to data passed to i.
I'm looking for something similar to razor c# where you can even create loops and write c# code that directly effects the content of the page.
It appears that razor c# doesn't work unless the page is a .cshtml page, so I'm kinda lost here since I don't want to use JavaScript.
Is there a better approach to it?
Say, below is the placeholder div
<div id = "maindiv">
<asp:PlaceHolder ID ="maindivs" runat="server">
</asp:PlaceHolder>
</div>
Then you may add divs to your placeholder div like below
protected void addmainDiv(int m)
{
for(int i = 0; i< m; i++)
{
System.Web.UI.HtmlControls.HtmlGenericControl newdivs = new System.Web.UI.HtmlControls.HtmlGenericControl("DIV");
newdivs.Attributes.Add("class", "maindivs");
newdivs.ID = "createDiv";
newdivs.InnerHtml = " I'm a div, from code behind ";
maindivs.Controls.Add(newdivs);
}
}
If you want to create HTML dynamically in .aspx then you can create your html code in your .cs (backend file)
string htmlAsStringToShow = "<div>" + variable + "</div>";
Then show its content in your .aspx page like this :
<%= htmlAsStringToShow %>

Render dynamic # of HTML controls in aspx page like #Razor?

I know we can put a placehold on aspx page, then add controls dynamically from backend code. Is there a way in aspx page that is similar to #razor engine that we can add html control directly in aspx page?
<%
int count = GetImageCount();
for (int k = 0; k < count; k++)
{
string id = "img_" + k.ToString();
%>
<asp:Image runat="server" ImageAlign="Middle" />
<%
}
%>
We can use above code to add multiple Image control in aspx page, but how can we set them with different id and src?
Or, we can not do this in aspx directly?
thanks
You cannot set ID for server controls dynamically. Also, as far as I am aware of, setting ImageUrl direclty in aspx for the control in for loop, like in your code, might not be possible - you should probably take a look and asp:Repeater control.
To achieve what you want, you might find useful this piece of code, using HTML <img> control (as #Grundy suggested), instead of asp:Image control:
<% int count = 5;
for (int i = 0; i < count; i++)
{
string id = "id_" + i;
string imageUrl = "/Images/img_" + i;
%>
<img id="<%=id%>" src="<%=imageUrl%>"/>
<%
}
%>

How to use a Variable or Function to declare my SqlDataSource ID in HTML

I'm writing a for loop that displays a list of links with some chartfx display. The chartfx needs an sqlDataSource. I'm trying to give the unique ID each time the for loop does one iteration but I can not pass it a value or function. Example below in my code.
getSQLID() is just a function that returns a string which I want to be my ID. This is all done on the aspx page and the function is in the .cs . Any help would be really appreciated thank you.
//name of the contentplace holder on the aspx page
<asp:Content ID="Content2" ContentPlaceHolderID="ContentPlaceHolder1" Runat="Server" >
//code behind
Control ctrl = LoadControl("WebUserControl.ascx");
Control placeHolderControl = this.FindControl("Content2");
Control placeHolderControl2 = this.FindControl("ContentPlaceHolder1");
ctrl.ID = "something";
if (placeHolderControl != null)
placeHolderControl.Controls.Add(ctrl);
if (placeHolderControl2 != null)
placeHolderControl2.Controls.Add(ctrl);
First of all, recall that server controls declared in the designer like this are attached to your class at compile time. So it doesn't make sense to try to create multiple instances in a loop at runtime, and that's why the values in e.g. the Id tag have to be known at compile time.
One alternative is to create them in the code behind, with something like:
for (int i=0; i<2; ++i)
{
var chart = new Chart();
chart.Id = "chartId" + i;
chart.DataSourceId = "srcid" + i;
var src = new SqlDataSource();
src.Id = "srcid" + i;
Controls.Add(chart); // either add to the collection or add as a child of a placeholder
Controls.Add(src);
}
In your case converting all of those declarative properties to the code behind can be a bit of work (though it is possible). An alternative is to make a user control (ascx) that contains the markup that's now in your aspx page. You would instantiate the controls in your code behind with something like:
for (int i=0; i<2; ++i)
{
var ctrl = LoadControl("~/path/to/Control.ascx");
ctrl.Id = "something_" + i;
Controls.Add(ctrl); // again, either here or as a child of another control
// make the src, hook them up
}

method firing twice during pageload in asp.net

I know such questions are asked already on SO, and i also went through most of them but no one satisfied me, so i am posting mine one. Attached is my code. actually i am building a jquery gallery from code from databale. see the code:
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
DisplayHotProducts();
}
}
private void DisplayHotProducts()
{
var dt = ProductData.GetAllHotProducsts();
if (dt != null && dt.Rows.Count > 0)
{
StringBuilder sb = new StringBuilder();
sb.AppendLine(" <div id='spotlight_products' class='widget_box center_box'>");
sb.AppendLine("<div class='wintitle'>");
sb.AppendLine(" <div class='inner_wintitle'>");
sb.AppendLine(" Hot Products</div></div>");
sb.AppendLine("<div class='winbody'>");
sb.AppendLine("<div class='mall_focus' id='mall_focus'>");
sb.AppendLine("<a id='bp_carousel_prev'>prev</a>");
sb.AppendLine("<a id='bp_carousel_next'>next</a>");
sb.AppendLine("<div id='bp_carousel'>");
sb.AppendLine(" <div class='carousel_list'>");
for (int i = 0; i < dt.Rows.Count; i=i+5)
{
sb.AppendLine(" <div class='product_list'>");
sb.AppendLine("<div class='bestproduct_wrapper'> <div class='bestproduct'>");
sb.AppendLine(" <div class='icon_best'></div>");
sb.AppendLine("<ul>");
sb.AppendLine(" <li class='best_Img'><span></span><a href='#' target='_blank'> ");
sb.AppendLine(string.Format(" <img src='{0}' width='200' height='200' /></a></li>", dt.Rows[i]["ImagePath"]));
sb.AppendLine("</ul>");
sb.AppendLine(" <p class='product_name'>");
sb.AppendLine(string.Format("<a href='Products/{0}' target='_blank'>{1}</a></p>", dt.Rows[i]["Id"], dt.Rows[i]["Name"]));
sb.AppendLine("</div></div>");
for (int j = i+1; j <= 4; j++)
{
sb.AppendLine("<div class='product_item'>");
sb.AppendLine("<ul><li class='product_Img'>");
sb.AppendLine(string.Format("<a href='Products/{0}'><img src='{1}' width='70' height='70'/></a>", dt.Rows[j]["Id"], dt.Rows[j]["ImagePath"]));
sb.AppendLine("</li></ul>");
sb.AppendLine(string.Format("<p class='product_name'><a href='Products/{0}'>{1}</a></p>", dt.Rows[j]["Id"], dt.Rows[j]["Name"]));
sb.AppendLine("</div>");
}
}
sb.AppendLine(#" </div></div></div></div></div>");
ltlHotProducts.Text = sb.ToString();
}
}
}
My code behind has the above code only and the page load is firing twice.
Also this is code behind of a usercontrol, placed on a page where i have 3 more similar control performing similar functionalities that is , building jquery slideshows from code behind. But the above two user controls has some issues with the img tags as some of them are missing images. Also the page on which the user controls are placed has a master page. Now please suggest me the solution.
So the heirarchy is something like this:
Master Page (master) -> Content Page (aspx) -> Has->3 UserControls (ascx)
I have typically seen two reasons for the PageLoad to fire twice - First, if you have an img tag that doesn't have a src attribute, IE and Chrome will both request the page again, which causes the event to fire again. Second, if the event is wired up more than once, it'll fire more than once; Typically I've seen this happen when the code behind includes the event wireup code (eg, this.PageLoad += ... PageLoad) and the page directive AutoEventWireup=True is included in the aspx file.

ASP.NET OnClick not firing in Repeater.ItemTemplate that has a custom control

Title is a bit of a mouthful, but I'm a bit stuck on this issue.
I have a search result page with has a custom control that has a Repeater. The ItemTemplate in the repeater is a PlaceHolder so I can construct the Item in a particular format; more specifically, I have a string of 'diseases' that come in the form of Disease1|disease2|disease3 and need to be given links for each disease.
Now for some code:
The following is the SearchResultControl.ascx
<asp:Panel ID="SearchResultPanel" runat="server" ScrollBars="Auto">
<asp:Repeater ID="Repeater1" runat="server"
onitemcreated="Repeater1_ItemCreated"
onitemcommand="Repeater1_ItemCommand">
<ItemTemplate>
<asp:PlaceHolder ID="ItemTemplatePlaceHolder" runat="server">
</asp:PlaceHolder>
</ItemTemplate>
<SeparatorTemplate>
<tr>
<td colspan="6"><hr /></td>
</tr>
</SeparatorTemplate>
</asp:Repeater>
</asp:Panel>
The code behind: SearchResultControl.ascx.cs
protected void Repeater1_ItemCreated(object sender, RepeaterItemEventArgs e)
{
if (e.Item.DataItem != null)
{
PlaceHolder placeHolder = e.Item.FindControl("ItemTemplatePlaceHolder") as PlaceHolder;
Control searchResultItem = Page.LoadControl("SearchResultItem.ascx");
DataRow row = (e.Item.DataItem as DataRowView).Row;
if (row != null)
{
string diseaseState = row["DiseaseStates"] as string;
searchResultItem.GetType().GetProperty("DiseaseStates").SetValue(searchResultItem, diseaseState, null);
placeHolder.Controls.Add(searchResultItem);
}
}
}
(Full disclosure, I got this idea from this question)
The SetValue calls the DiseaseStates property in SearchResultItem which in turn calls the following method to build the links, set the text, and the events:
private void BuildDiseaseStateLabels(string diseaseStates)
{
PlaceHolder placeHolder = FindControl("DiseaseStatePlaceHolder") as PlaceHolder;
string[] diseaseStateSplit = diseaseStates.Split(new char[] { '|' }, StringSplitOptions.RemoveEmptyEntries);
int count = diseaseStateSplit.Length;
foreach (string diseaseState in diseaseStateSplit)
{
LinkButton diseaseStateLink = new LinkButton();
diseaseStateLink.Text = diseaseState;
//diseaseStateLink.Click += new EventHandler(OnDiseaseStateLinkClick);
diseaseStateLink.CommandArgument = "<%# Eval(\"PatientID\")+ \";\" + Eval(\"PatientStatus\")+ \";\" + Eval(\"Age\")+ \";\" + Eval(\"DiseaseStates\")%>";
diseaseStateLink.CommandName = "OnDiseaseStateLinkClick";
//diseaseStateLink.Command += new CommandEventHandler(OnDiseaseStateLinkClick);
placeHolder.Controls.Add(diseaseStateLink);
if (count != 0)
{
Label splitLabel = new Label();
splitLabel.Text = "|";
placeHolder.Controls.Add(splitLabel);
}
}
}
This is the layout for the SearchResultItem
<div id="SearchResultItemDiv" class="MainSearchResultItem">
<asp:PlaceHolder ID="DiseaseStatePlaceHolder" runat="server">
</asp:PlaceHolder>
</div>
Initially I tried setting the Click event, but that doesn't work at all. I then set the CommandArgument and CommandName but that didn't seem to do the trick. I figured the Command event might need to be set, but again, no luck. I should note that when a link is clicked the Repeater1_ItemCreated in SearchResultControl.ascx.cs is called. But since there is no data in e.Item.DataItem is null and I lose the results.
In one of the questions regarding the same issue, it was suggested that the OnItemCommand be added, but even that doesn't get called.
I also read A Stumper of an ASP.NET Question and A Stumper of an ASP.NET Question: SOLVED!, to no avail.
What could I possibly be doing wrong? All of the correct event hookups seem there, I'm checking for IsPostBack and not doing DataBind() again. blaaargharaggh
Help is always greatly appreciate.
I believe you're running into this issue because the LinkButton controls are recreated too late in the page lifecycle. You have to remember that when the page is posted back, the control technically does not exist anymore, so the event handler cannot be fired.
If you can recreate the LinkButton controls somewhere before the Page_Load event is reached, like OnInit for example, everything should work fine.
For simple pages the above usually works very well, but there are circumstances where recreating controls during OnInit requires a lot of overhead, such as storing counters or arrays in ViewState so you can keep track of the controls that need to be recreated after postback. In these situations I would suggest taking a look at the DynamicControlsPlaceHolder by Denis Bauer. This control is capable of persisting dynamic controls without any addtional code required, which is pretty awesome.
Here's a link to the latest version:
http://www.denisbauer.com/ASPNETControls/DynamicControlsPlaceholder.aspx
The problem might be that you're not doing the DataBind() again.
Because you're building the buttons on the fly, when the page post backs, the buttons haven't been created and are unable to work out which click event it should be firing.
Try getting rid of the IsPostBack check, so each time the page loads, you're re-build the repeater.

Categories