Storing an List<int> in viewstate - c#

I have an aspx page which has the following:
A repeater with a linkbutton in each
The link button has a commandargument of an integer value
A user control
The idea is that when the user clicks on the linkbutton the value of the commandarguement is stored in a List. No problem you may think, however I need the value to be stored in an List in the usercontrol, not in the ASPX page. The List needs to be persisted across postbacks, so it also needs to be stored in the viewstate.
So I created a public property in the user control like so:
public List<int> ImageString {
get {
if (this.ViewState["ImageString"] != null) {
return (List<int>)(this.ViewState["ImageString"]);
}
return new List<int>();
}
set { this.ViewState["ImageString"] = value; }
}
And then I was hoping that from my aspx page I could add a line of code to add a value to the list like so:
this.LightBoxControl.ImageString.Add(value);
The problem I'm getting is that the the value is actually never added to the list. The count is always zero.
I'm sure its just that I've set the property up wrong, but I'm not sure how to right it..
Any help would be greatly appreciated.
Thanks
Al

Your getter is wrong. This is the correct variant:
get {
if (this.ViewState["ImageString"] == null) {
this.ViewState["ImageString"] = new List<int>();
}
return (List<int>)(this.ViewState["ImageString"]);
}
Here you first check whether there is something you need in ViewState already, and if there is no, you add it there. Then you return the item from ViewState - it is guaranteed to be there.
Your solution was bad because it did not place new List<int>() into the ViewState

Related

Session won't store attribute on PostBack when CheckBoxList is triggered

My problem is that my List is not being retained during PostBack.
I am saving it into
Page.Session["mine"]
My design page consists of 2 controls( a Label and a CheckBoxList).
Because the number of items will change, I use a CheckBoxList instead of individual CheckBox controls and load each item dynamically in the page_load();
I am unsure of what it is called, either a member, attribute, or property. But I do have a variable that each method of the class are able to call on that holds the location of all selected items on the CheckBoxList.
Private List<int> locCheck = new List<int>();
When the OnSelectedIndexChanged() is triggered, the locCheck is renewed and stored in Session
locCheck = new List<int>();
int num = 0;
foreach(ListItem li in CheckBoxList.Items)
{
if(li.Selected)
locCheck.Add(num);
num++;
}
if(locCheck.Count == 0) //for testing purpose
locCheck.Add(1); //2nd item
Page.Session["mine"]=locCheck;
I want to keep the selected checks on PostBack, but it never happens. In Visual Studio I have no problem. But when I load to server, I am using mono, I get the following message:
Object reference not set to an instance of an object
I have this in page_load
if(Page.IsPostBack)
{
locCheck = new List<int>();
locCheck = (List<int>) Page.Session["mine"];
Label.Text = String.Format("Is PostBack {0}/{1}", locCheck.Count, CheckBoxList.Items.Count);
}
else
{
Label.Text = String.Format("Is Not PostBack 0/{0}", CheckBoxList.Items.Count);
}
I know the problem is with this line because locCheck.Count never changes in Label.Text
locCheck =(List<int>) Page.Session["mine"];
I know I have to attach
if(Page.Session["mine"] != null)
But I don't believe it is currently necessarry. Due to testing purposes, it should always have locCheck with at least one element.
i couldn't find an answer, so i started from scratch. instead of checklists i use panels of checkboxes that turn invisible when not needed.
I am still uncertain about Sessions but I have found something strange.
For example:
Session["mine"] = locCheck; //where lockCheck has 4 elements
there are times that locCheck is set to null sometime between page_load to a button click.
i have found that when this is so, debug back to a point where locCheck is not null and has correct element values. then perform following:
Session["mine"] = locCheck;
and later on, you perform the following code in click event
locCheck = new List<int>();
locCheck = (List<int>) Session["mine"];
//then make your update:
locCheck[3] = 7;
Session["mine"] = locCheck;
for some reason, if i don't do this both Session["mine"] and locCheck become null and data is lost.
try to use this
locCheck = (List<int>) Page.Session["mine"].ToString();
why to use page.session when you can use only session.

how to prevent data losing when selectedNodeChanged event triggered in asp.net Treeview?

Each time when pressing tree node then selectegNodeChanged event triggered and page is reloaded.So, i lost some data stored in Dictionary,ArrayList...How to prevent those data losing?
So, I stored those Dictionary and ArrayList as "static".It is now resolved my problem.
Is it good way to do that?
No. Do not use static. Try to store these in ViewState or Session instead.
You can consider ViewState if it is not a large amount of data.
static will be accessible across Session and is not a good practice.
You can create properties and avoid code duplication like shown below.
public ArrayList TreeNodeDataList
{
set
{
ViewState["TreeNodeDataList"] = value;
}
get
{
if (ViewState["TreeNodeDataList"] == null)
{
return (new ArrayList());
}
else
{
return (ViewState["TreeNodeDataList"] as ArrayList);
}
}
}
Now, when you want to re-assign data, read TreeNodeDataList property. If count of that ArrayList is 0, fetch from DB, else use it.
Hope I am clear enough.

Problem looping through web controls

I've got a web page where I am dynamically creating controls during Page_Load event (this is done so because I do not know how many controls I will need until session is active and certain variables are accessible)
I need to be able to loop through these controls to find Checkbox when a button click is processed. Looping through the Form.Controls does not appear to be sufficient. I would think that Request.Form might work but it does not appear to be accessible in my C# block?
What should code for Request.Form look like? OR
Has anyone done this before with dynamically created controls?
Any insight is appreciated.
Simplified Example from MSDN:
var myControl = FindControl("NameOfControl");
if(myControl != null)
{
//do something
}
else
{
//control not found
}
Hope this helps! ;)
Your controls will be accessible trough the Controls collection of their immediate parent. Unless you add them like Page.Form.Controls.Add (myControl);, you won't find it in Page.Form.Conttrols. If you add them to a place holder, you must find them in thePlaceHolder.Controls.
LinkButton myDynamicLinkButton = new myDynamicLinkButton ();
myDynamicLinkButton.ID = "lnkButton";
myPlaceHolder.Controls.Add (myDynamicLinkButton);
//........
LinkButton otherReferenceToMyLinkButton = myPlaceHolder.FindControl ("lnkButton");
As #David said in his comment, you should probably think about using a Repeater instead. It would probably simplify your case a lot.
Since the controls might be nested in other controls, you need to search recursively. You can use this method to find the control:
public Control FindControlRecursive(Control root, string id)
{
if (root.ID == id)
{
return root;
}
foreach (Control c in root.Controls)
{
Control t = FindControlRecursive(c, id);
if (t != null)
{
return t;
}
}
return null;
}
And you can implement it this way:
CheckBox check = FindControlRecursive(Page.Form, "CheckBox1");
You should have access to Request["xyz"] anywhere in your aspx.cs code. You can either find control as described above and read it's value or do so directly from Request using the Control.UniqueID property. For example if it's a checkbox that's within the repeater then the UniqueID would look like dtgData$ctl02$txtAmount
Thanks for the insight guys. I kind of took the discussion and ran with it and found my solution that worked best for me.
foreach(String chk in Request.Form)
{
if (chk.Contains("chkRemove"))
{
int idxFormat = chk.LastIndexOf("chkRemove");
objectname = chk.Substring(idxFormat);
}
}
Turned out really all I needed was the name. The string contained a number at the end which was needed to determine a position of datatable items. Thanks for the advice!

Find controls in listview item template of the same type

I am working on claim expenses application for the staff where I work. Part of the process contains a listview, part of a new requirement is that if an expense type is mileage the user will not be able to edit the item, only delete and resubmit as part of business rules and UK tax reasons etc.
Anyway, I want to be able to find a control in each item of the listview that has a certain text value.
I thought something like the following but this is not correct and I know why.
Label ExpenseTypeLabel = (Label)Expenses.FindControl("ExpenseTypeLabel");
string ExpenseType = (ExpenseTypeLabel.Text.ToString());
if (ExpenseType == "Mileage")
{
foreach (ListViewDataItem thisItem in Expenses.Items)
{
ImageButton btnEdit = (ImageButton)thisItem.FindControl("btnEdit");
btnEdit.Enabled = false;
}
}
The expenses are based on weekending and as the page loads it throws my excepion as It cannot bind to a particular individual control as there are many ExpenseTypeLabels associated with the expense for the current weekending (which loads first).
What I am trying to accomplish here is to find all ExpenseTypeLabels in both the item template and the alternating item template and disable the edit function of that expense item. FYI incase you're wondering the weekending is the expense, and the children are the individual expense items.
Could one of you lovely people please educate me on the best way to accomplish this?
Thanks
Matt
Binding order, and timing for accessing bound items, is extremely important; this is especially true when you have sub controls that have binding items also.
If you want to affect the the display for these bound controls, you can usually do it from the aspx end.
Create a link from the front end to a function on the server end, then pass it all the necessary parameters:
<asp:listview id='lstExpense'>
...
<asp:button id='btnEdit' enabled='<%#= isEnabled(((Expense)Container.DataItem).ExpenseType) %>' ...
...
<asp:listview>
On the server end, make a public function to return that value:
public boolean IsEnabled(string ExpenseType) {
return ('Mileage' != ExpenseType);
}
Best solution though, is to use jQuery. Not exaggerating, but you can accomplish all of that with something as simple as:
$('.rowClass').each(function() {
if ($(this).find('.expenseTypeClass').val() == 'Mileage'))
$(this).find('.btnEditClass').attr('disabled','disabled');
})
use OnItemDataBound event as follows
OnItemDataBound="Expenses_ItemDataBound"
protected void Expenses_ItemDataBound(object sender, ListViewItemEventArgs e)
{
if (e.Item.ItemType == ListViewItemType.DataItem)
{
Label ExpenseTypeLabel = (Label)e.Item.FindControl("ExpenseTypeLabel");
string ExpenseType = (ExpenseTypeLabel.Text.ToString());
if (ExpenseType == "Mileage")
{
// disable button
}
}
}

Trouble with FindControl and dynamicly created controls

Example code:
var div = new HtmlGenericControl("div");
div.Controls.Add(new Literal() { ID = "litSomeLit" });
var lit = (Literal)div.FindControl("litSomeLit");
Assert.IsNotNull(lit);
This code fails the assert, because lit is null. Debugging shows that div.Controls definitely contains a literal with ID of "litSomeLit." My questions are "Why?" and "Is there any way to get a control of a specific ID without doing a recursive search of div.Controls[] by hand one element at a time?"
The reason I'm doing things this way is that my actual application is not so straightforward- a method I'm writing is given a complex control with several subcontrols in a number of possible configurations. I need to access a specific control several layers down (eg, the control with ID "txtSpecificControl" might be at StartingControl.Controls[0].Controls[2].Controls[1].Controls[3]). Normally I could just do FindControl("txtSpecificControl"), but that does not seem to work when the controls were just dynamically created (as in the above example code).
Near as I can tell, there is no way to do what I'm trying to accomplish without adding the control to the page. If I had to guess, I'd say that FindControl uses the UniqueID property of the control, which generally contains the IDs of all the controls above the current one (eg OuterControlID$LowerControlId$TargetControlID). That would only get generated when the control actually gets added to the page.
Anyway, here's an implementation of recursive depth-first-search FindControl that'll work when the control is not attached to the page yet:
public static Control FindControl(Control parent, string id)
{
foreach (Control control in parent.Controls)
{
if (control.ID == id)
{
return control;
}
var childResult = FindControl(control, id);
if (childResult != null)
{
return childResult;
}
}
return null;
}
Change your code to
var div = new HtmlGenericControl("div");
Page.Controls.Add(div);
div.Controls.Add(new Literal() { ID = "litSomeLit" });
var lit = (Literal)div.FindControl("litSomeLit");
As far as i know FindControl only works when the control is in the visual tree of the page.
When you confirmed that the control was in the Controls collection, did you do that by inspecting the collection directly? FindControl() may not work in this context.
When you debug the test, is the var lit null? If so, you may have to access the member by item index instead of using the FindControl() method.

Categories