Insert TextBoxes into a Repeater dynamically and retrieve their value - c#

I have a Repeater bound from SQL, containing a mixture of editable and read-only elements. When you click the "Edit" button on a row, the editable portions should convert to textboxes, and when you click "Update", it should save your changes.
Here's a (very) simplified version of the Edit and Update buttons' OnClick code:
switch(commandName)
{
case "Edit":
Label1.Visible = false; //hide read-only version
PlaceHolder1.Visible = true; //show editing version
//Dict1 is Dictionary<string, string> in this example.
foreach (var key in Dict1)
{
//insert a TextBox dynamically into the PlaceHolder
PlaceHolder1.Controls.Add(new TextBox
{
ID = "txt" + key,
Text = Dict1[key]
});
}
break;
case "Update":
//retrieve user input from dynamically-added TextBoxes
foreach (var TextBox1 in PlaceHolder1.Controls.Where(c => c.ID.StartsWith("txt")))
{
doStuff(TextBox1);
}
Label1.Visible = true; //show read-only version
PlaceHolder1.Visible = false; //hide editing version
break;
}
The problem is that my dynamically-added TextBoxes aren't there when the page posts back. I've examined PlaceHolder1.Controls in the debugger, and there are no TextBoxes in it. PlaceHolder1 itself is inside a Repeater, but I'm not rebinding the Repeater on PostBack.
I've considered using raw HTML in place of TextBox controls and pulling the values out of Request.Form, but that feels hackish to me. How can I make the dynamically-added TextBoxes persistent across postbacks?
EDIT:
There are some complications here that it's hard to show without a ton of sample code. Here are the big ones:
Each cell in the repeater can have a mixture of read-only and editable text (i.e. dynamically-inserted Labels and TextBoxes)
I don't know how many editable regions are going to be in each cell. The original text might look like blah blah #A1# blah blah #A2# blah..., and I would have to insert replacement text in place of #A1#, #A2#, etc. In edit mode, only that replacement text is editable.

When you add controls dynamically, they aren't preserved across postbacks. You have to add them to the page again after postback. I usually do it in Page_Load. Then, once they are added, their postback states will be restored correctly later in the ASP.NET page life cycle.
So,
Dynamic controls must be added each time the page is loaded,
but once you add them, ASP.NET restores their state correctly.
Hope this helps!

One possible answer would be to have the TEXTBOX pre-defined in the ITEM TEMPLATE, and then just set its' visiblity for the given row on the click event.
A second possibility would be to refactor the repeater to utilize the GRIDVIEW control instead. It supports editable templates out of the box.
Anyways, what's happening, is that since those controls are added dynamically, they're not getting recreated on the next postback. On the postback, you'll need to re-create the controls if you want to access them. BUT, don't forget that the value IS stored in the form collection:
myval = request.form["txt" + key];

Related

Asp.Net C# Dynamic GridView Options and Values on Sorting

I have a GridView Control that displays data from a table on Page_Load as well as a progress bar in one column and a button in another.
It then loops through each row of the Table and depending on the values, hides said button (i.e. if the 1st column value = "Open" then the button in the 8th column is hidden). It uses a simple foreach loop;
foreach (GridViewRow Row in MyGridView.Rows)
{
if(Row.Cells[0].Text == "Open")
{
Row.Cells[7].Text = "";
}
}
This is working absolutely fine on page load... unfortunately once the user sorts the data by the column values it doesn't work. I can get the event to fire (testing between OnSort and OnSorting amongst other events on the board) but it isn't actually making any changes to the table.
It seems the issues lays somewhere in how I'm attempting to initiate it... does anyone have any ideas?
Thanks in advance.
Managed to work this out through more testing. For anyone with a similar problem, use the DataGridViews OnPreRender Event.

Focus control inside DataGridView control

I have a Form, that contain a DataGridView control named gridParameters.
I'm adding control on-top of it, based on some values, to add controls that are not available (for example, DateTimePicker):
DateTimePicker dtp = new DateTimePicker();
gridParameters.Controls.Add(dtp);
dtp.Location = rowCellLocation;
dtp.Size = rowCellSize;
dtp.CustomFormat = "yyyy/MM/dd";
dtp.Format = DateTimePickerFormat.Custom;
dtp.Tag = rowId;
dtp.ValueChanged += dtp_SelectedValueChanged;
dtp.Visible = true;
That code is inside a loop that iterate throgh the grid rows, and is in an If statement (I add DateTimePicker for some rows and NumericUpDown for others based on a row cell value). The variables rowCellLocation, rowCellSize and rowId are set in a loop for each row.
My DataGridView selection mode is a full row selection.
I want to make the added control (DateTimePicker, NumericUpDown...) get focus when I switch to the row, for example, when I click on a row with the mouse.
To do so, I've tried the following:
In the loop that creates the controls, I set the control to the relevant row Tag property.
In the grid Row_Enter event (I also tried CurrentCellChanged) I added the following code:
if (gridParameters.CurrentRow.Tag != null)
{
((Control)gridParameters.CurrentRow.Tag).Focus();
}
I've also tried using Select() and also
this.ActiveControl = (Control)gridParameters.CurrentRow.Tag;
None of the above sets focus to the control.
I place a break point, and the code is getting to this line, the Tag property contain the control that need to be focused, and execute it, but I can't get the control to focus.
UPDATE: Setting focus to another control outside of the DataGridView, works just fine (for example, setting focus to an "OK" button works).

Gridview returning empty even when there is data. I want to use data in the gridview to update another database

I have a webform that the user captures data. This data affects other data that is in a Gridview. I wanted to right a DetailsView type control that makes an Update button visible if data is change in textboxes and drop downs that would require a master data update.
I have a gridview on the form and when the user click on update I wanted to update all the rows in the databases using the primary key OrderId which is on the Gridview.
If I put the code being each textbox and dropdown box and that calls a routine called DoUpdate then this works. However it is a little slow since each change forces an update, so I thought it would be better to rather have an update button appear, and then the user can click that to do the update.
When the routine is called from the button routine though the gridview has no rows. I assume I either need to findcontrol (gridview) or something but have not idea what to find it in.
Here is the code that works when called in a behind button event, but the gridview returns row count 0 when called as part of the code behind button:
List<string> _OrderIds = new List<string>();
foreach (GridViewRow gvr in gvOrderLines.Rows)
{
Label myOrderIDLablel = (Label)gvr.FindControl("lblOrderID"); //find control since it is a template field
_OrderIds.Add(myOrderIDLablel.Text);
}
The gridview is defined in the ASP page
<asp:GridView ID="gvOrderLines" runat="server" AutoGenerateColumns="False"
DataSourceID="odsOrderDetail"
Per my comment above you may want to do something like:
List<string> _OrderIds = new List<string>();
DataTable table = gvOrderLines.DataSource as DataTable;
foreach (GridViewRow gvr in table.Rows)
{
Label myOrderIDLablel = (Label)gvr.FindControl("lblOrderID"); //find control since it is a template field
_OrderIds.Add(myOrderIDLablel.Text);
}
I hope this get you headed in the right direction.

Complicated Viewstate issue on dynamic controls

I have a control that is loaded dynamically and contains a repeater amongst other items such as a group of text boxes.
The control is used for address verification.
The process is:
control displays.
User enters the address info.
page posts back and the control validates the address info against a web service.
If the address isn't 100%, it displays a list of potential matches. <- this occurs in a repeater. In other words, it is databound in code.
The user checks a box next to the one they want to accept.
page posts back
So far, 1 - 6 work. The problem is that when the page posts back, there are no repeater items.
How can I get the repeater to load it's items without having to run another databind() operation?
Failing that, how can I run the databind() effectively? In other words, I don't have the controls street/city/state/zip values during the oninit. So, how can I reapply the viewstate changes?
Most controls are rendered as HTML form elements like <input type="text" name="myId" id="myId/>.
Part of Web Forms is really just a wrapper around all of this.
Whenever you need to get at the raw values, you can just look in the Request.Forms collection for any control values that are posted back with the name of the control you are looking for (which will usually be made up of the unique id that asp.net generates for uniqueness of controls, and your name).
This is exactly what you did - you went through the Forms collection looking at the raw form values that were POSTed to the server. It's also what you often do when you implement the IPostBackDataHandler interface when creating your own controls - you scoop the value out of Request.Forms.
The annoying thing with checkboxes is that with "standard" HTML they only get submitted if they are ticked.
Two important facts:
Controls in a Repeater Item should be created in OnItemCreated and not OnItemDataBound.
OnItemCreated runs during a Postback without one having to call DataBind().
I've been loading user controls into repeater items and the type of user control is based on the data item being bound to the repeater. So I added the type of user control into the repeater item's viewstate so that during the OnItemCreated that runs on postback (without a data item to bind to because DataBind() has not been called) it knows what type of user control to load. The viewstate will then be applied.
protected void rpTransactions_OnItemCreated(object sender, RepeaterItemEventArgs e)
{
if (e.Item.ItemType == ListItemType.Item || e.Item.ItemType == ListItemType.AlternatingItem)
{
var dataItem = e.Item.DataItem;
string typeName;
string viewStateKey = string.Concat("typeName", e.Item.ItemIndex);
if (dataItem == null)
{
typeName = ViewState[viewStateKey].ToString();
}
else
{
typeName = dataItem.GetType().ToString();
ViewState[viewStateKey] = typeName;
}
Control template = TransactionTemplateFactory(typeName);
template.ID = "trans";
e.Item.FindControl("phTransSpecific").Controls.Add(template);
}
}
If the code in OnItemDataBound runs, it fishes this user control out and binds its data to it.

Is there a textboxlist control available somewhere?

There are ASP controls such as radiobuttonlist and checkboxlist and you can databind them to a database query. It's great for creating dynamic lists with user interaction. What I'm trying to do is generate a list of textboxes in the same fashion. A list of textboxes that behave the same way.
The object is to have a checkboxlist that is generated via datasource/database. When the user is finished selecting items from this list, they click a button. That list hides (using jquery) and a new list is created based on their selections. However, the new list is now a list of their selections accompanied by an empty textbox. The user fills in the textboxes for each entry and submits again which commits it to a database.
SO:
checkbox - description
checkbox - description
checkbox - description
checkbox - description
Becomes:
Description - Textbox
Description - Textbox
The reason that I'm looking for a list-type control is so that I can ultimately loop through it for submission to the database using linq. Does that make sense? My real question is if there is a control like this yet. I gave the full description in case someone has any other ideas, short of creating a custom control.
There's nothing out of the box that does what you describe no. But you can still loop through controls. I would put your form controls inside of an asp:Panel or a div with runat="server" and use something like the following code to cycle through them as you described.
foreach(Control ctl in myPanel.Controls)
{
//check control type and handle
if (ctl is TextBox)
{
//handle the control and its value here
}
}
asp:ListBox has a property named SelectionMode, which can be set to SelectionMode="Multiple" and allows you to select items you want. This is not exactly what you need but it's a simple solution.
After the selection is made in checkboxlist, make a postback to server. Here create a datatable with two columns (description & text). For every selected item in the checkboxlist add a row to this table and bind it to a gridview control. Here 'text' column will be always empty. Configure the gridview to use template column with a textbox in the itemtemplate

Categories

Resources