Wrap automatically inserted TextBoxes with HTML in ASP.NET - c#

I want to generate TextBoxes in my ASP.NET webpage. It works fine
foreach (var Field in db.A_Settings)
{
TextBox t = new TextBox();
t.ID = Field.ID.ToString();
t.CssClass = "smallinput";
t.Text = Field.Text;
LabelPlaceHolder.Controls.Add(t);
}
And it nicely generates something like this:
<input name="ctl00$ContentPlaceHolder1$1" type="text" value="ValueA" id="ContentPlaceHolder1_1" class="smallinput">
<input name="ctl00$ContentPlaceHolder1$2" type="text" value="ValueB" id="ContentPlaceHolder1_4" class="smallinput">
<input name="ctl00$ContentPlaceHolder1$3" type="text" value="ValueC" id="ContentPlaceHolder1_5" class="smallinput">
It is correct, but in fact I want to wrap it with some HTML, like
<p>
<label>Label for the first TextBox obtained from database</label>
<span class="field">
<input name="ctl00$ContentPlaceHolder1$1" type="text" value="ValueA" id="ContentPlaceHolder1_1" class="smallinput">
</span>
</p>
I couldn't found how to do it this way, so I was thinking about putting it into List<TextBox>, but I'm stuck here either (the same problem - no idea how to wrap the object with HTML).
Is there any way to do this?
For any posts like "Why don't you add those TextBoxes manually?" I'll send a photo of me hitting my head at keyboard, while there will be a dump of SQL with dozens of fields that needs to be handled displayed on the screen :)Or a photo of a lemur. Lemurs are okay, too

Not the cleanest solution, but should work...
foreach (var Field in db.A_Settings)
{
TextBox t = new TextBox();
t.ID = Field.ID.ToString();
t.CssClass = "smallinput";
t.Text = Field.Text;
//add literal control containing html that should appear before textbox
LabelPlaceHolder.Controls.Add(new LiteralControl("html before"));
LabelPlaceHolder.Controls.Add(t);
//add literal control containing html that should appear after textbox
LabelPlaceHolder.Controls.Add(new LiteralControl("html after"));
}

I would probably use a Repeater control:
<asp:Repeater ID="SettingsRepeater" runat="server">
<ItemTemplate>
<p>
<asp:Label ID="ItemLabel" runat="server"></asp:Label>
<span class="field">
<asp:TextBox ID="ItemTextbox" runat="server"></asp:TextBox>
</span>
</p>
</ItemTemplate>
</asp:Repeater>
And bind the list to the repeater:
SettingsRepeater.DataSource = db.A_Settings;
SettingsRepeater.DataBind();
Then write your ItemDataBound code to set the existing values.

You want HtmlGenericControl controls.
foreach (var Field in db.A_Settings)
{
TextBox t = new TextBox();
t.ID = Field.ID.ToString();
t.CssClass = "smallinput";
t.Text = Field.Text;
var label = new HtmlGenericControl("label");
label.Controls.Add(new LiteralControl("LABEL TEXT"));
var p = new HtmlGenericControl("p");
p.Controls.Add(label);
var span = new HtmlGenericControl("span");
span.Attributes.Add("class", "field");
span.Controls.Add(t);
p.Controls.Add(span);
LabelPlaceHolder.Controls.Add(p);
}

You can create custom ASP.NET Controls that can render any HTML you need. Have a look at this:
Developing a Simple ASP.NET Server Control. This way you can create your a control called CustomTextBox which will render a Textbox inside a paragraph.

Related

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.
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.
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>
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.
You need to use a placeholder control, which is a standard ASP.Net
control for adding controls dynamically at run-time.
You place the placeholder control at the location in your page markup where you would like the dynamically created button to appear.
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>
<% } %>

Reference items within placeholder control

Trying to reference the following text nested within a placeholder control. I'm able to get the "text" textbox but the IF(childCnt is textbox) condition fails on the "title". I'm guessing it's due to the first textbox being nested within a DIV but now sure how to resolve. (See screenshot)
foreach (Control cnt in phInitiatives.Controls)
{
foreach(Control childCnt in cnt.Controls)
{
if (childCnt is TextBox)
{
string controlID = childCnt.ClientID.Substring(3);
TextBox tb = (TextBox)childCnt;
//string title = tb2.Text;
string text = tb.Text;
//updateInitiatives(controlID, title, text);
}
}
}
-- UPDATE --
Not sure if this will help? Here is the output HTML. I'm able to get textarea (txt1) but not the input(lbl1)
<div class="initiative">
<div>
<input name="lbl1" type="text" value="Try" id="lbl1" class="labelCSS">
<a onclick="javascript:return confirm('Confirm Delete?');" id="btn1" href="javascript:__doPostBack('btn1','')"><i class="fa fa-times"></i></a>
</div>
<textarea name="txt1" rows="3" cols="20" id="txt1">Test text **</textarea>
<br><br>
Was able to fixed by just removing the nested div.

Prevent <span> tags ASP.NET server control

I have an issue with this span tags. they are generated but i dont want them. how can i remove them ?
I have the following code
<asp:RadioButtonList ID="rbl_items" runat="server" RepeatLayout="Flow" Visible="true" ClientIDMode="Static" >
</asp:RadioButtonList>
Renders this
<span>
<input>
<label>
<br>
<input>
<label>
<br>
(...)
< /span>
And i wanted something like
< label>< input (radio)>< /label>
< label>< input (radio)>< /label>
In other words how can i remove the < span> tags and if possible structure the render html to be like the client wants(as i showed).
Thanks
One approach to get rid of span is to follow the technique mentioned Here
Another would be, to use the input control of html and place runat="server" and ID attribute and use in code behind.
Another would be to use something like this:
<ul ID="rbl_items" runat="server" clientidmode="Static" style="list-style: none;"></ul>
Code Behind:
int count = 0;
foreach (var yourObj in YourList)
{
HtmlGenericControl li = new HtmlGenericControl("li");
HtmlInputRadioButton radioButton = new HtmlInputRadioButton
{
Value = yourObj,
Name = "controlGroup",
ID = "controlID" + count
};
li.Controls.Add(radioButton);
Label label = new Label { Text = yourObj, CssClass = "YourCSSClass", AssociatedControlID = "controlID"+count };
li.Controls.Add(label);
rbl_items.Controls.Add(li);
count++;
}
You can set style for ul and li later.

User Control is added on postback, but does not display

I have a placeholder control within an UpdatePanel, and when the Add Vehicle button is clicked, I need a new "row" of controls to appear (The first "row" is added declaratively). The UserControl however gets added but not displayed. How can I fix this?
protected void AddVehicleButton_Click(object sender, EventArgs e)
{
int count = Convert.ToInt32(VehicleRegistrationCountHiddenField.Value);
var TBId = "VehicleRegistrationEnhancedTextBox" + count;
IList<Panel> oldPanels = (IList<Panel>)Session["VehiclePanels"] ?? new List<Panel>();
//Seperator
Literal hr = new Literal { Text = "<HR/>" };
//Vehicle Registration
UserControl uc = new UserControl(){ID="3"};
uc.LoadControl("~/Controls/ImageUploadAndCrop/ImageUploadAndCrop.ascx");
Label vehicleRegistration = new Label
{
ID = TBId + "_Label",
AssociatedControlID = TBId,
Text = "Vehicle Registration:"
};
EnhancedTextBox vehicleTypeTextBox = new EnhancedTextBox
{
ID = TBId,
Required = true,
RequiredErrorText = "Vehicle Registration is a required field."
};
//Readd previously added panels
foreach (var addedPanel in oldPanels)
{
AddVehiclePlaceholder.Controls.Add(addedPanel);
}
//Add new controls to the form
Panel newPanel = new Panel();
newPanel.Controls.Add(hr);
newPanel.Controls.Add(uc);
newPanel.Controls.Add(vehicleRegistration);
newPanel.Controls.Add(vehicleTypeTextBox);
AddVehiclePlaceholder.Controls.Add(newPanel);
//Increment the ID count
count++;
VehicleRegistrationCountHiddenField.Value = count.ToString();
//Save the panel to the Session.
oldPanels.Add(newPanel);
Session["VehiclePanels"] = oldPanels;
}
The html is below:
<div id="Step2" style="" data-step="2">
<h2> Step 2 (optional): Capture the offender(s) vehicle Information. </h2>
<hr>
<div id="VehicleTypeFields">
<div>
<label for="">Vehicle Registration</label>
<div id="Body_Body_UpdatePanel1">
<div>
<div>
<script type="text/javascript" src="/Controls/ImageUploadAndCrop/Javascript/jquery.Jcrop.min.js">
<link type="text/css" rel="stylesheet" href="/Controls/ImageUploadAndCrop/CSS/jquery.Jcrop.css">
<script type="text/javascript">
<div>
<div id="Body_Body_VehicleRegistrationImageUploadAndCrop_UploadPanel">
</div>
<input id="Body_Body_VehicleRegistrationEnhancedTextBox" type="text" placeholder="CA123-456" name="ctl00$ctl00$Body$Body$VehicleRegistrationEnhancedTextBox">
</div>
</div>
</div>
</div>
<div id="Body_Body_AddVehiclesUpdatePanel">
<input id="Body_Body_VehicleRegistrationCountHiddenField" type="hidden" value="2" name="ctl00$ctl00$Body$Body$VehicleRegistrationCountHiddenField">
<div>
<hr>
<label id="Body_Body_VehicleRegistrationEnhancedTextBox1_Label" for="Body_Body_VehicleRegistrationEnhancedTextBox1">Vehicle Registration:</label>
<input id="Body_Body_VehicleRegistrationEnhancedTextBox1" type="text" name="ctl00$ctl00$Body$Body$VehicleRegistrationEnhancedTextBox1">
<span id="Body_Body_VehicleRegistrationEnhancedTextBox1_RequiredFieldValidator" style="display:none;">Vehicle Registration is a required field.</span>
</div>
</div>
</div>
The problem is your use of LoadControl on a new instance of UserControl rather than the page's implementation. IE rather than this:
UserControl uc = new UserControl(){ID="3"};
uc.LoadControl("~/Controls/ImageUploadAndCrop/ImageUploadAndCrop.ascx");
Do this instead:
Control uc = LoadControl("~/Controls/ImageUploadAndCrop/ImageUploadAndCrop.ascx");
uc.ID = 3;
I think that will fix your immediate problem with nothing displaying but there are still other issues. While it might be possible to store the entire control in session state, my own testing shows there are anomolies in doing so. I would instead make sure you give each instance of your user control a unique ID (not "3" every time), and store those ID's somewhere. Then loop thru those ID's, calling LoadControl for each one, setting the control ID as you loop (this is required in order for the past viewstate to be reapplied to your user control.
I would also move that loop to either Page_Init or Page_Load so that your dynamically created user controls can properly participate in the page's lifecycle. Creating them in that click event is too late for them to catch their own events and they won't be created at all in the case of a postback outside of that click event.

Why is the data from my html form not submitting?

I am developing asp.net mvc application. I have a section on the form where I add some text boxes dynamically when the user clicks a "Add New Part" button. The problem is when I submit the form I don't get the data from the fields I added dynamically. I am passing the FormCollection to my controller and stepping through the code in the debugger and those fields are not there. If I look at them in firebug I see them just fine. Any ideas?
Here is the javascript for adding the text fields to the page:
function moreFields() {
var newFields = document.getElementById('readrootpu').cloneNode(true);
newFields.id = '';
newFields.style.display = 'block';
var newField = newFields.childNodes;
for (var i = 0; i < newField.length; i++) {
var theName = newField[i].name
if (theName)
newField[i].name = theName;
}
var insertHere = document.getElementById('newpartusageitems');
insertHere.parentNode.insertBefore(newFields, insertHere);
}
Here is the html:
<div id="readrootpu" class="usedparts" style="display: none">
<% var fieldPrefix = "PartUsage[]."; %>
Part ID:
<%= Html.TextBox(fieldPrefix + "ID", "")%>
Serial Number:
<%= Html.TextBox(fieldPrefix + "Serial", "")%>
Quantity:
<%= Html.TextBox(fieldPrefix + "Quantity", "") %>
<input type="button" value="Remove" onclick="this.parentNode.parentNode.removeChild(this.parentNode);" />
</div>
When I inspect the html with firebug it looks fine to me:
Part ID: <input type="text" name="PartUsage[].ID" id="PartUsage[]_ID" value="" />
Serial Number: <input type="text" name="PartUsage[].Serial" id="PartUsage[]_Serial" value="" />
Quantity: <input type="text" name="PartUsage[].Quantity" id="PartUsage[]_Quantity" value="" />
Thoughts?
Verify with Firebug that all the post data is being sent from the page via the "Net" tab.
Also, i agree with Kobi: you need to increment the ID's on the cloned elements so they are unique.
I would suggest you look into jQuery for dynamically creating html elements. I have only just started learning jQuery and its very easy.
The following code demonstrates a simple file upload form that allows the user can add more input elements dynamically. Each time the jQuery adds a new input element, i append a chr to the id attribute so they are all unique. Hopefully this helps you:
The script block for the jQuery.. notice the last part is for the ajax animation. The actual copying code is only those 4 lines from $("#moreFiles").click
<script type="text/javascript">
var counter = "oneFile";
$(document).ready(function() {
$("#moreFiles").click(function() {
var newCounter=counter+"1";
$("p#"+counter).after("<p id='"+newCounter+"'><input type='file' name='"+newCounter+"' id='"+newCounter+"' size='60' /></p>");
counter=newCounter;
});
$("#submitUpload").click(function() {
$("#submitUpload").val("Uploading...");
$("img.uploadingGif").show();
});
});
</script>
..and the aspnet markup:
<% string postUrl = Model.PostUrl + (Model.ModelID > 0 ? "/" + Model.ModelID.ToString() : ""); %>
<form id="uploadForm" class="uploadForm" action="<% =postUrl %>"
method="post" enctype="multipart/form-data">
<label>Select file(s) for upload (size must not exceed
<% =Html.Encode(ServiceConstants.MAX_FILE_UPLOAD_SIZE_INBYTES) %> bytes):</label>
<p id="oneFile"><input type="file" name="oneFile" id="oneFile" size="60" /></p>
<% if(Model.MultipleFiles) { %>
<p><a id="moreFiles" href="#">add more files</a></p>
<input id="MultipleFiles" type="hidden" name="MultipleFiles" value="true" />
<% } %>
<p><%--<input id="submitUpload" type="submit" value="Upload" />--%>
<% =Html.InputSubmit("Upload","submitUpload") %>
<% =Html.LoadingImage("uploadingGif") %>
</form>
..this all only boils down to a few lines of html and jQuery.
When you have multiple values on fields with same names, you should be able to see them on the server side using Request.Form.GetValues("key").
You should note that when you clone the nodes, you create copies with the same IDs, which is considered invalid.
Also, you have a for loop there, and I don't quite get what it does (reads the node's name and sets it back - what is the reason for doing that? Should that be var theName = newFields[i].name ?)
I was working with plain HTML when my form worked fine if all the fields were left blank, but it would not submit if I filled out the form.
Later I realized that it was because of a text-field entry 'Email' in which I was entering an email-address containing the character '#'. When I removed the '#', the form started submitting again.
You cannot just aribtrarily add text boxes client-side without having the corresponding server-side controls ready to read the data from the postback. However, you should be able to read the raw data from HttpRequest.Form.
update: oops! it's MVC. I didn't read^H^H^H^H see that. never mind.
-Oisin

Categories

Resources