看起来当你有一个WinForms .NET应用程序和一个ComboBox(设置为"DropDown"样式),并且ComboBox中有多个相同的项目时,会发生奇怪的事情.具体而言,所选项的索引可以更改,而不会触发SelectedIndexChanged事件.
当然,这会引起大规模的混乱和奇怪的,模糊的错误,这就是我最近一直把头发拉出来的原因.
这是一个简单的例子,你可以用来看我在说什么:
创建一个新的.NET WinForms项目(我使用VB.NET,但随意翻译 - 它很简单).
将一个ComboBox,一个按钮和一个TextBox(设置MultiLine = True)拖放到表单上.
使用以下代码加载具有3个相同项目的ComboBox,并在SelectedIndexChanged事件触发时打印一些状态消息,并查看当前选择的索引(通过按钮):
Private Sub ComboBox1_SelectedIndexChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ComboBox1.SelectedIndexChanged TextBox1.Text = TextBox1.Text & vbNewLine & "ComboBox SelectedIndexChanged event fired." & vbNewLine & _ "SelectedIndex is: " & ComboBox1.SelectedIndex End Sub Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load ComboBox1.Items.Add("John Doe") ComboBox1.Items.Add("John Doe") ComboBox1.Items.Add("John Doe") End Sub Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click TextBox1.Text = TextBox1.Text & vbNewLine & _ "Button clicked." & vbNewLine & _ "SelectedIndex is: " & ComboBox1.SelectedIndex End Sub
运行项目并从ComboBox中选择一个项目(例如,中间项目).然后,单击ComboBox的下拉箭头,但不要选择任何内容.单击按钮(默认为Button1)并查看其内容.
除非我忘记了,否则你应该看到以下内容:
ComboBox SelectedIndexChanged event fired. SelectedIndex is: 1 Button clicked. SelectedIndex is: 0
换句话说,SELECTED INDEX已经改变但没有激活SelectedIndexChanged事件!
仅当ComboBox中的项目相同时才会发生这种情况.如果它们不同,则不会发生这种情况.(如果ComboBox的"DropDown"样式设置为"DropDownList",也不会发生这种情况.)
我怀疑这可能是.NET框架本身的一个错误,而不是我可以解决的问题,但是如果其他人对这里做什么(或者我可能做错了什么)有任何想法,请发出声明!我无法解释这种行为或解决它(我希望SelectedIndex保持不变,除非,你知道,你实际上通过选择其他东西来改变它!)
.NET Framework实际上并没有跟踪组合框下拉列表的选定索引; 这是由Windows API在内部处理的.因此,当所选索引通过发送到组合框的窗口句柄的通知消息发生更改时,.NET依赖于Windows API来通知它,以便它可以依次触发SelectedIndexChanged事件.
不幸的是,事实证明,.NET所监视的特定通知消息(CBN_SELCHANGE
确切地说)并未涵盖所选索引可能发生变化的所有可能情况.具体而言,CBN_SELCHANGE
仅当用户单击或使用箭头键选择下拉列表中的项目时,才会通过Windows API发送.但是,在DropDown样式组合框中,打开组合框的操作会导致Windows查看组合框的编辑部分中的文本,搜索项目列表以查找匹配项,以及是否找到匹配项选择匹配项(或第一个匹配项,如果有多个匹配项).这可以更改所选索引,但不会发送CBN_SELCHANGE
通知消息,因此.NET忽略了它已更改的事实,并且未触发SelectedIndexChanged事件.
Windows在DropDown样式组合框中完成所有这些操作,因为用户无需在下拉列表中选择内容; 他们可以打字他们想要什么.因此,每次打开组合框时,它都会假定用户可能已更改了文本,并尝试重新同步列表中的内容(如果可以).
在您的情况下,当您第二次打开组合框时,它将重新同步并选择编辑部分中文本的第一个匹配项,即"John Doe"#0,并将所选索引更改为0 .NET意识到了.
所以它基本上是.NET Framework中的一个错误.遗憾的是,没有完美的解决方法 - 您无法让Windows不进行重新同步,并且在重新同步发生后没有事件会立即触发,您可以在其中获取新选择的索引.(DropDown事件实际上在重新同步发生之前就会触发,因此它不会看到新的索引.)关于你可以做的最好的事情是处理DropDownClosed事件,假设索引可能在那时发生了变化,并采取相应的行动.