How do I maintain viewstate on a user control - asp.net

I have an ASP Page that uses two listboxes and a third party control (Fluent.ListTransfer) to transfer values from one list to the other. Fairly standard stuff:
<td>
<asp:ListBox ID="ListBoxAvailable" Runat="server"
SelectionMode="Multiple" EnableViewState="true">
</asp:ListBox>
</td>
<td style="vertical-align: middle">
>"/>
<br />
<img src="img/LeftArrow.jpg" alt="<<"/>
</td>
<td>
<asp:ListBox ID="ListBoxSelected" Runat="server"
SelectionMode="Multiple" EnableViewState="true">
</asp:ListBox>
</td>
On the Controls Page_load event, I set the content of the 'available' and 'selected' box with some sql:
protected void Page_Load(object sender, EventArgs e)
{
if (IsPostBack)
return;
// Some code to work through a recordset, adding values
// to the selected or available list boxes
I assumed that on postbacks, the viewstate would take care of the state of listboxes, as changed by the user (after all, they are standard ASP controls). However, both list boxes are blank if I do a postback.
What's the proper way to maintain the state of the listboxes between postbacks?
Thanks for any help
Ryan

ViewState is like a "spy" on the client side. Whenever there's a change in the client side, the ViewState will report back (read: PostBack) the changes to the server so that the server can re-process the page.
Unfortunately, if we alter contents of a control like ListBox using client-side scripts, ViewState does not see it.
I guess your Fluent.ListTransfer is a client side function.
Some solutions:
The listbox store in ViewState only
the selected item (I guess only the
index), not all the items from list,
so it knows only to restore the
selected item, not the whole list. Is
a little bit strange but is logic. To
mantain in ViewState all the items you
can write a new ListBox derived from
ListBox or HtmlSelect, but you must
handle in the same time the
possibility that the items from list
box be modified on client using
javascript code.
When a page posts back none of the
items in a listbox are sent back to
the server EXCEPT those that are
SELECTED on the client. This makes
sense when you think about it. Forget
about viewstate and all the rest. You
can select multiple items so make all
the items you add to listbox 2
selected.
Another workaround to this, but not so
elegant. The basic idea is to record
all the items on ONE LISTBOX to a
control, and repopulate the listboxes
using the control's value everytime
the page being posted back. Hopefully
somebody could come up with something
better:
Put a HIDDEN input in your web page. Remember to put RUNAT=SERVER
attribute so that you can reference
your input from your ASP.NET code
behind.
In your Javascript, whenever you change the contents of one listbox,
concatenate all the values - using a
separator - and put the value into
your HIDDEN input.
On your Page_Load code, put the code to read the HIDDEN input,
separate the values, and fill in the
listboxes.
Hope this all helps...

If I understand your code correctly you are moving ListItems on the client side from one ListBox to another. But ListBoxes do not submit their whole content - only selected items. So you have to maintain that. I don't know, if Fluent.ListTransfer will do that for you.

Related

How can I persist the changes of a text box when it loses focus?

I have several text boxes on a page. I want to save the text in the corresponding TextBox on LostFocus. It is to ensure the data is not lost when power failure or internet connectivity is lost. How can I accomplish something like this?
Another solution would be to use an UpdatePanel. You could then leverage server-side events rather than an event handler. You're markup might look something like this:
<asp:ScriptManager ID="ScriptManager" runat="server" />
<asp:UpdatePanel ID="UpdatePanel1" UpdateMode="Conditional" runat="server">
<asp:TextBox ID="SomeTextBox" runat="server" TextChanged="SomeTextBox_TextChanged" AutoPostBack="true" />
</asp:UpdatePanel>
</asp:ScriptManager
and then in the TextChanged handler you could do your work.
Explanation: the TextChanged event will fire on the text box in which the text was actually changed when the post back occurs. The AutoPostBack property is necessary so that the page will in fact post back when the text box loses focus.
One final thing to remember, you will probably need to leverage this.IsPostBack on the page in the Load handler because every time a control is updated it's going to reconstruct the page. It's common that logic in the Load handler only needs to execute one time, but that would pose a problem if you didn't check to see if it was a post back because then it would execute every single time you left a text box.
Use jquery.
First on attach blur event handler, where you call ajax method to server passing new value of the textbox.
Handle this ajax event on serverside and write your data to the database or anywhere else.
Here is a piece of code that may help.
$('#textbox_id').blur(function() {
$.ajax({url:"handler_url.ashx?textbox_value=" + $('#textbox_id').val()});
});
Then create your ashx handler on server and handle your requests with ProcessRequest method. From there you will have access to Request.QueryString where new value of textbox will be stored.

Why is ASP.NET ViewState retained with an asp:dropdownlist but not an asp:table?

Alright, there is probably a super-obvious answer to this question for many of you, but it has me stumped.
I have an asp.net web form, and on it I have two controls (well, more than these two but we'll focus on these) - the first is an asp:dropdownlist and the second is an asp:table.
Both of these controls are declared on the HTML side, and filled (child controls added) in the code-behind page.
My simple question (hopefully with a simple answer) is this:
Why does the viewstate persist for the dropdownlist, and NOT for the table?
I have to fill the table on every page load, but I can fill the dropdownlist once (using Not Page.IsPostBack), and it persists.
NOTE: I have read about the lifecycle of ASP.NET pages, and I have tried placing these same calls in the Init() and PreInit() page events with the same results.
What obvious detail am I missing here?
Thanks for the help.
You're not missing anything, your assessment is correct. ASP.NET tables do not save their contents to view state.
I assumed at least part of the reason is that a table can contain any amount of any type of data and could really start to add to the size of the view state in some cases.
Either way, that's the way they work. If postbacks are possible on your page, you'll need to either repopulate the table on each load event, or store the table data to view state yourself and then repopulate the table from the data.
The asp:table object does not store its contents in ViewState, as it is not a data bound control. It works the same as an asp:panel in this regard; if you add controls to it programmatically, you need to do so on each post-back, or the items in it will not persist.
If you wanted to store the contents of the table in viewstate - which I woudln't recommend, but sometimes you need to do so ... you would just have to use the standard asp.net table control.
<asp:Table ID="Table1" runat="server">
<asp:TableRow ID="TableRow1" runat="server">
<asp:TableCell ID="TableCell1" runat="server">
This is Cell 1
</asp:TableCell>
<asp:TableCell ID="TableCell2" runat="server">
This is Cell 2
</asp:TableCell>
</asp:TableRow>
</asp:Table>
You can read more how to do this at http://weblogs.asp.net/nannettethacker/archive/2008/01/24/html-tables-to-asp-net-table-controls.aspx.
You might also try adding runat="server" to the table tag.
Not really answering your question directly, but thought it might be helpful nonetheless.

Why is the ASP.NET Repeater.Items collection empty, when controls are on the screen?

I have an ASP page with the following repeater:
<asp:Repeater runat="server" ID="RegionRepeater"
DataSourceID="SqlDataSourceRegions" EnableViewState="true">
<ItemTemplate>
<tr>
<td valign="top">
<b><%#Eval("description")%></b>
<asp:HiddenField runat="server" ID="RegionID"
Value='<%#Eval("region_id")%>'/>
</td>
<td>
<asp:FileUpload ID="FileUpload" runat="server" Width="368px" />
</td>
</tr>
</ItemTemplate>
</asp:Repeater>
(The repeater is inside a Wizard, inside a content pane).
The code behind is connected to the
protected void Wizard1_NextButtonClick(object sender, WizardNavigationEventArgs e)
event. There are two items on the screen (two rows inside the table). However, when the code tries to read those items, the Items collection is empty!
foreach(RepeaterItem region in RegionRepeater.Items)
{
// Never runs - the RegionRepeater.Items.Count = 0
FileUpload fileUpload = (FileUpload) region.FindControl("FileUpload");
String regionID = ((HiddenField)region.FindControl("RegionID")).Value;
...
Why is the collection empty, when there are controls drawn on the screen?
Thanks a lot for any help; this is starting to drive me nuts.
(BTW: I tried adding/removing the EnableViewState="true" tag)
Have you made sure the repeater has been rebound on the postback?
The ASP.NET Page Life Cycle means that on a postback, you will need to rebind controls like the repeater in order for event handlers to be able to see the data.
Check if you have a Page.DataBind() in the page code behind. That makes your repeater to bind with empty data.
Maybe you do the databinding every time you load the page, do you check that there is no postbacl before you do it ?
I believe Items is only populated when bound. So you have to rebind your data on every page load. This is due to the statless environment of the web; it doesn't remember the data source, but loads the created controls from viewstate that were bound to it.
So on subsequent requests, it loads the control hierarchy but knows nothing about the data source that created the UI unless you rebind again.
HTH.

Ajax dropdown limit to list

pretty much what the title says.
I am using an Ajax Drop Down as illustrated here:
http://www.asp.net/AJAX/AjaxControlToolkit/Samples/DropDown/DropDown.aspx
using linkbuttons ... is there a way to limit to list?
Thank you.
Edit: I think it was VB 6 maybe that you could select "LimitToList" in a drop down. Meaning the user can only select the values in the drop down and not enter his own data.
Since you're extending a textbox, I think the best option would be to attach an event listener that voids keypresses, you could do this in the ASPX:
<asp:Textbox id="txtFoo" onkeypress="return false;" runat="server"/>
Or, in the code behind:
txtFoo.Attributes.Add("OnKeyPress","return false;");
This will prevent a user from typing in the textbox, essentially creating the effect you want.
A bonus side effect is that a user is allowed to free type an entry if javascript is disabled and the dropdown extender doesn't work.

How to populate UpdatePanel in Repeater in ASPX (not code-behind)?

I have a Repeater which displays a list of items (in a grid-like/table view). When a user clicks an item, I display an UpdatePanel under this item with additional information relevant to the item (similar to Accordion control). I know how to populate the elements of the UpdatePanel in code-behind (I pass the ID of the selected element in the Repeater control as CommandArgument, get additional info for this ID, and set up the text fields of the active UpdatePanel controls). But I'm wondering if I could set up binding directly in the ASPX (instead of code-behind). When I used the <%= %> syntax to assign text fields of the UpdatePanel control the values of the page properties, e.g. <%= Comment %>, it sort of worked, but it changed the fields of all UpdatePanels in the repeater. Is there any way to bind the active UpdatePanel to the current values and leave already bound UpdatePanels unchanged?
Are you looking to display a container that displays additional information? Is there other activity in the "box" that requires it be an updatepanel?
<asp:repeater>
<itemtemplate>
<%# Eval("Name") %> <%# Eval("LastName") %><br />
<span onclick="$get('<%# Eval("Id") %>')">View Age</span>
<div id="<%# Eval("Id")%>" style="display:none;">
Age: <%# Eval("Age") %>
</div>
<itemtemplate>
</asp:repeater>
Ithink that's right, some syntax may be off a bit (typing without intellisense). Would that work?
I used ID as a unique identifier for the div id and the onclick command. You could also use jquery, asp:controls or whatever else you wanted.
Easiest way is to nest a FormView inside the update panel. Then the only thing you need to do in the code behind is get the additional info, assign it to the FormView.DataSource, and call FormView.DataBind(). Everything in the FormView will use the <%# Eval("SomeColumn") %> syntax. You'll probably need to use a FindControl() to get a reference to the FormView. I'd type up the code for you but I'll save you some headaches down the road and say DON'T DO THIS.
The update panel is about the most inefficient way to do any ajax stuff. The only way to get it all to wire up correctly with this repeater and server side code is to either have a gigantic viewstate or to rebind the repeater in your page load. You are turning a request that could be 300ms into something that will take over a second...or longer! Get familiar with a good ajax framework and don't be afraid to write real html. At the very least, use a webservice that loads a usercontrol with your markup.
I know the update panel is easy, and it's built in. It might even be adequate for what you are doing, but you must resist. You'll be glad you did.

Resources