探讨C#.NET下DropDownList的一个有趣的bug及其解决办法

摘要:

本文就C#.Net 环境下Web开发中经常使用的DropDownList控件的SelectedIndex属性进行了详细的探讨,发现了这一属性在使用中存在的问题,并经过测试,提出了回避和解决的办法。

关键词:

DropDownList,SelectedIndex, 跟踪调试, C#.NET

Probe Into A Bug of DropDownList in C#.NET and the Resolvent

Abstract:

This article have discussed the attribute-SelectedIndex of DropDownList Control,which is frequently used in the development environment of C#.Net. By tracing the program in detail,find out the reason of this bug . Give out the scheme of how to avoid of and resolvent it.

Keywords:

DropDownList, SelectedIndex, Debug, Trace into, C#.NET

1. 引言

信息和网络的发展,使基于Web应用的系统越来越普及, VS.Net无疑是开发Web应用的系统的最合适的工具之一。但我们在长期的开发实践中发现,C#.Net下DropDownList控件在使用过程中会遇到一些问题,它的SelectedIndex属性存在一个读写缺陷,这个问题也一直困扰着其他的开发人员。因此,本文专门对DropDownList做了详细的测试,来探求问题所在和解决办法。

2. DropDownList控件介绍

DropDownList是C#.Net 控件面板Web Form下的一个控件,它的命名空间是System.Web.UI.WebControls.DropDownList。它是一个允许用户从下拉列表中选择一项的控件,通过在 DropDownList 控件的开始和结束标记之间为每个项放置一个 ListItem对象,可以指定希望显示在 DropDownList 控件中的项,也支持数据绑定。DropDownList的功能决定了它在日常开发中的实用性,在数据输入控件中其使用率仅次于TextBox。通过预先设定或动态数据绑定将其填入可供用户选择的数据,既方便了用户操作,增强软件的易用性,又能有效的规范数据输入,成为软件开发人员最常选择的控件之一。

3. 关于SelectedIndex的有趣的问题

在长期的使用过程中我们发现,当在程序中动态将DropDownList列表中的某项选定,

或指定SelectedIndex为某一值时,会出现意想不到的错误。而使用断点跟踪调试方法或将SelectedIndex值读取到某个变量进行测试,却难以找到问题所在。

3.1 发现问题

假设有如下简单代码

private void Page_Load(object sender, System.EventArgs e)

{

if (!IsPostBack)

{ //初始化DropDownList下拉列表

Init_FillList();

}

}

private void btnOK_Click(object sender, System.EventArgs e)

{

string strID=txtContinentID.Text.Trim();

//选择指定项

listContinent.Items.FindByValue(strID).Selected=true;

Response.Write("OK!");

}

#region初始化下拉列表方法

private void Init_FillList()

{    //定义ListItem对象

ListItem item;

//清空列表

listContinent.Items.Clear();

//写入列表

listContinent.Items.Add(" ");

item=new ListItem("亚洲","Asia");

listContinent.Items.Add(item);

item=new ListItem("欧洲","Euro");

listContinent.Items.Add(item);

item=new ListItem("美洲","Amer");

listContinent.Items.Add(item);

}

#endregion

把它放到一个简单web页面中直接运行,在输入框中输入大洲编号Asia,Euro ,Amer中的任一个,点击btnOK按钮键,看似没有任何问题的代码,报出了如下VS.Net著名的错误黄页:(记为:错误A )

DropDownList 不能有多个项被选定。

说明: 执行当前 Web 请求期间,出现未处理的异常。请检查堆栈跟踪信息,以了解有关该错误以及代码中导致错误的出处的详细信息。异常详细信息: System.Web.HttpException: DropDownList 不能有多个项被选定。

通过认真核查代码并查询联机帮助,发现DropDownList的使用符合相关 说明文档的使用方法,没有任何问题。 为了跟踪查找错误的原因,在btnOK_Click()事件下的所有代码外围加 try…catch保护进行调试,单步执行,发现一直执行 到Response.Write("OK!")句,程序都没有跳出,继续向下,此时该事件已经 执行完了,没有错误,应该显示出正常的web页面,就在这时,上面的错误 黄页又出现了。调试无法找到错误所在,如何才能解决这个问题, 难道是开发工具的原因,于是想到以下办法. 3.2 问题暂时解决 不能有多个项被选定,可能是因为DropDownList在选择新项之前不能自动去除原来 的选择,即,不能有效的对已添入数据的列表进行初始化。于是在每次PostBack后 将DropDownList的数据重新绑定刷新恢复到系统自己规定的默认值,然后再 进行新的项的选择,将Page_Load()事件下的代码做如下调整

private void Page_Load(object sender, System.EventArgs e)

{

//去掉 if (!IsPostBack)每次都重写数据

Init_FillList();

} 此时再运行程序,不再出现错误A,运行正常。 但是web应用不同于局域网内系统的应用,它对程序执行效率要求更高, 要尽量减少对服务器的访问。如果一个页面在每次刷新时都要重新 访问服务器初始化数据地话,会严重增加服务器的负担。一旦数据量大 或访问的终端增多,将会使页面显示变的非常慢,客户无法忍受。 需要继续寻求其他的解决办法。 3.3 有趣的bug 由于过去曾经长期从事Delphi下的应用系统的开发,对Combox控件的 使用非常熟悉,由于他们的功能基本相同,推断其使用方法应该也是 有些相通的,于是对txtOK_Click()进行修改,得txtOK2_Click()事件:

private void txtOK2_Click(object sender, System.EventArgs e)

{

string strID=txtContinentID.Text.Trim();

this.listContinent.SelectedIndex=-1;//新加行

listContinent.Items.FindByValue(strID).Selected=true;

Response.Write("OK!");

} 运行程序,果然在加上IsPostBack判断的情况下,程序仍能正常运行。 然而这与msdn联机帮助对DropDownList的使用说明是不符的。 相关的属性说明:“DropDownList.SelectedIndex 属性, DropDownList控件中的选定项的索引。默认值为 0,该值选择列表中的 第一项。备注 使用 SelectedIndex 属性以编程方式指定或确定 DropDownList控件中的选定项的索引。DropDownList 控件中总是选择一项。 无法在列表中同时取消选择所有项。注意 DropDownList 控件中的项的 索引从零开始”。有趣的是不符合使用规定的程序没有报任何错误, 反而使程序运行正常。 为了查看SelectedIndex在运行时的实际值是0还是1或其他的值,再次跟踪 调试,此时发现了一个有趣的bug。把断点设置到 this.listContinent.SelectedIndex=-1行,当程序运行到这里时将鼠标移到 SelectedIndex的位置,查看它的值,(或者通过开发环境下边的变量 查看器查看),发现此时的值是0,继续向下运行,错误A又出现了。 而同样是调试状态,单步执行代码,只是不进行查看SelectedIndex的操作 (通过变量查看器看也不可以),直到跟踪完毕,程序运行也没有问题。 很明显,这是C#.Net的一个bug。 3.4 换一种取值方式 既然不能在调试时通过系统的返回值提示查看变量值,只能变通一下, 通过自己定义变量来获取SelectedIndex的值。于是对txtOK2_Click() 进行修改,得txtOK3_Click()事件:

private void btnOK3_Click(object sender, System.EventArgs e)

{

//新加行 调试后知 i=0

int i= listContinent.SelectedIndex;

string strID=txtContinentID.Text.Trim();

this.listContinent.SelectedIndex=-1;

//新加行 调试后知 j=0

int j=this.listContinent.SelectedIndex;

listContinent.Items.FindByValue(strID).Selected=true;

Response.Write("OK!");

}

运行程序,真正的问题出现了,不管在debug状态还是非调试状态,都是一样的“DropDownList 不能有多个项被选定”错误。这说明SelectedIndex的值根本不能进行查看或读取,这也进一步证明C#.Net中对SelectedIndex的读取实现代码有问题,存在不安全的判断。

另外,经过此时的调试观察i和j的返回值是一样的结果,这个结果也和系统规定的SelectedIndex的默认值为 0一致。这证明了this.listContinent.SelectedIndex=-1这行代码在txtOK2_Click()中是没有起作用没有用途的,然而加上该行代码却能解决问题,使程序正常运行。

3.5问题根源

通过反编译工具和.NET源码的帮助,找到了C#.Net中关于DropDownList的源码实现,发现了这个问题存在的根源。以下是C#.Net中DropDownList的SelectedIndex属性源码实现:

[WebCategory("Behavior"),DesignerSerializationVisibility( DesignerSerializationVisibility.Hidden),DefaultValue(0),WebSysDescription( "DropDownList_SelectedIndex")] public override int SelectedIndex {       get        {             int num1 = base.SelectedIndex;             if ((num1 < 0) && (this.Items.Count > 0))             {                   this.Items[0].Selected = true;                    num1 = 0;              }             return num1;        }       set        {             base.SelectedIndex = value;        } }

这段源码实现表明,在取SelectedIndex时自动进行了判断,只要有数据那么Selected的值就肯定大于等于0,所以我们在查看时发现设置成-1是无效的,它会自动改为0。另外它还做了另外一部操作this.Items[0].Selected = true,这个也就是直接导致Exception产生的原因(开发者只是想看看SelectedIndex它就把Item[0]的Selected值给改了...),所以在调试程序时要注意回避这个问题,我们只能通过修改代码使程序运行正常,而无法改变 VS.NET的源码实现。

图一是程序测试界面,btnOK,btnOK2,btnOK3和列表数据绑定代码的实现已在上面给出。

4. 结束语

经调试,在初始设置SelectedIndex=0的情况下同样存在“错误A”的问题。而且若将3.3中SelectedIndex=-1改为SelectedIndex=0,此中情况程序不调试运行也会出现“错误A”。

在系统对效率要求不高,数据量小的情况下可以采用3.2的方法来回避这个问题,即每次加载页面重新初始化DropDownList列表。也可采取3.3中将SelectedIndex设为-1的方法来改进这一问题,但此时不要对SelectedIndex=-1行进行单行调试。两种方法在工程交付运行时都不会有任何因为SelectedIndex而引起的程序错误。

该文所有测试在Microsoft .NET Framework 1.1, C# .NET 2003 version 7.1,IE6.0 环境下编写调试。

参考资料

[1].[美] G.Andrew Duthie 著,《ASP.NET程序设计》[M],清华大学出版社,2002.7

[2].林煌章,《ASP.NET程式设计》[M],清华大学出版社,2001.6

补充:

上述问题利用List的ClearSelection()方法便可以解决。
我遇到了同样问题但是,以上所有方法试过之后还是不行。
我是每次绑定数据后添加了一个新行,
于是我想到ListItem与DropDownList的关系是不是和DataRow与DataTable的关系相似呢?
在下面网址找到了答案
http://gamtindev.spaces.live.com/blog/cns!E6E0862AFE090F4B!126.entry?wa=wsignin1.0

"不能在 DropDownList 中选择多个项。"其解决办法及补充的更多相关文章

  1. 不能在DropDownList 中选择多个项

    在绑定DropDownList时如果出现多次绑定,会出错以下错误: “不能在DropDownList 中选择多个项” 经了解,只需要在选中值是清空选择即可:xxDropDownList.ClearSe ...

  2. .net中不能在DropDownList中选中多个项的解决方法

    页面中放有多个DropDownList,点击修改时候,需要根据值来设置两个DropDownList的选中项,当值为空时则需要选中默认值. 页面报错:不能在DropDownList中选中多个项. 直接粘 ...

  3. “不能在dropdownlist中选择多个项

    DropDownList.ClearSelection(); DropDownList.SelectedItem.Text = "value值";

  4. Delphi for iOS开发指南(6):在iOS应用程序中使用ComboBox组件来从列表中选择某一项

    http://blog.csdn.net/delphiteacher/article/details/8924110 Delphi for iOS开发指南(6):在iOS应用程序中使用ComboBox ...

  5. Ubuntu 14.04 LTS 火狐浏览器中,鼠标选择文字被删除的解决办法

    这篇文章主要介绍了Ubuntu 火狐浏览器中,鼠标选择文字被删除的解决办法,需要的朋友可以参考下在终端中输入命令: ibus-setup将 “在应用程序窗口中启用内嵌编辑模式“ 选项取消

  6. 关于hasNextInt判断后无限循环输出else项的解决办法

    话不多说,上来就是干! import java.util.Scanner; public class Test_hasNextInt { /** * @param args */ public sta ...

  7. Excel在任务栏中只显示一个窗口的解决办法

     Excel在任务栏中只显示一个窗口的解决办法  以前朋友遇到过这个问题,这次自己又遇到了,习惯了以前的那种在任务栏中显示全部窗口,方便用Alt+Tab键进行切换. 如果同时打开许多Excel工作簿, ...

  8. 把myeclipse中的web项目导入eclipse中不能编程web项目的解决办法

    title: 把myeclipse中的web项目导入eclipse中不能编程web项目的解决办法 tags: grammar_cjkRuby: true --- 右键单击项目,properties-- ...

  9. IIS关于“ 配置错误 不能在此路径中使用此配置节”的解决办法

    IIS关于“ 配置错误 不能在此路径中使用此配置节”的解决办法 原文链接:http://www.cnblogs.com/200325074/p/3679316.html 今天刚安装好IIS8.5, 我 ...

随机推荐

  1. 常用shell 命令整理 一 进程 cpu

    1.查看内存从大到小排列 ps -e -o "%C : %p : %z : %a"|sort -k5 -nr 分析: -e 显示进程 -o 按用户自定义格式显示 %C cpu %p ...

  2. eclipse常用快捷键

    1. ctrl+shift+r:打开资源 这可能是所有快捷键组合中最省时间的了.这组快捷键可以让你打开你的工作区中任何一个文件,而你只需要按下文件名或mask名中的前几个字母,比如applic*.xm ...

  3. node.js下使用RSA加密事例(windows)

    1.安装openss 直接下载window下的安装包 http://houjixin.blog.163.com/blog/static/3562841020144143494875/ 以我发博文现在的 ...

  4. C#微信公众号开发系列教程二(新手接入指南)

    http://www.cnblogs.com/zskbll/p/4093954.html 此系列前面已经更新了两篇博文了,都是微信开发的前期准备工作,现在切入正题,本篇讲解新手接入的步骤与方法,大神可 ...

  5. Oracle:试图访问正在使用的事务临时表

    处理步骤为 1.找到表ID select * from dba_objects where object_name like 'TPT_RPWORPA1_QRY' 2.通过表ID查找正在使用的事务 s ...

  6. TJpgDec使用说明

    TJpgDec模块应用说明 [TOC] 怎么使用 首先,你应该构建和运行如下所示示例程序.这是一个典型的使用TJpgDec模块,它有助于调试和缩小问题. 解码会话分为两个阶段.第一阶段是分析JPEG图 ...

  7. Latex中插入C语言代码

    Latex是一个文本排版的语言,能排版出各种我们想要的效果.而且用代码排版的优点是易于修改板式,因此在文本内容的排版时,Latex应用十分广泛. 当我们需要在Latex中插入代码时,就需要用到 \us ...

  8. 序列化,反序列化和transient关键字

    一.序列化和反序列化的概念 序列化:指把java对象转换为字节序列的过程. 反序列化:指把字节序列恢复为java对象的过程. 对象的序列化主要有两种用途: 1) 把对象的字节序列保存到硬盘上,通常存放 ...

  9. python 旋转数组

    #!/usr/bin/env python3 #-*-encoding:utf-8-*- l = [] u = [] q = 5 xx=[[col for col in range(q)] for r ...

  10. Ruby混合类型

    当一个类拥有可以从多个父类继承的特点,类应该显示多重继承. Ruby没有直接支持多继承,但Ruby模块有另一个精彩使用.他们几乎消除多重继承的需要,提供了一个工厂,称为混入. 混合类型给一个精彩的控制 ...