ListView not calling ItemCreated after postback - asp.net

I have an asp:ListView where I want to filter the items based on a property of the contained DataItem
I want to set the item invisible unless the specified property (FieldName) has the value I'm looking for.
protected override void ItemCreated(object sender, ListViewItemEventArgs e)
{
if (DataBinder.Eval(e.Item.DataItem, FieldName).ToString() != FieldValue)
{
e.Item.Visible = SetVisible;
}
}
But on postback, the DataItem is null, and I am unable to restore the previous state. My expectation was, that the Listview would be able to keep the visible property in Viewstate, but it doesn't. I could call Listview.Databind, but the listview is used to select items with a checkbox, and this selection is lost on databind.
How can I restore the items visibility to its first state after databind?

Put your logic in the OnItemDataBound event instead. The data is always null because at the time the item is created, it has not yet been bound.

For this scenario, you are checking an additional field. Store that additional field in the DataKeyNames collection, and pull the value from there. That value is stored in viewstate, so it will be available across postbacks.
HTH.

Related

Losing DropDownList items on post back

I have an webforms ASPX page, which has a dynamically added user control. The user control has two DropDownLists, one is in an UpdatePanel as its items depend on the selection in the first DropDownList.
The problem is that if you do not change the value of the first DropDownList the value of the second DropDownList does not get saved. If you do change the value of the first DropDownList then it works fine.
This is how it works briefly...
The first time the page loads, the previous values are set. This happens in the main ASPX page Page_Load event where the user control is dynamically added and the initial values are set through a property of the user control. The property sets the selected value of the first DropDownList, triggers the SelectedIndexChanged event which populates items in the second DropDownList with choices based on the first DropDownList selection, and then selects the previous value of the second DropDownList.
Then in the main ASPX page, the user control is dynamically added again on post back in the PreLoad event.
Viewstate is fully enabled throughout.
I have debugged and on post back the second DropDownList has no Items. Even during the UpdatePanel partial post back the items collection is empty. However, if the first DropDownList is changed then the Items collection is correct - it's only if the second DropDownList is last populated on the initial load that the problem happens.
Here's the relevant parts of the code...
Any help greatly appreciated.
ASPX page:
protected void Page_PreLoad(object sender, EventArgs e)
{
if (Page.IsPostBack)
{
PopulateTemplateOptions();
}
}
protected void PopulateTemplateOptions(bool init = false)
{
//this is where the control is dynamically added
//If not post back then initial values are set by passing values to
//user control's TemplateOptions property
}
User control:
public string TemplateOptions
{
set
{
//this is where the initial values are set
}
}
protected void ddlEnquirySubjectDefault_SelectedIndexChanged(object sender, EventArgs e)
{
//this is where items are added to the second drop down list based on the selection in the first one.
}
(These are the only parts that are relevant, but there's another 7,000 lines of code I didn't include.)
Here are two of my ideas:
1) This line might be wrong in your code: if (Page.IsPostBack)
It must be if (!Page.IsPostBack), although I never used Page_PreLoad event so I am not sure if it is right.
2) Try to do what you want in Page_Init event instead. I use it on my projects, without problem.

Best way to allow postback on a gridview without viewstate

My gridview is basically a hit list of results from which the user selects one, and I want to get a postback saying which one he has selected. There is no need for the grid contents to survive the postback round-trip because the hit list disappears as soon as he has selected an item.
I don't want to use viewstate because the hit list is likely to be large. I don't want to databind from the database in PageLoad to repopulate the grid because the search may take a while.
what I'm thinking at the moment is that I can put some javascript on the 'select' link to store the ID of the selected item in a hidden field and then call __doPostBack
this still seems a bit clunky. can you think of a cleaner way?
If the postback should be triggered when the user clicks the anywhere in the row, use the ItemDataBound event to attach a client onclick handler:
protected void GridView1_ItemDataBound(object sender, GridViewRowEventArgs e)
{
var dataItem = e.Item as GridViewRow;
if (dataItem != null)
{
dataItem.Attributes["onclick"] = string.Format("__doPostBack(this, '{0}')", e.Row.RowIndex);
}
}
Is the ID of the selected item something sensitive that should not be seen by the user?
If it is not, you could just make your select button a link to the next page, with the ID as a querystring variable. Clicking would just move to the next page, no postback required.

Why is DataItem always null in the Page_Load handler?

I've created an ASP.net web page with an ASP Repeater. I bind the ASP Repeater with an IOrderedEnumerable<int> DataSource.
I need to access the Repeater DataItems at inside the Page_Load event handler. However, when I try to get the value of repeater.Items[x].DataItem, I always get null. There should always be an integer value here.
In spite of this, the page otherwise renders fine. Why can't I access the DataItem property of my RepeaterItems inside the Page_Load event handler?
Your Repeater doesn't databind until later in the page lifecycle. If you want to reference Repeater.Items[i].DataItem in a Page.Load handler, try to early-bind the Repeater first:
repeater.DataBind()
Its only available during databinding.
As everyone else has said, it's only available during databinding. You will need to wire up the OnItemDataBound event of the repeater. In its event handler, you can do this:
protected void Repeater_ItemDataBound(object sender, RepeaterItemEventArgs e)
{
switch (e.Item.ItemType)
{
case ListItemType.Item:
case ListItemType.AlternatingItem:
WhateverType Item = e.Item.DataItem as WhateverType;
break;
}
}
The data items only exist after the databinding process has taken place. The only thing that is preserved across postbacks in the repeater are the control properties that are serialized in viewstate. If you need the data in those items, you can do like pseudocoder said and databind earlier or if that is not possible you could write a utility function that takes a repeater data item and extracts it from the controls in your repeater (assuming you stored all the information you need in those controls somehow)

Repeater Control finding the object that raises the event

Inside repeater control HeaderTemplate i have some LinkButtons and Checkbox
I want to findout the object (Linkbutton or checkbox) that raises the event.
protected void Repeater1_ItemCommand(object source, RepeaterCommandEventArgs e)
{
switch(e.CommandSource)
{
case LinkButton:some work here;
case CheckBox :some work here;
}
}
When i write such code i received error as
A switch expression or case label must be a bool,
char, string, integral, enum, or corresponding nullable type
How to achieve this?
As the error messages states you could use switch with bool, char, string, integral, enum or corresponding nullable type. In your case you want to compare types. This could be achieved with an if statement:
if (e.CommandSource is LinkButton)
{
}
else if (e.CommandSource is CheckBox)
{
}
First, I pretty sure the checkbox won't ever fire the Repeater_ItemCommand event, as it is not a Button (or something that inheriets from Button) as only buttons create the ItemCommand event inside a Repeater. You can set the CheckBox's AutoPostBack property to true and handle it's OnClick event, though you'll have to be careful to be able to figure out which CheckBox fired the event b/c all the CheckBoxes will have the same event handler in your code behind files and they won't have some of the nice information inside the EventArgs that events raised by the Repeater will have.
In addition, checking the control type in the ItemCommand event handler seems both inefficent and limiting. As your code would break if there were ever mulltiple controls of the same type in the Repeater row that needed different processing. For controls that will actually raise the ItemCommand event, setting either the CommandName or the CommandArgument property of button will allow you to uniquely identify the control that raised the event, without taking the performance hit of the type check, plus it will be more maintainable.
Use this code within the ItemCommand event handler:
switch(e.CommandName)
{
case "LinkButtonCommandName1":
.......
break;
case "LinkButtonCommandName2":
.......
break;
}

Should a DropDownList within a CompositeControl remember selected item?

Given the following
public class MyControl : CompositeControl
{
private DropDownList myList;
protected override void CreateChildControls()
{
base.CreateChildControls();
myList = new DropDownList();
myList.AutoPostBack = true;
this.Controls.Add(myList);
if (!Page.IsPostBack)
{
myList.DataSource = MyBLL.SomeCollectionOfItems;
myList.DataBind();
}
}
}
I find that the items in the list persist properly, but when a different control is rendered and then this one is rendered again, the last selected item is not persisted. (The first item in the list is always selected instead)
Should the last selected item be persisted in ViewState automatically, or am I expecting too much?
I think this is a hidden ViewState issue. You create and bind a control in CreateChildControls. You should only create the control at this place. Move the binding code to the classes load event and use EnsureChildControls.
Here is the solution which is best recommended. It lies in understandng the Page Life Cycle correctly!! Postback Controls like Drop Down List restore their posted state (the selected item of a Drop Down List posted). It forgets its selected value because you are rebinding it in Page_Load event, which is after the Drop Down List has been loaded with posted value (because View State is loaded after Page_Init event and before Page_Load event). And in this rebinding in Page_Load event, the Drop Down List forgets its restored selected item. The best solution is to perform the Data Binding in the Page_Init event instead of Page_Load event.
Do something like the below...
Suppose Drop Down List name is lstStates.
protected void Page_Init(object sender, EventArgs e)
{
lstStates.DataSource = QueryDatabase(); //Just an example.
lstStates.DataTextField = "StateName";
lstStates.DataValueField = "StateCode";
lstStates.DataBind();
}
ASP.NET loads control's View State after Page_Init event and before Page_Load event, so Drop Down List's selectedIndex will not be affected, and you will get desired results magically!!

Resources