我需要跟踪ListBox上的选定项,以根据当前选定的值更新/禁用其他控件。
这是重现该问题的代码:
public partial class Form1 : Form
{
private readonly BindingList<string> List = new BindingList<string>();
public Form1()
{
InitializeComponent();
listBox1.DataSource = List;
listBox1.SelectedValueChanged += (s, e) => System.Diagnostics.Debug.WriteLine("VALUE");
listBox1.SelectedIndexChanged += (s, e) => System.Diagnostics.Debug.WriteLine("INDEX");
addButton.Click += (s, e) => List.Add("Item " + (List.Count + 1));
removeButton.Click += (s, e) => List.RemoveAt(List.Count - 1);
logSelectionButton.Click += (s, e) =>
{
System.Diagnostics.Debug.WriteLine("Selected Index: " + listBox1.SelectedIndex);
System.Diagnostics.Debug.WriteLine("Selected Value: " + listBox1.SelectedValue);
};
}
}
我的窗体有一个列表框listBox1
和三个按钮:addButton
、removeButton
和logSelectionButton
。
如果按addButton
(从空列表开始),然后removeButton
,最后再次addButton
,SelectedValueChanged
和SelectedIndexChanged
都不会在最后一次按addButton
时触发,即使您在最后一次按addButton
之前和之后按logSelectionButton
,您会看到SelectedIndex
和SelectedValue
的值分别从 -1 更改为 0,从null
更改为"第 1 项", 并且"项目 1"在列表框中看起来处于选中状态。
这将导致我需要根据所选项目更新的任何其他控件保持禁用状态,直到用户手动选择列表框中的项,即使已选择第一项也是如此。
我想不出任何解决方法。也许还订阅我的 BindingList 的ListChanged
事件以查看列表是否为空,但我不知道列表框中的项是否会在我的事件处理程序触发之前或之后更新,这会导致其他问题。
似乎您在数据绑定时ListControl
内部处理PositionChanged
事件时发现了一个错误(如果您在 VS 中打开异常,则当第一项添加到空列表中时,您将看到异常)。
由于ListControl
派生类(如数据绑定模式下的ListBox
、ComboBox
等)将它们的选择与BindingManagerBase
的Position
属性同步,因此可靠的解决方法(基本上是一个更通用的抽象解决方案)是处理底层数据源绑定管理器CurrentChanged
事件:
listBox1.BindingContext[List].CurrentChanged += (s, e) =>
System.Diagnostics.Debug.WriteLine("CURRENT");
我找到了一个似乎工作正常的解决方法。由于 ListBox 通过设置SelectedIndex
属性来更新所选索引,并且该属性是虚拟的,因此我可以重写它以跟踪它:
public class ListBoxThatWorks : ListBox
{
private int LatestIndex = -1;
private object LatestValue = null;
public EqualityComparer<object> ValueComparer { get; set; }
public override int SelectedIndex
{
get { return base.SelectedIndex; }
set { SetSelectedIndex(value); }
}
private void NotifyIndexChanged()
{
if (base.SelectedIndex != LatestIndex)
{
LatestIndex = base.SelectedIndex;
base.OnSelectedIndexChanged(EventArgs.Empty);
}
}
private void NotifyValueChanged()
{
if (!(ValueComparer ?? EqualityComparer<object>.Default).Equals(LatestValue, base.SelectedValue))
{
LatestValue = base.SelectedValue;
base.OnSelectedValueChanged(EventArgs.Empty);
}
}
private void SetSelectedIndex(int value)
{
base.SelectedIndex = value;
NotifyIndexChanged();
NotifyValueChanged();
}
}