Why doesn't globally declared DataTable retains its value? - c#

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.

Related

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.

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

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";.

UltraWinGrid: How to refresh displayed data

i am new to infragistics and to winGrids
I have SQL database with the following table
Costumers
[ID][Name]
In addition , i have UserControl which has winGrid and add\remove buttons.
When the UserControll becomes, active winGrid datasource is bounded to the table
winGrd.DataSource = Tables.Costumers;
When user wants to add\remove data from costumers table he clicks the relevant button.
The table is changed accordingly but the displayed data in the grid is not changed.
I used
winGrd.Refresh();
but it does no effect
what is the way to do this, Code examples are welcome
thanks
---Edited ----
Adding code:
private void BtnAdd_Click(object sender, System.EventArgs e)
{
//...
DB.DataProxy.AddCostumer(txtType.Text);
winGrd.Refresh();
//...
}
AddCostumer method eventually calls for the following method that updates the costumer table
public void AddCostumer(string type)
{
Costumers.InsertOnSubmit(new InsertOnSubmit{ Name = name});
}
If your DataTable is being updated, the UltraGrid should be showing those changes for you. What you could try is to call
ultraGrid1.Rows.Refresh(Infragistics.Win.UltraWinGrid.RefreshRow.ReloadData);
or
ultraGrid1.Rows.Refresh(Infragistics.Win.UltraWinGrid.RefreshRow.RefreshDisplay);
Not sure, but, as from MSDN documentation on InsertOnSubmit()
The added entity will not appear in query results from this table until after SubmitChanges has been called.
So, perhaps, if you want the result appear immediately in the Costomers entity and then in the WinGrid, you should call, in the code above the SubmitChanges()
public void AddCostumer(string name)
{
Costumers.InsertOnSubmit(new Costumers() { Name = name});
// Submit the change to the database.
try
{
db.SubmitChanges();
}
catch (Exception e)
{
// message to the user???
}
}
Once you executed the add/remove command, re-pull the data from database and redo the binding. Set the DataGridView.DataSource to null before all that, just in case; you wouldn't want to display wrong data if the connection or any other database-related process fails.
I think it's because you are setting a DataSource that doesn't implements IBindableList which UltraGrid uses for automatically update data it changes.
You could try refreshing it manually setting DataSource to null and resetting DataSource again when you need to show new info.

global variable is null

Dictionary<string, string> propertyCompany = new Dictionary<string, string>();//gloabal variable
protected void Page_Load(object sender, EventArgs e)
{
if(!isPostBack){
propertyCompany .add("a","1");
propertyCompany .add("b","2");
propertyCompany .add("c","3");
}
}
protected void btnGetProperty_Click(object sender, EventArgs e)
{
string a=propertyCompany["a"];//error this key is not exist
//propertyCompany is null
}
when define a propertyCompany and fill in form_load. in on click button propertyCompany is null!?
i use a static but i does not understand sometime say error is null.
Each request creates new page object, therefore you cannot use in second request (bnt click) dictionary you have created in first request (load without postback)
Remove test for postback for quick fix.
Other fix posibilities:
* store dictionary in viewstate.
Every variable defined in a class inheriting Web.UI.Page will be destroyed at the end of the Page-Lifecycle, hence it will be null in a Postback if you don't reinitialize it.
One way to persist it across postbacks is to store it in a Session-variable.
You will find a complete list of all options on how to persist variables across postbacks here: http://msdn.microsoft.com/en-us/magazine/cc300437.aspx
Application
Cookies
Form Post / Hidden Form Field
QueryString
Session
New State Containers in ASP.NET
Cache
Context
ViewState
Web.config and Machine.config Files
Conclusion
It's in the nature of HTTP-protocol that it is stateless.
I'm pretty sure your title should be "global variable does not have the data I want." The dictionary will be constructed each time the page is loaded (postback or otherwise), but because of this line:
if(!isPostBack) {
}
it won't have the data you want on a button click.
In order to notify the page of the click, a post back is performed, so saying !isPostBack (which I'm assuming is set somewhere via Page.IsPostBack) is also saying "if I haven't clicked the button", which is of course not what you want.
In order to get the functionality you want, you should either move the population of the dictionary out of that if block, or else have an else condition that also populates it with data you want.
Another alternative to using the class variable is to store the data in another location. Options include ViewState, Session, Application (if it really is application-wide data), the Cache, and some others as well. It's not clear exactly what the dictionary is doing, so it's hard to say which location would be most appropriate.
One way to get your dictionary to live between requests is to declare it static, or in viewstate as was suggested earlier.
How are you accessing the items in propertyCompany in the button click event? If you are doing that incorrectly, that is more likely the issue.
try this
Dictionary<string, string> propertyCompany;//gloabal variable
protected void Page_Load(object sender, EventArgs e)
{
propertyCompany = new Dictionary<string, string>();
if(!isPostBack){
propertyCompany .add("a","1");
propertyCompany .add("b","2");
propertyCompany .add("c","3");
}
}
protected void btnGetProperty_Click(object sender, EventArgs e)
{
//propertyCompany is null
}

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.

Categories