Page refresh Vs IsPostBack - c#

I've got an index page which sends users to an edit products page on separate browser tabs.
For each products edited the index rewrites the Session["ProductID"].
The Edit page then has the following code to have a unique identifier for this tab and product:
if (!IsPostBack) //first time page load
{
Random R = new Random(DateTime.Now.Millisecond + DateTime.Now.Second * 1000 + DateTime.Now.Minute * 60000 + DateTime.Now.Minute * 3600000);
PageID.Value = R.Next().ToString();
Session[PageID.Value + "ProductID"] = Session["ProductID"];
}
This works, and when the same user opens multiple tabs I only reference the Session[PageID.Value + "ProductID"] in my code so that I always have the proper ID. (I'm working in a trusted environment this is for an intranet, therefore I'm not too bothered with the level of security).
My issue occurs if the user does a page refresh by hitting the F5 key. At which point the Session[PageID.Value + "ProductID"] gets the Session["ProductID"] of the last product he opened.
For example:
User 1 opens product1 in tab1
User 1 opens product2 in tab2
Whenever they use the tool normally, everything works fine. However if:
User 1 on product1 page hits the refresh button (F5) the product1 page becomes product2 page
Is there a way to detect a page refresh from a "first load/redirect from another page" so that I can then tell my page not to update my Session[PageID.Value + "ProductID"]?

Personally, I would go for URL parameters. E.g. pass the product IDs as URL parameters.
If you need the pages without parameters, you could e.g.
Pass parameter to page.
Page reloads itself if parameter is present and removes the parameter
This way you could distingues between first call (=parameter present) and second+ call (parameter not present).

I've solved a very similar issue by storing two versions of a state-identifying parameter: one in Session and one in either the ViewState or the URL (QueryString).
If you compare the two values on Page_Load, that will tell you whether the session variable has changed since the page was first loaded. This should be just what you need.
EDIT: Rough sketch of the code (warning - haven't seen the actual code since I wrote it 3 years ago):
protected string currentProductID
{
get
{
return Request.QueryString["ProductID"];
//or:
//return (string)ViewState["ProductID"];
//or:
//return HiddenField1.Value;
}
set
{
Response.Redirect(ResolveUrl("~/MyPage.aspx?ProductID=" + value));
//or:
//ViewState.Add("ProductID", value);
//or:
//HiddenField1.Value = value;
}
}
protected void Page_Load(object sender, EventArgs e)
{
//If the problem only occurs when not posting back, wrap the below in
// an if(!IsPostBack) block. My past issue occurred on both postbacks
// and page refreshes.
//Note: I'm assuming Session["ProductID"] should never be null.
if (currentProductID == null)
{
//Loading page for the first time.
currentProductID = (string)Session["ProductID"];
}
else if (currentProductID != Session["ProductID"])
{
//ProductID has changed since the page was first loaded, so react accordingly.
//You can use the original ProductID from the first load, or reset it to match the one in the Session.
//If you use the earlier one, you may or may not want to reset the one in Session to match.
}
}
In the above code, note that changes to the ViewState (including the value of a Hidden control) will only take effect on the next PostBack. On a refresh, they'll revert to their most recent value. In my case, that was what I wanted, but it sounds like it's not quite right for your situation. Still, that information could be useful to you, depending on how you implement this.
I've left out a discussion of comparing currentProductID to Session[PageID.Value + "ProductID"], since I've already posted a lot of code, and I don't know the details of what you're trying to do. But there are various ways you can use Session, ViewState, and the QueryString to glean information about the state and history of the page.
Hopefully that should give you the general idea. Let me know if that's not enough to get you going.

You may want to have a look at this. I think it's close to what you're looking for.

Related

Find all elements by xpath attribute (aria-required = true) .//*[#aria-required='true']

I'm trying to get a list of IwebElements that contain the attribute aria-required. Reason why I'm trying to get this list, is so that i can check if all required fields are necessary for the user to fill out, before he can continue to the next page.
So far after 2 days of searching I'm still convinced that it shouldn't be that hard. I'm using the expression:
".//*[#aria-required='true']"
From my research that would mean that it will search for ALL the elements starting from the root of my webdriver.
[TestMethod]
public void CreateProjectWithoutRequiredFields()
{
GoToProjectPage();
tracking = CM.GoToNewlyCreatedFrameAfterClickButton("ftbNew", tracking, theDriver);
CM.Wait(2000);
bool succesSave = false;
CM.LogToFile("Create project whitout required fields", tracking);
foreach (IWebElement e in theDriver.FindElements(By.XPath(".//*[#aria-required='true']")))
{
FillDataInNewProjectPage();
e.Clear();
CM.GetDynamicElementById("btnSave", theDriver).Click();
try
{
CM.GetDynamicElementById("titel", theDriver).Click();
}
catch (Exception)
{
succesSave = true;
NUnit.Framework.Assert.IsFalse(succesSave, "The page is saved with succes, without entering text in the following required fields : " + e.GetAttribute("id").ToString());
}
CM.Wait(1000);
}
}
I will try to explain what i did here:
First i went to a overview page with all my existing projects. On this page i clicked the ftbNew button to create a new project. The driver is automatically switch to the correct frame (i now the right frame is selected because i used this line on other page's.)
then the line
foreach (IWebElement e in theDriver.FindElements(By.XPath(".//*[#aria-required='true']")))
{
should normaly find a the elements in my driver with an attribute "aria-required='true'"
Then it would fill in the page with data, clear the first element that is found from its data en try to save it.
if the element titel is found on the page, than we are still on the same page en the save action wasn't successful ( <- so this is good)
so next we again overwrite every field on the page and clear this time the second element that is found.
en so on...
What I'm guessing, that xpath has difficulty finding the 'old' aria-required attribute... When i try to validate my expression using firebug and other xpath checkers, the aria-required attribute isn't always present. Sometimes it finds the input field sometimes it doesn't.
source code page
As you can see in the firebug console not all attributes are loaded, account-manager has a aria-required attribute, but project leader doesn't. If i inspect the element again, but this time click project leader. The attribute will be loaded. Very strange....
Extra info: I am using frame's and i know a lot can go wrong if you are situated in the wrong frame, but i am sure that he is looking in the correct frame. Especially because i can't find the elements using firebug with the above expression. If i change my expression to .//input, it will find the elements but also selects input fields that aren't required.
In advance i want to thank everybody that want to look into my problem :)
Adriaan
Based on your description of the behavior on the page, you cannot rely on the aria-required attribute to indicate a required field. I think that you should go back to the developers of the site to ask them to give you a reliable handle. It may be as simple as looking for the "*" as the last character of the label associated with the input field, but that's kind of annoying to have to deal with.
As an aside, you should consider catching a more specific exception for your success case. Right now, if ANY exception happens, you'll declare success, but that may not be what you really want to do.

Efficiently populating an ASP.Net Calendar control

I have an ASP.Net Calendar control that, on DayRender, loops through a list of "CalendarEvents" (one of my classes). If the day that DayRender is on is in the list of CalendarEvents, then fill that calendar cell with the details of the event.
In my test environment, there is no problem, but I imagine with thousands of records, it might become a problem. Here is what I'm doing:
public partial class calendar : System.Web.UI.Page
{
CalendarEventList allevents = CalendarEventManager.GetListAll();
//Page_Load, etc...
protected void calmaincalendar_DayRender(object sender, DayRenderEventArgs e)
{
foreach (CalendarEvent x in allevents)
{
if (e.Day.Date == x.EventDate)
{
//logic to extract the data from x, and modify the e.Cell to display that data
}
}
}
}
My questions are:
1) I'm putting the events as a page level variable, so that the database only needs to be called once, and not on every single DayRender. Good, bad? Potential issues? This page is purely to read the Calendar Events, not edit or delete.
2) Looping through the "allevents" list on every DayRender, is that potentially dangerous/resource intensive? Does the Calendar control call DayRender for just the month that's being displayed? If so, meaning only 30 or 31 calls at a time, it might be fine. But if it does something like calls DayRender for the entire year, or the previous and next months as well...Anyway, hopefully you get my point. Advice on this would be appreciated.
3) Is there anything that I could be doing better? I realize I only posted the bare skeleton, but if anyone has any similar experience, or pitfalls they wished they had realized earlier, that advice would be appreciated.
As I was typing up the questions I realized that when I get the CalendarEventList at the page level, I could immediately loop through it and cut out everything except for the last 3 and future 3 months or something. Problem there is, somehow keeping track of it so that when the user goes back 4 months, I'd have to realize this and call the database again. I have a vague feeling that the answer is somewhere in that direction, but still not sure.
As you talk about records I guess you have an underlying database. If so, I will suggest to create a query for that, so you can do this:
foreach (CalendarEvent x in CalendarEventManager.GetForDate(e.Day.Date))
{
//logic to extract the data from x, and modify the e.Cell to display that data
}
This way, you will retrieve just the needed records.

Can I pass a .net Object via querystring?

I stucked at a condition , where i need to share values between the pages. I want to share value from Codebehind via little or no javascript. I already have a question here on SO , but using JS. Still did'nt got any result so another approach i am asking.
So I want to know can i pass any .net object in query string. SO that i can unbox it on other end conveniently.
Update
Or is there any JavaScript approach, by passing it to windows modal dialog. or something like that.
What I am doing
What i was doing is that on my parent page load. I am extracting the properties from my class that has values fetched from db. and put it in a Session["mySession"]. Some thing like this.
Session["mySession"] = myClass.myStatus which is List<int>;
Now on one my event that checkbox click event from client side, i am opening a popup. and on its page load, extracting the list and filling the checkbox list on the child page.
Now from here user can modify its selection and close this page. Close is done via a button called save , on which i am iterating through the checked items and again sending it in Session["mySession"].
But the problem is here , when ever i again click on radio button to view the updated values , it displays the previous one. That is , If my total count of list is 3 from the db, and after modification it is 1. After reopening it still displays 3 instead of 1.
Yes, you could but you would have to serialize that value so that it could be encoded as a string. I think a much better approach would be to put the object in session rather than on the URL.
I would so something like this.
var stringNumbers = intNumbers.Select(i => i.ToString()).ToArray();
var qsValue = string.Join(",", stringNumbers);
Request.Redirect("Page.aspx?numbers=" + sqValue);
Keep in mind that if there are too many numbers the query string is not the best option. Also remember that anyone can see the query string so if this data needs to be secure do not use the query string. Keep in mind the suggestions of other posters.
Note
If you are using .NET 4 you can simplify the above code:
var qsValue = string.Join(",", intNumbers);
Make the object serializable and store it in an out-of-process session.
All pages on your web application will then be able to access the object.
you could serialize it and make it printable but you shouldn't
really, you shouldn't
The specification does not dictate a minimum or maximum URL length, but implementation varies by browser and version. For example, Internet Explorer does not support URLs that have more than 2083 characters.[6][7] There is no limit on the number of parameters in a URL; only the raw (as opposed to URL encoded) character length of the URL matters. Web servers may also impose limits on the length of the query string, depending on how the URL and query string is stored. If the URL is too long, the web server fails with the 414 Request-URI Too Long HTTP status code.
I would probably use a cookie to store the object.

OutputCache behaving strangely

protected void Page_Load(object sender, EventArgs e)
{Label1.Text = DateTime.Now.ToString();}
If, after I compiled this newly created application, browser B1 is
first to request Cache.aspx, then the time displayed on received page
is 10.16:20.
If, after say 10 seconds, I refresh B1’s Cache.aspx( by clicking a
button to cause a postback), then time 10.16.30 will show up, and on
all subsequent postbacks 10.16.30 will always be displayed ( until 1
hour elapses ).
If few minutes after B1 first requested Cache.aspx, browser B2 also
requests the same page, then upon receiving the page for the first
time, B2’s Cache.aspx will display time 10.16.20, and on all
subsequent B2’s postbacks time 10.16:30 will be displayed instead.
A) So it appears that Asp.net generates two cached versions of
Cache.aspx ( thus code is executed twice ), one cached page is given
to browsers that request Cache.aspx for the first time, and second
cached page is given to browsers which only refresh their Cache.aspx?
B) Since my book never mentioned such a behavior, I’m wondering if you
get the same behavior or is my Net environment again “unique”?
C) Creating two copies of same page ( thus executing the code twice )
seems like a waste of time, so why does Asp.Net operate like that –
there must be some reasoning/benefits behind it?
thank you
A complete guess, but presumably the cache is isolated by the overall request (query string args, etc)... even though the "none" turns off the key/value pairs, presumably a "GET" is simply still counted as different to a "POST"???
Try using simple links (<a href...> etc) rather than ASP.NET buttons.
if i try
if (ispostback)
begin
response.cache.setcachebility(nocache)
end
then the button click updates the label every time.

How do you get the current image name from an ASP.Net website?

Scenario: You have an ASP.Net webpage that should display the next image in a series of images. If 1.jpg is currently loaded, the refresh should load 2.jpg.
Assuming I would use this code, where do you get the current images name.
string currImage = MainPic.ImageUrl.Replace(".jpg", "");
currImage = currImage.Replace("~/Images/", "");
int num = (Convert.ToInt32(currImage) + 1) % 3;
MainPic.ImageUrl = "~/Images/" + num.ToString() + ".jpg";
The problem with the above code is that the webpage used is the default site with the image set to 1.jpg, so the loaded image is always 2.jpg.
So in the process of loading the page, is it possible to pull the last image used from the pages properties?
int num = 1;
if(Session["ImageNumber"] != null)
{
num = Convert.ToInt32(Session["ImageNumber"]) + 1;
}
Session["ImageNumber"] = num;
You can store data in your page's ViewState dictionary
So in your Page_Load you could write something like...
var lastPicNum = (int)ViewState["lastPic"];
lastPicNum++;
MainPic.ImageUrl = string.Format("~/Images/{0}.jpg", lastPicNum);
ViewState["lastPic"] = lastPicNum;
you should get the idea.
And if you're programming ASP.NET and still does not understands how ViewState and web forms work, you should read this MSDN article
Understanding ViewState from the beginning will help with a lot of ASP.NET gotchas as well.
You'll have to hide the last value in a HiddenField or ViewState or somewhere like that...
If you need to change images to the next in the sequence if you hit the F5 or similar refresh button, then you need to store the last image id or something in a server-side storage, or in a cookie. Use a Session variable or similar.
It depends on how long you want it to persist (remember) the last viewed value. My preferred choice would be the SESSION.
#chakrit
does this really work if refreshing the page?
i thought the viewstate was stored on the page, and had to be sent to the server on a postback, with a refresh that is not happening.
#John ah Sorry I thought that your "refresh" meant postbacks.
In that case, just use a Session variable.
FYI, I suggested you use the ViewState dictionary instead of Session because the variable is used inside only that single page, so it shouldn't be using session-wide variable, that's bad practice.

Categories

Resources