ListItems attributes in a DropDownList are lost on postback?
Solution 1
I had the same problem and wanted to contribute this resource where the author created an inherited ListItem Consumer to persist attributes to ViewState. Hopefully it will save someone the time I wasted until I stumbled on it.
protected override object SaveViewState()
{
// create object array for Item count + 1
object[] allStates = new object[this.Items.Count + 1];
// the +1 is to hold the base info
object baseState = base.SaveViewState();
allStates[0] = baseState;
Int32 i = 1;
// now loop through and save each Style attribute for the List
foreach (ListItem li in this.Items)
{
Int32 j = 0;
string[][] attributes = new string[li.Attributes.Count][];
foreach (string attribute in li.Attributes.Keys)
{
attributes[j++] = new string[] {attribute, li.Attributes[attribute]};
}
allStates[i++] = attributes;
}
return allStates;
}
protected override void LoadViewState(object savedState)
{
if (savedState != null)
{
object[] myState = (object[])savedState;
// restore base first
if (myState[0] != null)
base.LoadViewState(myState[0]);
Int32 i = 1;
foreach (ListItem li in this.Items)
{
// loop through and restore each style attribute
foreach (string[] attribute in (string[][])myState[i++])
{
li.Attributes[attribute[0]] = attribute[1];
}
}
}
}
Solution 2
Thanks, Laramie. Just what I was looking for. It keeps the attributes perfectly.
To expand, below is a class file I created using Laramie's code to create a dropdownlist in VS2008. Create the class in the App_Code folder. After you create the class, use this line on the aspx page to register it:
<%@ Register TagPrefix="aspNewControls" Namespace="NewControls"%>
You can then put the control on your webform with this
<aspNewControls:NewDropDownList ID="ddlWhatever" runat="server">
</aspNewControls:NewDropDownList>
Ok, here's the class...
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Security.Permissions;
using System.Linq;
using System.Text;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace NewControls
{
[DefaultProperty("Text")]
[ToolboxData("<{0}:ServerControl1 runat=server></{0}:ServerControl1>")]
public class NewDropDownList : DropDownList
{
[Bindable(true)]
[Category("Appearance")]
[DefaultValue("")]
[Localizable(true)]
protected override object SaveViewState()
{
// create object array for Item count + 1
object[] allStates = new object[this.Items.Count + 1];
// the +1 is to hold the base info
object baseState = base.SaveViewState();
allStates[0] = baseState;
Int32 i = 1;
// now loop through and save each Style attribute for the List
foreach (ListItem li in this.Items)
{
Int32 j = 0;
string[][] attributes = new string[li.Attributes.Count][];
foreach (string attribute in li.Attributes.Keys)
{
attributes[j++] = new string[] { attribute, li.Attributes[attribute] };
}
allStates[i++] = attributes;
}
return allStates;
}
protected override void LoadViewState(object savedState)
{
if (savedState != null)
{
object[] myState = (object[])savedState;
// restore base first
if (myState[0] != null)
base.LoadViewState(myState[0]);
Int32 i = 1;
foreach (ListItem li in this.Items)
{
// loop through and restore each style attribute
foreach (string[] attribute in (string[][])myState[i++])
{
li.Attributes[attribute[0]] = attribute[1];
}
}
}
}
}
}
Solution 3
Simple solution is to add the tooltip attributes in the pre-render
event of the dropdown. Any changes to the state should be done at pre-render
event.
sample code :
protected void drpBrand_PreRender(object sender, EventArgs e)
{
foreach (ListItem _listItem in drpBrand.Items)
{
_listItem.Attributes.Add("title", _listItem.Text);
}
drpBrand.Attributes.Add("onmouseover", "this.title=this.options[this.selectedIndex].title");
}
Solution 4
If you only want to load the listitems on the first load of the page then you will need to enable ViewState so that the control can serialize its state there and reload it when the page posts back.
There are several places where ViewState can be enabled - check the <pages/>
node in the web.config and also in the <%@ page %>
directive at the top of the aspx file itself for the EnableViewState
property. This setting will need to be true
for ViewState to work.
If you don't want to use ViewState, simply remove the if (!IsPostBack) { ... }
from around the code that adds the ListItems
and the items will be recreated on each postback.
Edit: I apologize - I misread your question. You are correct that the attributes do no survive postback as they are not serialized in ViewState. You must re-add those attributes on each postback.
Solution 5
One simple solution- Call your drop down loading function on the click event where you request for post back.
David Hodgson
Updated on July 09, 2022Comments
-
David Hodgson almost 2 years
A coworker showed me this:
He has a DropDownList and a button on a web page. Here's the code behind:
protected void Page_Load(object sender, EventArgs e) { if (!IsPostBack) { ListItem item = new ListItem("1"); item.Attributes.Add("title", "A"); ListItem item2 = new ListItem("2"); item2.Attributes.Add("title", "B"); DropDownList1.Items.AddRange(new[] {item, item2}); string s = DropDownList1.Items[0].Attributes["title"]; } } protected void Button1_Click(object sender, EventArgs e) { DropDownList1.Visible = !DropDownList1.Visible; }
On the page load, the items' tooltips are showing, but on the first postback, the attributes are lost. Why is this the case, and are there any workarounds?
-
Ted over 10 yearswhy so cryptic? if this is meant to inherit from a ListItem then it doesn't work
-
Markus Szumovski over 10 yearsYou have to inherit a class from DropDownList and then use this, just as gleapman explained below ;)
-
Markus Szumovski over 10 yearsCould be that you'll have to add the Assembly to the Reference-Tag, even if it's in the same Assembly... I think it depends if it's a Web Application Project or a Website. This would, for a Web Application named "MyWebApplication", then read: <%@ Register Assembly="MyWebApplication" TagPrefix="aspNewControls" Namespace="NewControls"%>
-
m3div0 over 9 yearsI tried your sollution, but if I use your inherrited control, it is somehow inaccessible in the code behind. I mean if I try
ddlWhatever.Items
it throws null exception from theddlWhatever
Any idea why? -
Admin over 9 yearsThe solution involves creating a new control that I don't like. There is a way to do this without any subclassing.
-
Gabriel GM about 9 years@david : It doesn't work if you create a
UserControl
and try to inherit theDropDownList
. -
Patrick almost 8 yearsDon't forget to store the dropdown.SelectedIndex before you reload the dropdown so you can restore the user's selection afterward.
-
nZeus almost 8 yearsWith this solution you do requests to the database on every post-back. It's better to use ViewState.
-
rdans over 7 yearsused this successfully but needed to make one bug fix to get it to work right. In the two nested loops within the LoadViewState I moved the i increment to within the first loop but before the second loop and I also initialised i to 0 before the first loop
-
abney317 over 6 yearsWorked great for me for ListBox. Now I can use custom attributes like data-data to properly render out my controls through jQuery plugins like selectize on postback
-
Andrew Morton about 6 years@MPaul As it is generally considered impolite here to alter someone else's code, would you like to make the correction that rdans pointed out or would you like me to do it for you?
-
Leo over 4 yearsThanks, this answer solve the problem but are there any update for better solution?