In ASP.NET (C#), what is the proper way to render and reference collection-based forms?
-
01-07-2021 - |
Question
I imagine this question has been asked before (hasn't every question?), but I am struggling with the language necessary to find my way to a solution, so here are some specifics:
I have a web form for updating a single location, e.g. street address, city, state, etc., which is dealt with by having a static location-update form with various asp:TextBoxes which are referenced in the code-behind using their intellisensed controls, e.g. StreetAddressTextBox.Text.
I would like to update this form to work for multiple locations. In the past when I worked with PHP, this meant writing a lot of inline code wherein I wrapped the form-contents in a for-loop over the collection I was rendering, and then referenced each item with some ID="StreetAddressTextBox_<%=locationId%>"
-like schema.
I'm currently in the process of doing it this way in ASP.NET, but inline feels dirty, especially because I have to kick a lot of code-behind paradigms I've established on other pages where the form-content wasn't scaling based on a collection. My gut tells me there should be some way to template out a location and do everything in the code-behind, but I've yet to stumble upon and example that shows me this in practice.
Thanks for any help in advance.
Solution
Assuming I understand you correctly it sounds like you should be using a UserControl.
Basically create a UserControl.ascx file (and VS will generate the UserControl.designer.cs and code-behind classes for you). Then put your input fields in there with no special attributes, like so:
(Note that I prefer HtmlControls to WebControls, they give cleaner markup and don't muddy things. I suggest you give them a try):
<%@ Control blargh %>
<label for="<%= Street.ClientID %>">Street address</label>
<input type="text" runat="server" id="Street" /> <br />
<label for="<%= City.ClientID %>">Town / City</label>
<input type="text" runat="server" id="City" /> <br />
<!-- etc -->
Note how I'm not messing with the id=""
attribute of the input controls. Now just register it in your web.config:
<add tagPrefix="foo" tagName="Address" src="~/Controls/Address.ascx" />
Then in each page that needs an address form (assuming your UserControl is called AddressControl)
<%@ Page blargh %>
<p>Enter your address details below:</p>
<foo:Address runat="server" id="Address" />
Then in your page's code-behind you can just do this:
public override void OnLoad(Object sender, EventArgs e) {
if( Page.IsPostBack ) {
Validate();
if( Page.IsValid ) {
// get values from a POST
String street = this.Address.Street.Value;
String city = this.Address.City.Value;
// and so on
}
} else {
// set values if you're retrieving data from a DB or something
this.Address.Street.Value = "123 Fake Street";
this.Address.City.Value = "Frying Pan City";
}
}
Note how I used the "<%= Street.ClientID %>"-thing. ASP.NET will automatically generate the control names and IDs based on the control's parent name (and so on, recursively). So the actual HTML input will be rendered like this:
<label for="Address.Street">Street address</label>
<input type="text" name="Address_Street" id="Address.Street" />
Note this design and practice only applies to WebForms. ASP.NET MVC is completely different again.
OTHER TIPS
You could just use an <asp:PlaceHolder runat="server" id="plcDynamicControls" />
.
In your code behind create a private List<WebControl> dynamicControls
.
During .Init()
return whatever logic is appropriate for your locationID
requirements, and whatever your field requirements might be.
Say locationID = 4
required Name, Address1, Address2 fields:
Page_Init(object Sender, EventArgs e)
{
TextBox txt = new TextBox(){
ID = "txtName"
};
plcDynamicControls.Controls.Add(txt); //Add to Form
dynamicControls.Add(txt); //Add to reference list, to avoid having to do .FindControl()
TextBox add1 = new TextBox(){
ID = "txtAddress1"
};
plcDynamicControls.Controls.Add(add1);
dynamicControls.Add(add1);
TextBox add2 = new TextBox(){
ID = "txtAddress2"
};
plcDynamicControls.Controls.Add(add2);
dynamicControls.Add(add2);
}
Build a nice loop to add controls like that to your placeholder.
In your .Click()
event from your submit button you can iterate through the List<WebControl> dynamicControls
collection and process the response.