Dynamically Repeating Form Elements - c#

I building an online CV application that allows the user to add new education sections that consist of a number of text boxes and labels.
What is the best way of dynamically creating the form elements and reading them depending on whether the user hits an [Add another] button.

1) server side :In your asp.net button click event , Create the required controls (text box, drop down etc..) dynamically and add that to a conainer like a Panel.
2)Client Side : Using javascript create elements and append to an existing container (div). In this approach, you save a server round trip because you are doing it in the client side.
Here is a javascript sample using jquery which will give you an idea.
HTML
<div id="divContainer">
Education 1 <input id="txt1" type="text" class="txtBox"/>
</div>
<input id="btn1" type="button" value="Add Another" />
Javascript
var counter=1;
$("#btn1").click(function(){
counter++;
$("#divContainer").append("<br/> Education "+counter + " <input type='text' id='txt"+counter+"' class='txtBox' />");
});
Here is the working sample : http://jsfiddle.net/huYMT/13/

I don't know the best way unfortunately but I can tell you what I did in web forms when I used to use them.
I made a PlaceHolder where I could dump in controls on the fly type of thing. I made a UserControl that could be repeated again and again that had public properties. I wrapped an UpdatePanel around the PlaceHolder and attached a click event tied to the UpdatePanel so the user would have the AJAX experience.
Here is kind of a quick summarized version of the code. I can give you the full source if you want. Let me know.
<script runat="server">
void aspx_Init(object sender, EventArgs e)
{
AddCreditCardReceiptButton.Click += new EventHandler(AddCreditCardReceiptButton_Click);
}
void AddCreditCardReceipt()
{
AddCreditCardReceipt(this.ID + "_" + Guid.NewGuid().ToString("N").Substring(0, 5));
}
void AddCreditCardReceiptButton_Click(object sender, EventArgs e)
{
AddCreditCardReceipt();
CreditCardReceipt lastAdded = (CreditCardReceipt)CreditCardReceiptPH.Controls[CreditCardReceiptPH.Controls.Count - 1];
lastAdded.ContainerStyle = "display:none";
ScriptManager.RegisterStartupScript(lastAdded, typeof(CreditCardReceipt), lastAdded.ClientID,
"$('#CreditCardReceipt_" + lastAdded.ClientID + "').toggle(500);", true);
}
</script>
<asp:UpdatePanel runat="server" id="CreditCardReceiptUpdatePanel" UpdateMode="Conditional" ChildrenAsTriggers="true">
<ContentTemplate>
<asp:PlaceHolder ID="CreditCardReceiptPH" runat="server" />
<asp:Button runat="server" ID="AddCreditCardReceiptButton" CausesValidation="false" Text="(+) Add Credit Card Receipt" /></div>
</ContentTemplate>

I prefer to create a user control for education section.
And if you hit the add button, The click event of button could dynamic add a new user
control in UI.

Related

Why is there no PostBack for a form on a UserControl?

I have a problem understanding where I am losing the ability to catch the PostBack trigger or simply the handler method of serverclick in this setup and placing breakpoints only shows the PostBack occurs on rendering (Page_Load) but not after submit button is clicked. Allow me to elaborate on the scenario and keep in mind I have limited ability to change the way it is done and must figure out a way to make it work with minor changes using ASP.net / C# / WebForms / Bootstrap 4.5 per the client.
Dashboard.aspx [main page] has a simple (top nav showing the logo and logged in user's name with a signout drop down, sidebar menu which upon individual click will load into a display area) and the display area is an asp:PlaceHolder element:
<asp:PlaceHolder runat="server" ClientIDMode="Static" ID="TheScreen">
When the [main page] is loaded, it makes an API call and processes a collection of objects received and generates adding a series of UserControl Control1 objects (this can be zero items or n items) let's call this [default state] of the landing so we can refer to it later.
Each of the Control1 (which have unique IDs) have 3 buttons to perform 3 different actions.
<a class="btn btn-primary btn-sm" id="action1" runat="server" onserverclick="Command_Click">Do First Action</a>
<a class="btn btn-primary btn-sm" id="action2" runat="server" onserverclick="Command_Click">Do Second Action</a>
<a class="btn btn-primary btn-sm" id="action3" runat="server" onserverclick="Command_Click">Do Third Action</a>
Command_Click simply bubbles up an event handler to be processed by Dashboard.aspx
public void Command_Click(object sender, EventArgs e)
{
CommandClicked?.Invoke(sender, e);
}
action1 is handled on the fly with a confirmation modal which contains a simple button that runs onserverclick and reloads the [default state] having deleted that item using an API call.
<button type="button" class="btn" id="do-action1" runat="server" onserverclick="finish-action1">Do It</button>
This all works as expected.
action2 is handled by [main page] where it clears the PlaceHolder's controls and then dynamically creates and loads another UserControl which displays long form details of that object.
UControl2 theObject = (UControl2)Page.LoadControl("~/path/to/Control2.ascx");
TheScreen.Controls.Clear();
TheScreen.Controls.Add(theObject);
This all works as expected also.
action3 button is the one that is giving me the problem and just like action2 it is handled on [main page] where it clears the PlaceHolder's controls and then dynamically creates and loads another UserControl which provide a simple form with one button on it.
UControl3 otherObject = (UControl3)Page.LoadControl("~/path/to/Control3.ascx");
TheScreen.Controls.Clear();
TheScreen.Controls.Add(otherObject);
The form is very simple, contains a <select> elements whose <option> are populated using an asp:Repeater based on an API call that provides the DataSource for it. It also has a simple <input> textbox field and lastly a submit <button>.
<select id="..." name="..." required="required">
<option disabled selected value="">select payment account</option>
<asp:Repeater ID="..." ClientIDMode="Static" runat="server" ItemType="model.namespace">
<ItemTemplate>
<option value="<%#: Eval("...") %>">
<%#: Eval("...") + " " + Eval("...") %>
</option>
</ItemTemplate>
</asp:Repeater>
</select>
<input id="..." name="..." type="text" required="required" runat="server" />
<button id="finish-action3" class="btn" runat="server" onserverclick="do-action3">Do It</button>
What I don't get is why finish-action3 is not performing the actions of the handler method do-action3, it simply refreshes back to the [main page] with the initial state that we started from with the initial Control1s being rendered and nothing else, no PostBack either.
I have searched extensively and found nothing addressing this specific scenario and what I have found, everyone keeps suggesting using asp:Button but that makes no difference for me in the behavior.
As written above, clicking finish-action3 button which is part of Control3 will not cause the basic HTML5 validation and does not trigger the do-action3 handler method and simply refreshes back to the [main page] where we started.
If I add do-action3 to the onsubmit attribute OR add type=submit, I get the validation but once it passes validation, the same behavior, nothing but load the [main page].
Changing the element to an asp:Button made absolutely no difference. The validation is triggered without anything special (like using onsubmit attribute or having a type=submit but once it is validated the same behavior, never calls do-action3 nor does it even trigger a Page_Load/PostBack on control3 just back to the [main page].
I suspect I am overlooking some event or is not bubbling and is getting lost in the process but I can't think what it is and where and why. Why doesn't the form that is last on the screen loaded by Control3 "submitted" by that button click not generating a PostBack or running the onserverclick handler method and simply refreshing to the main default state page, what am I missing here?
Any help would be appreciated as I have been banging my head against the wall trying to figure out what I am missing. I never encountered this in MVC or Core and it is making me crazy.
Let's simplify. Basically, what you are saying is that the following does not work because the btnFinishAction3_Click method of the dynamically-added Control3 user control is not firing:
Default.aspx (main page):
<asp:PlaceHolder runat="server" ID="plhPlaceHolder1"></asp:PlaceHolder>
<asp:Button runat="server" ID="btnAction3"
Text="Do Third Action"
OnClick="btnAction3_Click"/>
Default.aspx.cs:
protected void btnAction3_Click(object sender, EventArgs e)
{
AddControl3();
}
private void AddControl3()
{
Control3 objControl3 = (Control3)Page.LoadControl("~/Control3.ascx");
plhPlaceHolder1.Controls.Clear();
plhPlaceHolder1.Controls.Add(objControl3);
}
Control3.ascx:
<asp:Label runat="server" ID="lblMessage"></asp:Label>
<asp:Button runat="server" ID="btnFinishAction3"
Text="Finish Action 3"
OnClick="btnFinishAction3_Click"/>
Control3.ascx.cs:
protected void btnFinishAction3_Click(object sender, EventArgs e)
{
lblMessage.Text = "Finished Action 3.";
}
This is normal behavior. After postback the dynamically added user control does not exist and the event is "ignored". In ASP.NET Web Forms, every dynamic control must be re-added to the page after every postback. So, you need something like the following:
Default.aspx (main page):
<asp:PlaceHolder runat="server" ID="plhPlaceHolder1"></asp:PlaceHolder>
<asp:HiddenField runat="server" ID="hifControl3Loaded"/>
<asp:Button runat="server" ID="btnAction3"
Text="Do Third Action"
OnClick="btnAction3_Click"/>
Default.aspx.cs:
protected void Page_Load(object sender, EventArgs e)
{
if (hifControl3Loaded.Value == "1")
{
AddControl3();
}
}
protected void btnAction3_Click(object sender, EventArgs e)
{
AddControl3();
hifControl3Loaded.Value = "1";
}
private void AddControl3()
{
Control3 objControl3 = (Control3)Page.LoadControl("~/Control3.ascx");
plhPlaceHolder1.Controls.Clear();
plhPlaceHolder1.Controls.Add(objControl3);
}
In other words, you need to add Control3 to the main page at every postback on Page_Load once the user has decided to add it by clicking the btnAction3 button in the first place.

Variables and form data lost when Page_Load is run again after pressing button that calls method via OnClick

I am unfamiliar with C#/ASP/.NET (learning as I go), so it is very likely that I am going about this in an inferior way, in addition to the problems with my current way of doing this. I will try to be as clear as possible (maybe overly so...).
I have a form with a textbox to take in a list of server hostnames, separated by line returns. Upon pressing the submit button, it uses PostBackUrl="~/btil_Info.aspx". In btil_info.aspx.cs codebehind, I get the info from said textbox (hostnames_tb) from the previous form in Page_Load() using:
string hostnames = ((TextBox)Page.PreviousPage.FindControl("hostnames_tb")).Text;
Within Page_Load(), I loop through this list of hostnames and build a form containing several fields for each host (a few textboxes and a dropdown menu for each host). After filling out these fields, upon pressing the Submit button here which calls Submit_Click() in the same codebehind, the page appears to reload because Page_Load is called again before Submit_Click() is executed. In doing so, the the filled form data is lost and the list of hostnames is also lost.
At an earlier stage in development, I had this all on the very first form page, and the submit button didn't call PostBackURL, it simply called Submit_Click(), which worked perfectly fine. The page didn't reload, the form stayed on the page, and I didn't lose data. But as it is now, the button works the same way. The only difference (that I see) being that this troublesome page is reached via the previous form calling PostBackURL.
I don't believe there's any point where anything should be requesting a page refresh/reload. I don't actually care if the page refreshes/reloads as long as the form data is posted and as long as the list of hostnames from the previous form is also retrievable. I just need to be able to access the form data and list of hostnames so that I can pass it to another method of mine that will update a SharePoint list (the code for that part is already working, though). I tried making the hostnames string a class variable in the 2nd codebehind (btil_Info.aspx.cs) and setting it as soon as soon as the page loads if it is empty, but apparently it does not persist over the page reload and is set back to null.
Here's some snippets of code that I hope will be of some help:
First page, add_BTIL.aspx:
<form id="form1" runat="server">
<div>
<asp:TextBox ID="hostnames_tb" runat="server" Rows="20" TextMode="MultiLine" Width="225px"></asp:TextBox>
<br />
<asp:Button ID="Submit" runat="server" PostBackUrl="~/btil_Info.aspx" Text="Submit" />
<br />
<asp:Literal ID="result" runat="server"></asp:Literal>
<br /><br />
<textarea id="hostnames_textarea" cols="25" rows="20" name="hostnames" title="Hostnames"></textarea></div>
</form>
First page codebehind, add_BTIL.aspx.cs:
protected void Page_Load(object sender, EventArgs e)
{
}
protected void Submit_Click(object sender, EventArgs e)
{
string hostnames = hostnames_tb.Text;
/*
* Etc.
*/
}
Second page, btil_Info.aspx:
<form id="hosts_form" runat="server">
<p>
<asp:Button ID="Submit" runat="server" Text="Submit" OnClick="Submit_Click" Height="26px" UseSubmitBehavior="False" /><br />
<asp:Literal ID="result" runat="server"></asp:Literal><br />
</p>
<br />
</form>
^ In this form, I read somewhere in my searches that UseSubmitBehavior="False" would prevent a page reload, but it did not help. I didn't use it in my earlier version anyway, and did not have this issue.
Second page codebehind, btil_Info.aspx.cs:
protected void Page_Load(object sender, EventArgs e)
{
string hostnames = ((TextBox)Page.PreviousPage.FindControl("hostnames_tb")).Text;
// etc.....
}
protected void Submit_Click(object sender, EventArgs e)
{
// etc.....
}
Many thanks for any help!
If you're posting from add_BTIL.aspx to btil_Info.aspx then the Submit_Click function in file add_BTIL.aspx.cs will never be called.
Based on the limited markup you've given us for the btil_Info.aspx page...
If you aren't rendering your host names list inside of server controls inside the form id="hosts_form" then when you hit the submit button nothing is going to be posted to your Submit_Click function.
Personally I don't like post from one page to another so here are some suggestions below.
If you want to do a multi-page wizard then you might consider the asp.net wizard control.
Or if you want to keep it simple, use two asp Panel controls and show the first one, post the data, and then hide the first and show the second one.

Method being triggered on page load but not via asp button

I have two asp:Labels, the first of which is replaced with a few buttons and the second with a list of items.
I want to click on the buttons to filter the items.
The contents of the buttons are added programmatically by replacing the text with html and works fine.
asp:
<form id="form1" runat="server">
<asp:Label id="filters" runat="server" Text="Filters here"/>
<asp:Label id="itemList" runat="server" Text="List of items here"/>
</form>
resultant html of filters label:
<input type="submit" onclientclick="Load_Items(0)" runat="server" value="First"/>
<input type="submit" onclientclick="Load_Items(1)" runat="server" value="Second"/>
<input type="submit" onclientclick="Load_Items(2)" runat="server" value="Third"/>
relevant c#:
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
Load_Items(0);
}
}
public void Load_Items(int filterType)
{
//code to load items (pseudo below)
for each row in list
if filterType = itemType
build string
replace second label with string
}
On page load everything happens just as I want it to with the contents being filtered by the first item (hence Load_Items(0)), and if I manually change the 0 to another number in Page_Load, it filters by the other types, but if I click the buttons which are programmatically added, nothing happens other than what looks like the page refreshing.
I know the post back check is working by adding a text replacement before and inside it.
I've also added an asp:button to make sure it's not something to do with the way the buttons are added as below (with some extra things recommended from searches):
<asp:Button runat="server" CausesValidation="False" onclientclick="Load_Items(2); return false;" text="Submit" />
So what could be the issue?
The OnClientClick property specifies the javascript to run in the browser when the button is clicked. Since you probably don't have a javascript function called Load_Items, this will generate a script error, and the button will then cause the form to post back.
The server-side Click event will fire on the server, but doesn't allow you to pass a parameter. You will only get the button instance and an empty EventArgs instance.
You might be better off using the Command event, combined with the CommandArgument property.
<asp:Button runat="server" CommandArgument="2" OnCommand="Load_Items" ...
The event handler would use the CommandArgument property of the CommandEventArgs to access the argument from the clicked button:
protected void Load_Items(object sender, CommandEventArgs e)
{
Load_Items(Convert.ToInt32(e.CommandArgument));
}
Well, that's the common problem which I think every asp.net developer deals some time. The common part of it, that asp.net event system doesn't work, as windows forms.
Page object, and all controls on that page, have lifecycle events, that are triggered during any request, even when it's from update panel.
As you create those controls by code, you have to keep in mind, that all events for those controls should work as part of Page object. That's why you have to create those object in Page_Init event, before all other control's event would be triggered.
Please also keep in mind that you have to create those controls as asp.net objects:
var btn = new Button();
But not by simply adding html markup. And you have to recreate them on each request, following that one, when they were created.
Please take a look on my another answer.

Using (static)HTML Controls in Code-Behind, Coding with C#/ASP.NET

I am working in VS10 using C# in ASP.NET. In my design form, I have a textArea(standard HTML control).
<textarea id="Text1" rows = "8"; cols="30" onkeyup="AutoGrowTextArea(this)" name="S1"> </textarea>
Now in my code-behind page, I am using C# to code the controls. I have given the textArea an auto-expand functionality which I require thru out my project. I need this TextArea as a server control, like when we put a textBox in the design page, we can use it in the code-behind page to code, since it is a server control. However, textArea isn't a server control.
I have gone thru the previous posts on the site but i did not get anything enough useful.
I have even tried using [<% %>] system, and [runat="server"] but it did not help.
What i wanna do is to use the textArea in the Code-behind page, i.e. call it in the coding space, just like we can call the TextBox control objects.
So, can anyone please help me with this,,,
Regards..
javascript for autogrowing text box is:
<script type="text/javascript">
function AutoGrowTextArea(textField)
{
if (textField.clientHeight < textField.scrollHeight)
{
textField.style.height = textField.scrollHeight + "px";
if (textField.clientHeight < textField.scrollHeight)
{
textField.style.height = (textField.scrollHeight * 2 - textField.clientHeight) + "px";
}
}
}
</script>
Try this
add runat="server" to the tag
<textarea id="Text1" runat="server" rows = "8" cols="30" onkeyup="AutoGrowTextArea(this)" name="S1"> </textarea>
you can manipulate raw html controls setting the runat="server" propery of the control.
<textarea id="Text1" runat="server" rows = "8"; cols="30" onkeyup="AutoGrowTextArea(this)" name="S1"> </textarea>
You can add runat="server" in your markup and it will be a server control.
You can also create server controls in your code behind. Declaration (In Visual Basic):
Protected WithEvents foo As Global.System.Web.UI.HtmlControls.HtmlGenericControl = Global.System.Web.UI.HtmlControls.HtmlGenericControl("textarea")
If you want to use something in all your project, you can declare a class inherited from Control or HtmlGenericControl and you can implement whatever you need to implement everywhere where you your new control.
<asp:TextBox id="thisIsMyTextBox" runat="server" multiline="true" width="200px" onkeyup="AutoGrowTextArea(this)" height="80px" style="resize:none;"></asp:TextBox>
You can play with the width/height as you wish !
function AutoGrowTextArea(textField)
{
if(textField.Lenght()%50==0)//50 is the number of character in your textbox
{
if (textField.clientHeight < textField.scrollHeight)
{
textField.style.height = textField.scrollHeight + "px";
if (textField.clientHeight < textField.scrollHeight)
{
textField.style.height = (textField.scrollHeight * 2 - textField.clientHeight) + "px";
}
}
}

Clickable Webcontrol, ASP.NET

This is question is in relation to my last question in case you want some more background information.
My question is: Is it possible to make a cell in an asp.net Table clickable?
Or Is it at least possible to make a clickable WebControl (which should be possible to place in a ControlCollection), that is not a Button or a LinkButton, in ASP.NET?
And if not, is it possible to multiple lines of information into the button text?
I've tried adding other components to the button's ControlCollection (which I've seen working in the Windows Forms version of the Button), to see if I could render child components to a button, but without success:
private void ModifyTableCell(TableCell cell)
{
//Create new button
Button btnCell = new Button();
btnCell.Click += (sender, args) =>
{
//Event for the button
};
//Create new Label
Label lblCell = new Label();
lblCell.Font.Bold = true;
lblCell.Text = "This text won't appear";
btnCell.Controls.Add(lblCell); //Attempt to add label to Button
cell.Controls.Add(btnCell);
}
EDIT: I ended up just creating a multi-lined LinkButton for the entire cell.
You should be able to make pretty much any control clickable by assigning an onclick attribute and leveraging the __doPostBack function.
ctrl.Attributes["onclick"] = string.Format("__doPostBack('{0}', '{1}');", ctrl.ClientID, "SomeArgument");
You could also use the GetPostBackEventReference method too. This option is actually safer, because it will register the __doPostBack function it doesn't already exist:
ctrl.Attributes["onclick"] = Page.ClientScript.GetPostBackEventReference(ctrl, string.Empty);
Then, in the code-behind you can simply override the RaisePostBackEvent method:
protected override void RaisePostBackEvent(IPostBackEventHandler source, string eventArgument)
{
base.RaisePostBackEvent(source, eventArgument);
if (eventArgument == "SomeArgument") //using the argument
{
//do whatever
}
}
You can add multiple lines to a asp.net button by using the string builder, something like:
System.Text.StringBuilder buttonText = new System.Text.StringBuilder();
buttonText.AppendLine("first line");
buttonText.AppendLine("second line");
btnMultiline.Text = buttonText.ToString;
I am not sure how you imagine such a composite control would be rendered. Remember that each ASP.NET control in the end outputs HTML. You Button essentially outputs a
<input type="button">The button text</input>
If you want to place anything else inside the <input> tag, it must be HTML-compatible. I'm not sure the input tag allows other HTML inside.
If it is a LinkButton on the other hand, the generated HTML markup is an <a href=""> tag. You can put anything there, even an image if you wish, which will become clickable.
I am not sure what is your full scenario, but what you're trying to do smells bad. I suggest that you either use a LinkButton or rethink your approach, just have in mind what the final output in HTML would be.
In reading both of your posts, it looks like you want to be able to click on anything in the cell and have it post back as if the whole cell were a button?
If so, one of the fairly simple ways to do it is to build your cell content as you would if you were not trying to post back. Then add a button with a style of display:none;. You can then use client side scripting to capture the click event of the cell, and raise the click event of the button to cause a post back.
This allows you to create the cell content in any way you like, and the postback code and handlers are automatically generated for you.
Using JQuery you end up with something along the lines of this:
<head runat="server">
<style>
input.ClickableCellButton
{
display: none;
}
</style>
</head>
<body>
<form id="form1" runat="server">
<script src="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.7.1.min.js" type="text/javascript"></script>
<script type="text/javascript">
$(document).ready(function () {
$("td.ClickableCell").click(function (event) { $(event.target).children("input.ClickableCellButton").click() });
});
</script>
<div>
<table>
<tbody>
<tr>
<td class="ClickableCell">
Cell Contents<br />
on multiple lines
<asp:Button ID="Button1" runat="server" Text="Button" CssClass="ClickableCellButton"
OnClick="Button1_Click" />
</td>
</tr>
</tbody>
</table>
</div>
</form>
</body>
And if not, is it possible to multiple lines of information into the
button text?
For this particular case, you can accomplish this via CSS only; no need to extend Button:
<asp:button id="myMultilineButton" runat="server" style="width:60px; white-space: normal;" Text="Several lines of text" />
It will render as:

Categories