列表框选定值已更改/选定索引已更改数据源更改时不触发



我需要跟踪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和三个按钮:addButtonremoveButtonlogSelectionButton

如果按addButton(从空列表开始),然后removeButton,最后再次addButtonSelectedValueChangedSelectedIndexChanged都不会在最后一次按addButton时触发,即使您在最后一次按addButton之前和之后按logSelectionButton,您会看到SelectedIndexSelectedValue的值分别从 -1 更改为 0,从null更改为"第 1 项", 并且"项目 1"在列表框中看起来处于选中状态。

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

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

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

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

最新更新