I am trying to understand the lifecycle that is taking place here. I have a asp list view where I get item ids and write them to a list like so.
protected void ShareWith_OnItemBound(object sender, ListViewItemEventArgs e)
{
if (!IsPostBack)
{
if (e.Item.ItemType == ListViewItemType.DataItem)
{
ListViewDataItem currentItemId = (ListViewDataItem)e.Item;
System.Web.UI.WebControls.DataKey currentDataKey = this.lvShareWithPanel.DataKeys[currentItemId.DataItemIndex];
int FriendId = Convert.ToInt32(currentDataKey["SharedUserId"]);
CurrentList.Add(FriendId);
}
}
}
Where my list is defined outside the method
private List<int> CurrentList = new List<int>();
after, the user adds some new items to the list then click on an asp button to continue. I am running a comparison of the current list versus the new one but observing in debug after the button click I find that my list "CurrentList" is now empty. Why might the list, which is outside any method, be effected?
Thanks for the help understanding
The list has no state value. So you need to store the list in either ViewState, Session state or otherwise.
Every ASP.NET page loses its values between page loads and only gets them back from state or if you enter them every time. Most controls store values in ViewState, which is page specific. This link should help.
ASP.NET is stateless, therefore the data is lost during Postback. You need to keep track of your CurrentList manually e.g. in the Session/ViewState.
public List<int> CurrentList
{
get
{
return (List<int>)Session["CurrentList"] ?? new List<int>();
}
set
{
Session["CurrentList"] = value;
}
}
All page's objects will be disposed at the end of the page's life-cycle. So you need to create and fill your list on every postback (or store it in Session what i wouldn't recommend).
You could use page's PreRender event to ensure that all events are already triggered:
protected override void OnPreRender(EventArgs e)
{
// why do you need a list as field variable at all? I assume a local variable is fine
List<int> CurrentList = new List<int>();
foreach(var currentItemId in lvShareWithPanel.Items)
{
System.Web.UI.WebControls.DataKey currentDataKey = lvShareWithPanel.DataKeys[currentItemId.DataItemIndex];
int FriendId = Convert.ToInt32(currentDataKey["SharedUserId"]);
CurrentList.Add(FriendId);
}
// do something with the list
}
Note that you should not make it static as someone commented. That means you would use the same "instance" for every user and every request.
Here you can see all events:
You can store the list into ViewState and in PageLoad event, assign the List stored in ViewState to your class level List. This happens because of the Page Life Cycle where objects are disposed.
Related
I have some LinkedList. I filled it with some DataTable from DataSet. So question is how do I implement button "Next" and "Previous" on Windows Form to navigate my tables?
LinkedListNode<DataTable> lln = new
LinkedListNode<DataTable(ds.Tables("GRAPHICS"))
LinkedList<DataTable> ll = new LinkedList<DataTable>
LinkedListClass.ll.AddFirst(lln)
LinkedListClass.ll.AddLast(ds.Tables("COORDS"))
LinkedListClass.ll.AddLast(ds.Tables("METHODS"))
I expect to press button "Next" get table "COORDS" and press "Previous" button get previous value ( "GRAPHICS" table)
First you'll have to store a current position. So add i.e. a property that stores the current item's table name. Than add two "click" events for your buttons(previous and next) and handle these events. Something like this
private string _currentTableName; // or reference list item directly
// use this pseudo code to create "Previous" method as well
void NextButtonPressed(object sender, EventArgs e)
{
// Get current element using stored name
if(string.IsNullOrEmpty(_currentTableName))
{
_currentTableName = firstElement.Name;
// show first
Display(firstElement);
}
else
{
// get current element
// get next element
_currentTableName = nextElement.Name;
Display(nextElement);
}
}
I have this code in my codebehind:
for (int i = 0; i < linkList.Count; i++)
{
var link = UppercaseFirst(linkList[i]);
var linkButton = new LinkButton
{
Text = link + " > ",
ID = Convert.ToString(i),
CommandArgument = urlList[i]
};
linkButton.Command += new CommandEventHandler(lnkWeb_Click);
bcHolder.Controls.Add(linkButton);
}
and here is the lnkWeb_Click method:
protected void lnkWeb_Click(object sender, CommandEventArgs e)
{
var url = e.CommandArgument.ToString();
//code...
}
This method is not getting triggered when I click on one of those generated linkbuttons.
Anyone have any idea what the problem is?
Tried OnCommand="lnkWeb_Click" in the aspx file and the method got trigged, but not those that I generate by code. They dont even have OnCommand="lnkWeb_Click" attribute.
The problem here is with the control life cycle. If you want to handle events of some control properly - you have to add this control to the page on every page loading process, that is on every postback.
Look what happens in your case:
Initial button is clicked
During the post back your dynamic link buttons are added to the page, event handlers are assigned to them
User clicks on the newly generated link button
During post back these dynamic link buttons are not added to the page again, ASP.NET does not know the origin of a event so it does not call the handler.
To fix this you might need to store in the View State information about link buttons that have to be added (please do not store the controls themselves, that would be a huge overhead). Also pay attention to their IDs - they have to be the same for the same controls.
Update. Some more hints on the View State solution.
Basically you need some indicator that during the page loading you need to create some dynamic link buttons. The very basic way to do it is to store the list of the link button identifiers (or texts, or both) and then during Page_Load check if there is anything stored in View State. For example:
// Property to access the view state data
protected List<string> Links
{
get { return ViewState['links']; }
set { ViewState['links'] = value; }
}
...
protected void Page_Load(object sender, EventArgs e)
{
...
if (this.Links != null && this.Links.Count > 0)
{
// inside this method you create your link buttons and add them to the page
// you actually have this code already
RenderLinkButtons();
}
}
...
// Not sure about what name you have here
protected void InitialButtonHandlerName(object sender, EventArgs e)
{
List<string> linkList = ...; //your variable, guessing a type
// this is exactly the method you use already to add links to the page
// just one more action added to it - store info about these links into View State to use it on later post backs
this.Links = linkList;
RenderLinkButtons();
}
Please use it just a point in right direction - you might have different implementation depending on your requirements and preferences. But I hope concept is clear now.
I have set up the following method when the checkbox list is checked.
protected void chk1_SelectedIndexChanged(object sender, EventArgs e)
{
foreach (ListItem list in chk1.Items)
{
if (list.Selected)
{
string name = list.Value.ToString();
}
}
}
I need to display the checked item from the checkbox list. However, for each iteration the selected attribute always comes false. It never satisfies the condition
if (list.Selected)
{
string name = list.Value.ToString();
}
How do I fix this?
Try something like this
var selectedListItems = chk1.Items.Cast<ListItem>().Where(x => x.Selected);
or in your case
var list = chk1.Items.Cast<ListItem>().Where(x => x.Selected);
now you will have a Collection that you can check / code against
also make sure that this code is being fired and or check if there is a PostBack
you can check this by checking if(!Is.PostBack){ }
My money is on you are re-binding the controls on every postback, instead do this:
if (!Page.IsPostBack)
{
// Only bind controls on initial page and let viewstate remember what the user did
}
I'm new to web forms.
1) My default web form is Default.aspx. It has a couple of combo boxes, and a Button control: all ASP.Net web controls.
2) In the Page_load(), I create a C# object "ScoringInfo ()":
protected void Page_Load(object sender, EventArgs e)
{
scoringInfo = new ScoringInfo();
...
3) ScoringInfo reads some info from a database into member variables, and uses the member variables to fill the combo boxes:
scoringInfo.GetOpenCrossByDate(dt, openCrossInfo);
cbAvailableBowlers.Items.Clear ();
foreach (OpenCrossInfoRec rec in openCrossInfo)
string s =
String.Format(
"Lane {0:00}: {1}",
rec.laneNo, rec.dateTime);
cbAvailableBowlers.Items.Add(s);
...
4) Here are the member variables:
...
protected ScoringInfo scoringInfo;
protected List leagueInfo = new List();
protected List openCrossInfo = new List();
5) When the user presses the button, I want to display a second .aspx page which processes the specific combo box item the user selected. Here is my "OnClick" event handler:
protected void bTest_Click1(object sender, EventArgs e)
{
int idx = cbAvailableBowlers.SelectedIndex;
Session["openCrossLaneUniqueId"] = openCrossInfo[idx].laneUniqueId;
...// THIS FAILS:
// "ARGUMENT OUT OF RANGE" exception;
// "idx" is 0; openCrossInfo[] list is empty...
It doesn't work ... because member variable, "openCrossInfo[]" and combo box property SelectedIndex don't seem to be valid any longer when bTest_Click1 is executed!
How/where do I save the state of the UI for other, subsequent pages in the same session?
The member variables for the page (such as openCrossInfo) will not persist from request to request. The Page object for the .aspx is created again each time a new request comes in. So when the event for bTest_Click fires, it is working with a new copy of the Page object. Your openCrossInfo array has no values because the page object was just freshly created, even though you set it in an earlier request.
If you want save state you will have to use something else such as Session state.
The problem, as Jay Douglass pointed out, is that the member variable "openCrossInfo" from the original page isn't persisted to the new, "postback" page.
The solution was:
create and initialize the objects in the original page ("if !IsPostBack"), save the initialized objects to the Session, then
restore them from the Session for the subsequent page:
protected void Page_Load(object sender, EventArgs e)
{
scoringInfo = new ScoringInfo();
if (!IsPostBack)
{
// 1st time, use current date/time; create new data
leagueInfo = new List<LeagueInfoRec>();
openCrossInfo = new List<OpenCrossInfoRec>();
laneUniqueIds = new List<string>();
updateGui(DateTime.Now);
Session["leagueInfo"] = leagueInfo;
Session["openCrossInfo"] = openCrossInfo;
Session["laneUniqueIds"] = laneUniqueIds;
}
else
{
// Subsequent callbacks: retrieve state
leagueInfo = (List<LeagueInfoRec>)Session["leagueInfo"];
openCrossInfo = (List<OpenCrossInfoRec>)Session["openCrossInfo"];
laneUniqueIds = (List<string>)Session["laneUniqueIds"];
}
}
I am trying to add a user control into a div at runtime. I can add the control no probelem but it overwrites the previous control added.
Basically, I am trying to add passengers to a travel system - the passenger details are in the user control and I don't know in advance how many there will be. I have an add new passenger button which should append the new user control into the div without overwriting the previous passenger.
The code is c#/.net 4.
I have tried to save the control data into viewstate and re add it with the new one but that also doesn't work. Here is a snippet of the code I'm using
foreach (Control uc in p_passengers.Controls) {
Passenger p = uc as Passenger;
if (p != null) {
p.SaveValues();
}
}
however, p.SaveAs() (just writes the control values into ViewState) is never hit.
Im sure its just something stupid but any ideas??
Cheers guys.
Are you re-creating all of your dynamic controls for every postback?
Remember each postback is a new instance of the Page class and any controls you previously created will need to be explicitly re-created.
Update
If you had a list of added items in viewstate, something like this..
private List<string> Items
{
get
{
return ViewState["Items"] = (ViewState["Items"] ?? new List<string>());
}
}
Then in your click handler you could simply add to this list :
private void btn_Click(object sender, EventArgs e)
{
this.Items.Add("Another Item");
}
Then override CreateChildControls
protected overrides CreateChildControls()
{
foreach (string item in this.Items)
{
Passanger p = new Passenger();
p.Something = item;
this.p_passengers.Controls.Add(p);
}
}