ListBox SelectedValueChanged/SelectedIndexChanged 数据源更改时未触发

ListBox SelectedValueChanged/SelectedIndexChanged not firing when data source changes

我需要根据当前选定的值跟踪列表框上的选定项到update/disable其他控件。

这是重现问题的代码:

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 和三个按钮:addButtonremoveButtonlogSelectionButton

如果您按 addButton(从空列表开始),然后再按 removeButton,最后再按 addButton,则 SelectedValueChangedSelectedIndexChanged 都不会触发在最后一次 addButton 按下时,即使您在最后一次 addButton 按下之前和之后按下 logSelectionButton,您也会看到 SelectedIndex 和 [=25] 的值=] 分别从 -1 变为 0,从 null 变为 "Item 1","Item 1" 看起来在列表框中被选中。

这会导致我需要根据所选项目更新的任何其他控件保持禁用状态,直到用户手动选择列表框中的项目,即使第一个项目已被选中。

我想不出任何解决方法。也许还订阅了我的 BindingList 的 ListChanged 事件来查看列表是否为空,但是我不知道列表框中的项目是否会在我的事件处理程序触发之前或之后更新,这将导致其他问题。

您似乎在 ListControl 数据绑定时 PositionChanged 事件的内部处理中发现了一个错误(如果您在 VS 中打开异常,当第一项是添加到空列表)。

由于 ListControl 派生 类 像 ListBoxComboBox 等在数据绑定模式下将它们的选择与 Position 属性 同步在 BindingManagerBase 中,可靠的解决方法(基本上是更通用的抽象解决方案)是处理基础数据源绑定管理器的 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();
    }
}