Saving webControls in the Session object - c#

I have a panel (pnlPanel) with lots of controls like Textboxes and DropDownLists. I want them to be persistent when the user gets back to the page, so i tried this:
/*i have saved the panel like this
Session["testPanel"] = pnlTest;
*/
protected void Page_Load(object sender, EventArgs e)
{
if (Session["testPanel"] != null)
{
panel = Session["testPanel"] as Panel;
}
}
But its not working. Is it possible? The reason why i want to do this is because overhead is not a problem, and i want to cut down on coding time.

I've never tried this myself, but this seems to me to be an extra-ordinarily bad idea. Without testing it, my guess would be that this will create a ton of ViewState problems. Even if you could maintain the ViewState, attempting to keep this control over multiple page loads would be dangerous at best.
My recommendation would be to have a common object that holds the properties of the panel you want and just build a method into one of the early events to prepopulate a new panel with those properties.

Without knowing the entire reason for doing something like this, you should have a look at output caching directives. You would be best served by pulling the content out of the panel and into a user control. Then setting output caching on control, using VaryByCustom so you can use the user name or some other unique identifier to separate by user.
http://msdn.microsoft.com/en-us/library/hdxfb6cy.aspx and
http://msdn.microsoft.com/en-us/library/system.web.httpapplication.getvarybycustomstring.aspx
Using session and/or caching will be problematic if you are in a webfarm scenario. Cache is scoped to the application instance, so another server in the web farm will not have access to it.
Some other side effects of something like this include issues with viewstate.

What you try to do here is to cache the Panel but this is not the way. The panel as you save it is a running object on the memory and can not be saved as it is. You need to convert it to html string and save and cache this string. So near the Panel you place a literal, then you render the Panel and save it on session, and then actually you display the text from this render.
if(Session["testPanel"] == null)
{
TextWriter stringWriter = new StringWriter();
HtmlTextWriter renderOnMe = new HtmlTextWriter(stringWriter);
// render and get the actually html of this dom tree
testPanel.RenderControl(renderOnMe);
// save it as cache
Session["testPanel"] = stringWriter.ToString();
}
// render the result on a literal
cLiteralID.Text = Session["testPanel"];
// hide the panel because I have render it on literal.
testPanel.Visible = false;
Need some tests as is it. I use some similar code for custom control and custom cache, never save on session this amount of data.

First Approach
protected void Page_Load(object sender, EventArgs e)
{
if (ViewState["panel"] != null)
{
panel = ViewState["panel"] as Panel;
}
}
In this approach your ViewState objects were different. You may be getting some null values once the ViewState["panel"] is given the control memory and the object is being accessed in the impression that the Session was Session["panel"]
Second Approach
Save the Complete panel HTML in database and access it on the form load by keeping the function under IsPostBack.
Now with the continuity of approach - 2 assign the value to your session object.
this.Controls.Add(new LiteralControl("Your HTML"));
Third Approach
You can use File system. Save the div in your file and access the file at runtime.
Hope this may help you.
EDIT - 1 => Added code for second approach

I had a similar problem. I tried to save an object to the View State that stored a Panel and I got an error message telling me that Panels aren't serializable. You could try using a SerializationSurrogate.
https://msdn.microsoft.com/en-us/library/system.runtime.serialization.iserializationsurrogate(v=vs.110).aspx

Related

asp.net/C# control - store value from previous page load

control (eg label) changes value when page loads.
before page load, label value is "x"
proceed to load page naturally x is lost at this point, so what is a simple way to store the value from the previous page
load?
It sounds like what you want is a history of previous states of the page, or at least just one previous value. I can suggest a way to do this, but with a caveat: Don't overuse it. You can persist data between posts in ViewState but that data gets written to the page itself. That way when the users posts the form, they're also posting that data back. (More at the end.)
Here's a simple example. First, define some class that contains all of the additional state you want to store:
[Serializable]
public class PageState
{
public string MyLabelPreviousText {get;set;}
}
Then in your code behind:
public partial class MainPage : System.Web.UI.Page
{
private PageState _pageState;
protected void Page_Load(object source, EventArgs e)
{
_pageState = ViewState["pageState"] as PageState ?? new PageState();
_pageState.MyLabelPreviousText = MyLabel.Text;
}
protected void Page_PreRender(object sender, EventArgs e)
{
ViewState["pageState"] = _pageState;
}
When you load the page, you're checking to see if you've already saved your class (in this case called PageState) to the ViewState. If you haven't, you create a new one.
In the PreRender event, after you're done updating the page, you're updating that class with the text of your label and then saving the whole thing to ViewState again. That way the next time the page loads you can retrieve it again.
I'm not sure at which point you want to save the previous text of your label. In this example it's saving the text during the Load event. That way if the text is changed at any point from then on, the value you have saved is the original text of the label. The details may vary depending on what you're trying to do, but this pattern lets you save that sort of custom data without using Session and piling up data like this in memory.
I mentioned not overusing it. If you save some labels, controls, and other data, ViewState probably won't get too large. ASP.NET is already putting the state of every server control there anyway. But if you go really crazy with it then ViewState can get huge. All of that data gets written to the page, and all of it gets posted back to the server with each postback.
It's probably not a huge concern, but be mindful of it. Use Chrome dev tools, Fiddler, or even just inspect your page source to see if that data is getting so large that it might impact performance.
For anyone who isn't familiar, ViewState looks like this in the HTML source:
<input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE"
value="/wEPDwULLTEyMzgxNzgyNDIPZBYCZg9kFgICAQ9kFggCAQ9kFgJmD2QWAgIBD2QWAmYPZBYMA
... etc.
As you can see, it's actually a hidden form input. ASP.NET is placing all of the details of the page's controls in the form so that when you post the form, it can piece all of that together and make sure that the page looks the same after postbacks. That's the reason why when we set up a lot of controls the first time we check if(!IsPostBack()). If it is a postback then we don't need to populate the dropdowns, etc. all over again. They are restored from that ViewState data.
This approach is functionally the same as adding an additional hidden input as suggested in one comment. The difference is that you're using an existing hidden input (viewstate) and if you need to save multiple values you're just making them properties of a class instead of adding more and more hidden inputs.
I'm not sure if this is what you mean without any code samples but I know from memory that in ASP.NET you can access the previous page property like so:
Page lastPage = currentPage.PreviousPage
Which returns the entire Page object. Assuming your label is defined like so:
<asp:label id="myLabel" runat="server" />
Then you can access the text property with:
Label myLabel = lastPage.FindControl("myLabel") as Label;
lastPageVal = myLabel.Text
So ensure that lastPageVal is a static variable, then it will also persist throughout pages.

ASP.Net passing textfield value to a label on a different web page

I have a page called webForm1, this page contains a textfield, when a user enters a value, I want the value to show up in a label on webForm2, when I do that, I am getting an error:
Label1 is inaccessible due to its protection level
This is what I am doing in webForm1
webForm2 webform = new webForm2();
webform.Label = textBox1.Text;
Response.redirect("~/webForm2.aspx");
but the above is not working, I am new to programming and not familiar with classes and complicated programming, what is the easiest way to get the value of the textbox in the label?
Thank you.
You can't instantiate the page class (webForm2) in your current page. You'll have to pass the value in another way to the second page and then bind the label. As Jason P says, the ASP.NET framework instantiates the webForm2 page for you, you can't do it yourself.
If the data is not sensitive, use the Query String:
Response.Redirect("~/webForm2.aspx?label=" + textBox1.Text);
This will redirect the user to a page with the url of whatever.com/webForm2.aspx?label=whatevervalue. On the second page, you can pull the text from the query string and bind it to the label:
public void Page_Load(object sender, EventArgs e)
{
Label.Text = Request.QueryString["label"].ToString();
}
Unlike WinForms, you don't instantiate the next form like that. Essentially, your first two lines are incorrect for WebForms. The third line is where you want to focus your attention. You redirect the user to the second form, allowing the framework to take care of instantiating it.
This is because WebForms, despite being "forms", is still an HTTP web application and does everything through requests and responses. By issuing a redirect you are telling the client to abandon the current page and make a new request for the specified page.
There are a number of ways to send a value to this next page. You can store it in some persisted medium (such as a database), you can use session state, etc. Probably the simplest approach at the moment would be to include it on the query string:
Response.Redirect("~/webForm2.aspx?label=" + textBox1.Text);
Then in the next page you'd get the string from:
Request.QueryString["label"]
You may want to URL-encode the text value first, I don't know if Redirect() does that for you. Also keep in mind that this isn't a "secure" transfer of data from one page to the next, because the client has full access to modify values in the URL. So if this is in any way sensitive data then you'll want to look into other approaches. (Keep in mind that "sensitive" could be a relative term... The information itself might not be sensitive but you might be doing system-sensitive things with it on the next page, which we can't know from the code posted.)

How to maintain the tree view user interactions through post back?

I use RadTreeView in my master page :
but i face the following problem :
I lose my selections when i click on the nodes of the tree view .and every time i click on the node it makes (Response.Redirect("...")) which make the master entering the (!Page.IsPostback)
every time and bind the tree view again so i lose my selections .
How to fix this problem .
My .aspx :
<telerik:RadTreeView ID="rtv_cm" runat="server" OnNodeExpand="rtv_cm_NodeExpand"
OnNodeClick="rtv_cm_NodeClick" Skin="WebBlue">
</telerik:RadTreeView>
My .cs :
protected void Page_Load(object sender, EventArgs e)
{
if (Session["emp_num"] != null && !string.IsNullOrEmpty(Session["emp_num"].ToString()))
{
if (!Page.IsPostBack)
{
LoadRootNodes(rtv_cm, TreeNodeExpandMode.ServerSide);
}
}
else
{
Response.Redirect("Frm_login.aspx");
}
}
protected void rtv_cm_NodeClick(object sender, RadTreeNodeEventArgs e)
{
dt_childs = (DataTable)Session["dt_childs"];
IEnumerable<DataRow> result = from myRow in dt_childs.AsEnumerable()
where myRow.Field<string>("task_id").TrimEnd() == e.Node.Value.TrimEnd()
select myRow;
if (result != null)
{
if (!string.IsNullOrEmpty(result.ElementAtOrDefault(0).Field<string>("page_name")))
{
Response.Redirect(result.ElementAtOrDefault(0).Field<string>("page_name").TrimEnd(), false);
}
}
}
How I get The menu :
private void LoadRootNodes(RadTreeView treeView, TreeNodeExpandMode expandMode)
{
dt = Menu_db.GetMenu(Session["emp_num"].ToString(), CMSession.Current.db_code);
IEnumerable<DataRow> result = from myRow in dt.AsEnumerable()
where myRow.Field<string>("task_id").TrimEnd() == "0"
select myRow;
if (result != null && result.Count()>0)
{
DataTable dt_roots = result.CopyToDataTable<DataRow>();
Session["dt"] = dt;
Session["dt_roots"] = dt_roots;
foreach (DataRow row in dt_roots.Rows)
{
RadTreeNode node = new RadTreeNode();
node.Text = row["task_name"].ToString().TrimEnd();
node.Value = row["task_id"].ToString().TrimEnd();
if (Convert.ToInt32(row["count"]) > 0)
{
node.ExpandMode = expandMode;
}
treeView.Nodes.Add(node);
}
}
}
HTTP is what's known as a "stateless protocol". Basically, it's as if the server has a severe case of Alzheimer's disease (no disrespect intended). The server answers your query, but then forgets you were ever there.
When HTTP was used primarily for fetching static documents, this posed no problem, but web applications require some kind of state. There are many ways this is accomplished, but ASP.NET typically makes heavy use of the "ViewState". The view state is simply an <input type="hidden"> tag which contains base-64 formatted byte code. (If you view the source of your rendered HTML page in the browser, you see it - named "__VIEWSTATE"). When the user resubmits the form (by clicking a button, etc), the viewstate data is sent back to server. Basically, it's like reminding the server about what it told you last time. This is how TextBoxes and GridViews are able to seemingly maintain their state between postbacks - they store their state in the "viewstate".
The problem with the viewstate, though, is that it can be easily lost. You must submit the form or the viewstate will not be persisted. If you use an anchor tag or Request.Redirect, you are side-stepping the form and hitting a web page all on your own and, in the process, you are not passing any of the viewstate along.
There is often no way to avoid this, so there are other ways to store your application's state. But, unlike the viewstate, this is something you must do manually for each and every value that you want to persist. You can store the data in a cookie, you can store the data on the server (using Server["key"] or Session["key"]) or you can persist the data in something more concrete, like a database or a text file. All of this must be done manually, however, and then you must reload the data when you reload the page.
In your case, you may want to guess which item was selected based on the current page (since the treeview items and pages seem to be linked) and set the selected item explicitly. If that's not feasible, you could try storing the selected item in the Session, using something like Session["Master_SelectedTreeViewItem"] = treeViewItem.Id;. Then, in Page_Load, get this value (careful, it may be null) and then set the corresponding treeview item as selected.
I would post more code examples, but you haven't provided the code where you are loading the treeview, etc, so it would be difficult to do.
Further Info
ASP.NET State Management Overview (MSDN)
The only way i can think of is storing the selection in session and on (!Page.IsPostBack), look for that Session key and update your selection after the binding.
The problem you face is because essentially, trough your Redirect, you go to a different html page, so ViewState is out the window, nevermind that it is the same address.
If you are binding the tree with urls that link to other page, append the selection using query string, then load the tree selection from the query string from the other page.
If you think that the second page you are redirecting to has same UI as the first, dont redirect instead load it on the same page with the new content for the selection, this will allow you to maintain the state in asp.net if the control supports view state.
I already had this problem, exactly same problem, needs to redirect on TreeViewItemClick.
I solve it storing the last selected item is session, and selection it again on page load.
Somethink like this
protected void rtv_cm_NodeClick(object sender, RadTreeNodeEventArgs e)
{
Session["LastClickedNode"] = e.Node.Value;
...
}
and in your Load method, verifi ir the node needs to be selected, like this.
private void LoadRootNodes(RadTreeView treeView, TreeNodeExpandMode expandMode)
{
//... your code
if (Session["LastClickedNode"] !=null)
{
if (row["task_id"].ToString().TrimEnd() == Session["LastClickedNode"].ToString())
{
node.Selected = true;
}
}
}
i think this will solve your problem.

Adding a control to a list of Controls dynamically

I have a web page where users need to enter customer contact information. They could enter from 0 to an infinite number of contacts.
I created this page code on page:
<ajaxToolkit:ToolkitScriptManager runat="Server" EnablePartialRendering="true" ID="ScriptManager1" />
<asp:PlaceHolder ID="phCustomerContacts" runat="server" EnableViewState="true">/asp:PlaceHolder>
<asp:LinkButton ID="btnAddContact" runat="server" OnClick="btnAddContact_Click" CssClass="LinkButton" Text="Add Contact"/>
In my code behind I added this:
public void btnAddContact_Click(object sender, EventArgs e)
{
IList<CustomerContactProfile> customerContacts = new List<CustomerContactProfile>();
if (ViewState["CustomerContactList"] != null)
customerContacts = (List<CustomerContactProfile>)ViewState["CustomerContactList"];
CustomerContactProfile contactProfile = (CustomerContactProfile)LoadControl("~/Controls/Embedded/CustomerContactProfile.ascx");
customerContacts.Add(contactProfile);
foreach (CustomerContactProfile contact in customerContacts)
phCustomerContacts.Controls.Add(contact);
ViewState["CustomerContactList"] = customerContacts;
}
This code doesn't work because the ViewState can't handle storing all of that control data. However, I cannot think of another way to store the controls that were already added.
The viewstate of the asp:PlaceHolder control doesn't save anything and I need the controls to be saved so that if a user puts in some data to the first control that the data isn't lost when they add a second one and so on.
Rather than store the entire control, simply store the underlying data in session, and rebuild the control set from that data every time you reload the page.
I'm not sure it's a best way to add contacts dynamically. Wouldn't it be better to create controls via jquery, and send data for creation to web method ?
It would be better to store the number of controls in viewstate to add instead... And then add them in Page Init or PreInit... ViewState would then be retained for each of the dynamic controls. This would be for postbacks after the button click of course.
HTH.
Store the number of controls the user has entered in the view state. Override the LoadViewState page method and add back the number of controls there. The framework will take care of reloading the posted data into the controls for you. You will not lose information. You just have to make sure you add the controls BEFORE the viewstate is restored.
Store it in the Session instead of Viewstate. It's just as bad but it will work!
I think you should not depend on any temporary storage for this -- Viewstate, Session, or otherwise.
You seem to be using your .ascx like I would normally use a class... User control's going to be bigger, though, I imagine, since it has lots of html in it (?).
Anyway, a generic list of a class would be...smaller, at least.
But otherwise, my favorite approach is just to insert each record into a database when it's done (one-by-one) -- at least for manual input, which that's my impression of what you're working with. For example, using a listview, detailsview, gridview, etc.

If one page executes a Response.Redirect() to a different web page, can the new page access values in asp.net controls from the original page?

I have a text string value that I'd like to persist from one web page to another without using query strings or the session/view states. I've been trying to get the ASP http://msdn.microsoft.com/en-us/library/system.web.ui.webcontrols.hiddenfield.aspx">HiddenField control to pass information from one web form to a different form.
All the hiddenfield control examples that I've seen is to preserve round trips from the client to the server for the same form.
Is there way for a form to access the ASP controls (and their values) from the previously-rendered form? Or is the initial form simply disposed of in memory by the time the second form executes it's OnLoad method?
Quick answer is no. As others have noted, you can use Server.Transfer and then you can - however this is to be used with caution. It is a "server side redirect" eg.
Your user is on http://mysite.com/Page1.aspx they click a button and you perform a Server.Transfer("Page2.aspx"). Page2.aspx will be rendered in their browser, but the URL will still be Page1.aspx, this can cause confusion and mess up back/forward navigation.
Personally I would only use Server.Transfer as a last resort - in the world of the web, sharing data across pages generally means you need to use a storage mechanism; Cookie, QueryString, Session, Database - take your pick.
You can't get the previous page fields with Response.Redirect.
You can with cross page posting :
if (Page.PreviousPage != null)
{
TextBox SourceTextBox =
(TextBox)Page.PreviousPage.FindControl("TextBox1");
if (SourceTextBox != null)
{
Label1.Text = SourceTextBox.Text;
}
}
If both pages live in the same application you can use Server.Transfer:
firstpage.aspx:
protected void Page_Load(object sender, EventArgs e)
{
Server.Transfer("~/secondpage.aspx");
}
secondpage.aspx:
protected void Page_Load(object sender, EventArgs e)
{
Page previousPage = (Page) HttpContext.Current.PreviousHandler;
Label previousPageControl = (Label) previousPage.FindControl("theLabel");
label.Text =previousPageControl.Text;
}
A somewhat better solution would be implementing an interface on your first page where you expose properties for the values needed by the second page.
I would presume that the Response.Redirect() sends a Location: HTTP header to do a redirect.
As HTTP is stateless, I'd also presume that these variables are inaccessible.
There are however, solutions.
Print a form with hidden fields, and use javascript to submit it
Redirect in the code internally (load up the thing it needs to get to manually)
Store the data in some temporary database table somewhere, and pass along a unique ID
However, from my experience, I can't understand why you might need to do this (other than re-submitting a form after a user authentication - which hopefully you should be able to use method 2 for
Remember, a Response.Redirect instructs the browser to issue another request to the server. So far as the server is concerned, this next request is indistinguishable from any other incoming request. It's certainly not connected to a previous form in any way.
Could you explain your aversion to storage in the session, so we can propose some viable alternatives?

Categories