variable initialized in class loses its previous value with the page loading - c#

I've declared a String variable test with "hi". every time I click on Button1, I expect that test will be appended with its previous value. But I have noticed that it loses its previous value when the button is clicked and the page is reloaded. That is every time I click it, it has its text as "hihi". I expect "hihihihi" on the next click and so on. What's the problem here with the code below?
public partial class _Default : System.Web.UI.Page
{
String test = "hi";
protected void Page_Load(object sender, EventArgs e)
{
}
protected void Button1_Click(object sender, EventArgs e)
{
test += test;
Button1.Text = test;
}
}

No, that's not the way asp.net works. If you need that behavior you should do this:
public string test {
get {
return (string) ViewState["test"] ?? "hi";
}
set {
ViewState["test"] = value;
}
}
When ASP.NET sends a request to the server, a new version of your class is instantiated. If you need to get the state, you need to use ViewState (which is saved in a hidden field in the browser and sent with every request, and therefore state saved per page), or you can use SessionState which is a state saved per user. SessionState by default is saved in memory. So, if you restart IIS, this state will go away. Note that viewstate's state will NOT go away if you reset IIS (since it's being sent by the browser). You can also use the Cache which again, is saved in memory. This state is for all users of your application. The same rules about resetting IIS apply. Finally, you could make your variable static. As I said, every time a request is made a new version of your class is instantiated. Of course, static variables are not instance variables, so the state of a static variable is saved across postbacks as well. The same rules about IIS reset apply to static variables as Cache and Session.

A field only exists for the duration of a single request. If you want it to live between requests you'll have to use something like session-state, view-state, a cookie, or a HTML form / request value.
In most "real" applications, you can't even guarantee that subsequent requests are being handled by the same physical machine.

Every time you visit a page, a new instance of the page is created with its own copy of your local variable. There are several ways you can persist values from one page view to the next, and they are all described here: ASP.NET State Management Overview

String test = "hi";
This is a private, instance class field.
You need a static one if you want to achieve your goal.
BTW, honestly, maybe you're looking to use a session item:
HttpContext.Current.Session["test"] = "hi";
Doing this way you'll have a code like this in your event handler:
string currentTestText = (string)HttpContext.Current.Session["test"];
currentTestText += currentTestText;
Button1.Text = currentTestText;
HttpContext.Current.Session["test"] = currentTestText;

Thats because a button generates a POST BACK you could declare the variable as a Static Property or Create a Session["Test"] or add some code on the button click if IsPostback {}

The problem with your code is that on every request your Page instance is recreated so test won't keep the previous value since it belongs a new Page instance.
This is the flow:
Request 1 Start
Page is created -> test = "hi"
Request 1 Ends
Page is detroyed
Request 2 Start
Page is created -> test = "hi"
Request 2 Ends
Page is detroyed

Remember that on a postback the asp.net recreates the objects and reassigns the values. In your case the test variable gets recreated and gets assigned the value of 'hi'. You might want to store the variable in session and then append the value.

This works, just try it
// in the page load event
if(!this.IsPostBack)
Button1.Text = test;
// in the Click event
this.Button1.Text += test;
The problem with your current code is that you are assigning an instance variable to the button text, since it is an instance variable it is being initialized every time you request the page with the same value that's why you always get hihi only and not hihihihihihihi
Every time you click the button, ASP.Net creates a new Page(), therefore the test member will always be initialized like: test = "hi";.

Related

How to determine what page you've just come from within OnNavigatedTo function?

I'm creating a UWP app in C#. I'm trying to run different blocks of code within my OnNavigatedTo function depending on what page sent me there. Right now I have if else statements determining which blocks of code are ran depending on what page sent me to the page I'm on now.
My existing code is shown below:
protected override void OnNavigatedTo(NavigationEventArgs e)
//runs every time MainPage is Navigated to from another page or when program is first started and MainPage loads
{
if ([SomeAttribute == Page2])
{
//attempt to add string from page[2] to List
if (e.Parameter is string && !string.IsNullOrWhiteSpace((string)e.Parameter) && !InspectorInst.Names_list.Contains(e.Parameter))
//if thing passed from previous page (e.Parameter) is a string and isnt null or whitespace and isnt already in the Names_list
{
string s = e.Parameter.ToString();//create string to hold e.Parameter
this.InspectorInst.Names_list.Add(s);//Add string to Names_list
}
}
else{
//do something else
}
base.OnNavigatedTo(e);
}
[SomeAttribute == Page2] should be something that will return true if the page that directed me to the page I'm currently on was Page2. And SomeAttribute would return the page that sent me to the page I'm currently on. I have not been able to find anything in the UWP documentation that would accomplish this.
If am going about this problem the wrong way and there is an easier way to accomplish this, what would that be?
Thanks
I think that if you want a page to have a different behavior depending on where it is called, the normal way would be to pass different values as the second parameter to the Navigate method.
If you absolutely want to know which page has called, then you can examine the last entry in the navigation stack, like this:
var entry = this.Frame.BackStack.LastOrDefault();
if (entry != null)
// Check entry.SourcePageType - it contains the type of the previous page
Note that you may also have to check the NavigationEventArgs.NavigationMode to see if the user is going back from the page or not.

How to call a C# Method in a VisualWebPart (.cs) from different Class within same Namespace

I am very new to C# and Sharepoint Programming.
I am trying to learn about WebPart and C# used on it. I made a visual webpart which adds/deletes Items on a list. I have a Method which is called on a Button click which add Item in the List.
Here is my Method:
public void TestMethod()
{
using (SPSite oSPSite = SPContext.Current.Site)
{
using (SPWeb ospweb = oSPSite.OpenWeb())
{
SPList lst = ospweb.Lists["CusomList1"];
SPListItem item = lst.Items.Add();
item["Item1"] = txt1.Text;
item["Item2"] = txt3.Text;
item["Item3"] = Convert.ToInt32(txt3.Text);
item["Item4"] = txt4.Text;
item.Update();
}
}
}
This is called as:
protected void Button1_Click(object sender, EventArgs e)
{
TestMethod();
}
This works Fine. I am trying to use the same method on a second WebPart which does the same thing (add item).
However when I added a new Visual Webpart on the same project and called the class and method as
protected void Button1_Click(object sender, EventArgs e)
{
VWP1 NewClass = new VWP1();
NewClass.TestMethod();
}
This Add button does not work and when I do a debug I get the following message:
Object reference not set to an instance of an object.
Can someone please tell me what should I do?
What you need to do is separate out the logic of saving the item in the list from the logic of interacting with the user interface.
Make a separate function that takes the data to be saved and saves it:
public static void SaveItem(string item1, string item2, int item3, string item4)//TODO rename parameters
{
SPListItem newItem = SPContext.Current.Web.Lists["CusomList1"].AddItem();
//set fields of new item
newItem.Update();
}
Then you can put that method in a utility class somewhere.
After you've done that you can call the method from each of the webparts:
protected void Button1_Click(object sender, EventArgs e)
{
MyUtilityClass.SaveItem(txt1.Text, txt2.Text, Convert.ToInt32(txt3.Text), txt4.Text);
}
As for why, there are a number of things going on here. The main problem is that when you create a new instance of the first visual webpart and call the method it's not accessing the textbox values of your second webpart, it's accessing the textbox values of the newly created webpart; the one that hasn't ever been shown the the user, or initialized by ASP. Because ASP (or you) never called it's initialization functions the textbox field were all null, hence your error. If you did initialize it then they'd all have empty text values, and it still wouldn't help you. The interaction with textboxes needs to happen within each of the different webparts; you can't (or at the very least shouldn't; it would be bad practice to allow it) access the internal controls from another class. What you can move to another class is everything besides the actual UI interaction; in this case the saving of the item to a list.
Several side notes:
You put the current context's SPSite in a using block; that will dispose of it. Don't do that. Only dispose of Site/Web objects that you create. The current context's site/web objects are re-used for different request; if you dispose of it that request will work, but when the disposed object is passed to the next request it will break, and it results in often hard to debug errors as the problem is in another request entirely.
You open up a new web in that example; are you sure that the current context's web isn't appropriate? If it's not, and you really do want the root web and won't always be at the root web, you can use SPContext.Current.Web.RootWeb to access it without opening up a new one.
You are using Convert.ToInt32 on a user provided value. This will break if they don't enter a proper number, if they included commas, etc. Consider using int.TryParse so that you can more gracefully fail if they enter an invalid value.
You shouldn't use list.Items.Add() to add an item; you should use list.AddItem(). Items.Add is considered deprecated.

ASP.NET - How do I register multiple JavaScript calls to run on PostBack?

I have an ASP.NET page with two instances of the same Web User Control (a simple WYSIWYG editor). On submit, the WUCs do a little JavaScript magic and then proceed with a normal postback.
The first instance seems to be working, but the second fails to post changes back to the server (it reverts to the original, and posts that). I believe the problem is that the JS only fires for the first WUC. I've traced that to the following code, from the generated client-side source:
function WebForm_OnSubmit() {
prepHtml('AddEditPopup1_ctlEditorQuestion_txtEdit','AddEditPopup1_ctlEditorQuestion_divEdit', 'AddEditPopup1_ctlEditorQuestion_divHT' );
//snip...
}
The problem seems to be that there should be two calls to prepHtml: one for the ctlEditorQuestion instance of the WUC, and one for the ctlEditorAnswer instance.
Instead, there's only the one for ctlEditorQuestion. Both controls are registering the OnSubmit event, but one of them overwrites the other.
The prepHtml call is registered from the WUCs' C# code at runtime:
//Page_Load
_onSubmit = String.Format("prepHtml('{0}','{1}', '{2}' );",
txtEdit.ClientID, divEdit.ClientID, divHT.ClientID);
//OnPreRender
Page.ClientScript.RegisterOnSubmitStatement(this.GetType(), "get-html", _onSubmit);
I should point out that I didn't write this control myself, and I've never seen this kind of runtime registration of OnSubmit JS code before. Page.ClientScript.RegisterOnSubmitStatement is totally new to me.
I need to register both prepHtml calls so they run sequentially. Is this possible? I'm open to alternatives to Page.ClientScript.RegisterOnSubmitStatement, so long as the code still gets fired on submit.
This should do what you want without tightly coupling the controls to the page.
protected void Page_Load(object sender, EventArgs e)
{
if (!Page.IsPostBack)
{
string onSubmit = string.Format("prepHtml('{0}','{1}', '{2}';",
txtEdit.ClientID,
divEdit.ClientID,
divHT.ClientID);
Page.ClientScript.RegisterOnSubmitStatement(this.GetType(),
this.Id + "_getHtml", onSubmit);
}
}
The key (as I mentioned in my comment) is the unique name. Notice that I use "this.ID" in the script name. The ID property is guaranteed to be unique within the page, so it would be a good candidate.

How to get feedback to a page from a running thread in ASP.NET?

So I have this interface that is just one big GO button that syncs a bunch of data from one tool to another. The problem is it takes a really long freaking time and some users are left wondering whats the deal. So I am wondering if there is a way that I can put something in my loop so that every so many entries it sends something back to the page to update them on the progress.
Currently it is just an .aspx page with an aspx.cs behind it. The Go button fires off the whole process and it calls Response.Write a ton of times (as well as writing the same thing to a log file I made) but the Responses don't show until the entire thing is done.
Please advise.
You could design a class which will be stored in the session and which will represent the current state of the operation:
public class OperationState
{
public object Result { get; set; }
public int Progress { get; set; }
public string Error { get; set; }
}
An instance of this class could be created when you start the operation and store it in the user session. Then at each step of the operation you could retrieve it from session and update the progress property. Once the operation terminates you could set the Result property or the Error property in case an exception occurs. In the meantime you could design a PageMethod which will be accessible from client script. This method will simply return the State instance from the session. You will then invoke it periodically and asynchronously from javascript to check the progress and update the DOM to notify the user.
I am assuming you are calling another class to do the work. Lets call this the WorkerClass
You can have the WorkerClass have an event hooked up to it, that the .aspx page hooks up too and will write a message when the event is triggered.
// Overload EventArgs to send messageas back up
public delegate void UpdateMethod(object sender, EventArgs e);
public class WorkerClass
{
public event UpdateMethod UpdateMethod;
}
WorkerClass worker = new WorkerClass();
worker.UpdateMethod += new UpdateMethod(worker_UpdateMethod);
EDIT based on Comment it is on there page
If you don't want to refactor to another class doing the work (which I suggest). You can post the messages this way.
protected override void Render(HtmlTextWriter writer)
{
base.Render(writer);
this.ProcessMassiveWorkLoad();
}
private void ProcessMassiveWorkLoad()
{
for(int i = 0; i < 100000; i++)
{
// Do some work
// Write the fact you have work
Response.Write(string.Format("Done {0} of 100000", i);
}
}
The simplest way to resolve your issue is to call Response.Flush() after each Response.Write.
This will flush the current response buffer back to the client, enabling them to see the current state of the page.
Even David's method would need this to get the responses out to the user in a timely manner.
The better solution would be along the lines of Darin's solution, which would involve some client side scripting of (say) an update panel, that you refresh with a JavaScript timer to get the latest state, but that may introduce other issues for you (needing JavaScript turned on, rewriting the long running method as something you can fire off asynchronously, etc).
If it's any consolation, I've done both in the past, and would use either again.

Why doesn't globally declared DataTable retains its value?

I have an aspx page with a gridview. In my page load event, I load a datatable with all the data like so:
HistoricalPricing historicalPricing = new HistoricalPricing();
DataTable dtHistoricalPricing = new DataTable();
protected void Page_Load(object sender, EventArgs e)
{
if (!Page.IsPostBack)
{
dtHistoricalPricing = historicalPricing.GetAuctionData();
}
}
The above loades the data into the datatable fine. I also have a listbox which contains a list of auctions. When I click on an auction, I use the RowFilter on a DataView to display a Gridview with the data that was selected, but the DataTable seems to loose its value and I can't figure out why. Here is the code below:
protected void lstAuctions_SelectedIndexChanged(object sender, EventArgs e)
{
DataView dvPricing = new DataView(dtHistoricalPricing); // Loses Value
dvPricing.RowFilter = "Auction = 1"; //Hard-Coded for Test
gvPricing.DataSource = dvPricing.ToTable();
gvPricing.DataBind();
}
Every time you do a postback you're dealing with a new instance of your page class. That means a new datatable object as well.
If you really want to persist it between postbacks (and make sure you consider the memory implications for that when you may have 1000 people hitting this web server at the same time) then you can put the datatable in the Session, ViewState, or other location that persists state.
I think I figured it out, is it because when I click on the ListBox, it does a postback and I am only loading the data on the first load of the Page? If this is correct, I think I answered my own question.
I put the datatable in Session after loading it on the first Page Load and this seemed to solve my problem. Not sure if this is the best way though.
Sort of answered your own question. You are creating a new instance of the object every page load, so when the listbox posts a postback, your code is dealing with a different object.
You would be better to declare the object globally, and then instantiate it in the !Postback code eg:
DataTable dtHistoricalPricing = null;
...
if (!Page.IsPostBack)
{
if (dtHistoricalPosting == null)
{
//shouldn't need to do a new dtHistoricalPricing as the method below is returning a new instance?
dtHistoricalPricing = historicalPricing.GetAuctionData();
}
}
It is difficult to persist values between requests in ASP.NET. The most reliable way would be to put it in ViewState, but that will send the whole thing to the client and back, so you shouldn't put much data there. The session is an alternative, but it can become a problem when a user opens several windows with your page within the same session. Then there is also application state and cache, but those are shared among ALL requests (regardless if user). Plus, if you have a web farm, the values in there are local for every server. Even more, IIS can spawn several ASP.NET processes on the same machine too, and they will each have their own Application State. The same can be said about static variables.

Categories