代码改变世界

ListItem 的 Attributes 在页面回发(PostBack)之间并不会保持视图状态?

2007-05-13 05:27  晓风残月  阅读(...)  评论(... 编辑 收藏
偶然之间发现 ListItem 的 Attributes 在页面回发(PostBack)之间并不会保持视图状态,测试如下:
protected void Page_Load(object sender, EventArgs e)
    
{
        
if (!Page.IsPostBack) {
            ListItem item 
= new ListItem("hi""0");
            item.Attributes[
"xvalue"= "x";
            ListBox1.Items.Add(item);
            item 
= new ListItem("hello""1");
            item.Attributes[
"xvalue"= "y";
            ListBox1.Items.Add(item);
        }

        Response.Write(
"attributes:");
        Response.Write(
"<br>");
        
foreach (ListItem item in ListBox1.Items) {
            IEnumerator iterator 
= item.Attributes.Keys.GetEnumerator();
            
while (iterator.MoveNext()) {
                
string key = iterator.Current.ToString();
                Response.Write(key 
+ "=" + item.Attributes[key]);
                Response.Write(
"<br>");
            }

        }

    }
首次加载呈现结果:
<select size="4" name="ListBox1" id="ListBox1">
    <option value="0" xvalue="x">hi</option>
    <option value="1" xvalue="y">hello</option>

</select>
但是,Postback 之后 attributes就丢失了。
DASM一下 ListItem :
.method assembly hidebysig instance object 
        SaveViewState() cil managed
{
  
// Code size       89 (0x59)
  .maxstack  4
  .locals init (
string V_0,
           
string V_1)
  IL_0000:  ldnull
  IL_0001:  stloc.
0
  IL_0002:  ldnull
  IL_0003:  stloc.
1
  IL_0004:  ldarg.
0
  IL_0005:  ldfld      
bool System.Web.UI.WebControls.ListItem::textisdirty
  IL_000a:  brfalse.s  IL_0013
  IL_000c:  ldarg.
0
  IL_000d:  call       instance 
string System.Web.UI.WebControls.ListItem::get_Text()
  IL_0012:  stloc.
0
  IL_0013:  ldarg.
0
  IL_0014:  ldfld      
bool System.Web.UI.WebControls.ListItem::valueisdirty
  IL_0019:  brfalse.s  IL_0022
  IL_001b:  ldarg.
0
  IL_001c:  call       instance 
string System.Web.UI.WebControls.ListItem::get_Value()
  IL_0021:  stloc.
1
  IL_0022:  ldarg.
0
  IL_0023:  ldfld      
bool System.Web.UI.WebControls.ListItem::enabledisdirty
  IL_0028:  brfalse.s  IL_003d
  IL_002a:  ldloc.
0
  IL_002b:  ldloc.
1
  IL_002c:  ldarg.
0
  IL_002d:  call       instance 
bool System.Web.UI.WebControls.ListItem::get_Enabled()
  IL_0032:  box        [mscorlib]System.Boolean
  IL_0037:  newobj     instance 
void System.Web.UI.Triplet::.ctor(object,
                                                                  
object,
                                                                  
object)
  IL_003c:  ret
  IL_003d:  ldarg.
0
  IL_003e:  ldfld      
bool System.Web.UI.WebControls.ListItem::valueisdirty
  IL_0043:  brfalse.s  IL_004d
  IL_0045:  ldloc.
0
  IL_0046:  ldloc.
1
  IL_0047:  newobj     instance 
void System.Web.UI.Pair::.ctor(object,
                                                               
object)
  IL_004c:  ret
  IL_004d:  ldarg.
0
  IL_004e:  ldfld      
bool System.Web.UI.WebControls.ListItem::textisdirty
  IL_0053:  brfalse.s  IL_0057
  IL_0055:  ldloc.
0
  IL_0056:  ret
  IL_0057:  ldnull
  IL_0058:  ret
}
 // end of method ListItem::SaveViewState


SaveViewState确实没有保存Attributes,只是持久化了Text Value Enabled(这个还是按需)三个属性,
其实,ListItem.Attributes 内部都只是简单的如此实现,并没有像常见的有单独一个私有StateBag _attrState 与 AttributeCollection _attrCol出现,只是如果 _atrrCol如果为null,直接传了局部的StateBag 给AttributeCollection构造函数:
.method public hidebysig specialname instance class System.Web.UI.AttributeCollection 
        get_Attributes() cil managed
{
  
// Code size       32 (0x20)
  .maxstack  8
  IL_0000:  ldarg.
0
  IL_0001:  ldfld      
class System.Web.UI.AttributeCollection System.Web.UI.WebControls.ListItem::_attributes
  IL_0006:  brtrue.s   IL_0019
  IL_0008:  ldarg.
0
  IL_0009:  ldc.i4.
1
  IL_000a:  newobj     instance 
void System.Web.UI.StateBag::.ctor(bool)
  IL_000f:  newobj     instance 
void System.Web.UI.AttributeCollection::.ctor(class System.Web.UI.StateBag)
  IL_0014:  stfld      
class System.Web.UI.AttributeCollection System.Web.UI.WebControls.ListItem::_attributes
  IL_0019:  ldarg.
0
  IL_001a:  ldfld      
class System.Web.UI.AttributeCollection System.Web.UI.WebControls.ListItem::_attributes
  IL_001f:  ret
}
 // end of method ListItem::get_Attributes

补记:
1。其实在 asp.net 1.x 设置ListItem.Attribtues,ListItem 并不会呈现这些 Attributes,已经被视为bug,虽然 2.0 做了改进,依然有问题。参见: ListControl Items and Attributes,
2。之前,为了给Checkbox设置一个客户端可用的 value ,偶尝试自定义了一个 带Value属性的扩展CheckBox控件,因为对于 value 这个Attribute,CheckBox内部好像对 ListItem 实现了强制性过滤。
3。早上baidu了一下,发现很多网友有类似的疑问,并提供了多种解决方案,
如何使CheckBoxList的Attributes属性生效(修改微软的一个bug).
非常有趣的是这个方案是通过重载Render方法,并将HtmlTextWriter输出文本,然后搜索匹配插入需要的 attribute
4。之前转载的一篇文章解决了这个问题:
[转]ListControl Items, Attributes, and ViewState
值得注意的是,这里  Scott Mitchell  采取的策略是分别扩展 DropDownList、CheckBoxlist等,而不是直接扩展 ListItem,减轻了工作量,详细见文章。
PS:其实很早都知道这个问题了,竟然直到现在才得以明白,惭愧