繁体   English   中英

为什么在修改所选项目时,ListBox会触发SelectedIndexChanged事件?

[英]Why does the SelectedIndexChanged event fire in a ListBox when the selected item is modified?

我们获得了一个Windows窗体应用程序,它是从Microsoft Visual Studio的模板(PasteBin 1 2 3 4上的设计器代码)创建的,带有默认的ListBox exampleListBox和Button exampleButton

我们使用1到10之间的数字填充ListBox。

for (int i = 0; i < 10; ++i)
{
    exampleListBox.Items.Add(i);
}

然后我们添加两个事件处理程序。

exampleListBox_SelectedIndexChanged将简单地向控制台写入当前选定的索引。

private void exampleListBox_SelectedIndexChanged(object sender, EventArgs e)
{
    Console.WriteLine(exampleListBox.SelectedIndex);
}

exampleButton_Click会将当前所选索引的项目设置为自身。 如此有效,这应该绝对没有改变。

private void exampleButton_Click(object sender, EventArgs e)
{
    exampleListBox.Items[exampleListBox.SelectedIndex] = exampleListBox.Items[exampleListBox.SelectedIndex];
}

单击按钮时,我预计不会发生任何事情。 然而,这种情况并非如此。 即使SelectedIndex未更改,单击该按钮也会触发exampleListBox_SelectedIndexChanged事件。

例如,如果我单击exampleListBox中索引2处的项目,则exampleListBox.SelectedIndex将变为2.如果我按exampleButton ,则exampleListBox.SelectedIndex仍为2.但是,然后触发exampleListBox_SelectedIndexChanged事件。

为什么即使所选索引未更改,事件也会触发?

此外,还有什么可以防止这种行为发生?

当您修改ListBox中的项目(或实际上,ListBox的关联ObjectCollection中的项目)时,基础代码实际上删除并重新创建项目。 然后它选择这个新添加的项目。 因此,所选择的索引被改变,并且相应的事件被引发。

我没有特别引人注目的解释为什么控制行为这样。 它既可以用于编程方便,也可以只是WinForms原始版本中的一个错误,后续版本必须维护该行为是出于向后兼容的原因。 此外, 即使未修改项目 ,后续版本也必须保持相同的行为。 这是您正在观察的反直觉行为。

而且,令人遗憾的是,它没有记录 -unless你明白为什么它正在发生,然后你知道SelectedIndex属性实际上得到改变在幕后,没有您的知识。

Quantic留下了一条注释,指向参考源中代码的相关部分

internal void SetItemInternal(int index, object value) {
    if (value == null) {
        throw new ArgumentNullException("value");
    }

    if (index < 0 || index >= InnerArray.GetCount(0)) {
        throw new ArgumentOutOfRangeException("index", SR.GetString(SR.InvalidArgument, "index", (index).ToString(CultureInfo.CurrentCulture)));
    }

    owner.UpdateMaxItemWidth(InnerArray.GetItem(index, 0), true);
    InnerArray.SetItem(index, value);

    // If the native control has been created, and the display text of the new list item object
    // is different to the current text in the native list item, recreate the native list item...
    if (owner.IsHandleCreated) {
        bool selected = (owner.SelectedIndex == index);
        if (String.Compare(this.owner.GetItemText(value), this.owner.NativeGetItemText(index), true, CultureInfo.CurrentCulture) != 0) {
            owner.NativeRemoveAt(index);
            owner.SelectedItems.SetSelected(index, false);
            owner.NativeInsert(index, value);
            owner.UpdateMaxItemWidth(value, false);
            if (selected) {
                owner.SelectedIndex = index;
            }
        }
        else {
            // NEW - FOR COMPATIBILITY REASONS
            // Minimum compatibility fix for VSWhidbey 377287
            if (selected) {
                owner.OnSelectedIndexChanged(EventArgs.Empty); //will fire selectedvaluechanged
            }
        }
    }
    owner.UpdateHorizontalExtent();
}

在这里,您可以看到,在初始运行时错误检查之后,它更新ListBox的最大项目宽度,设置内部数组中的指定项目,然后检查是否已创建本机ListBox控件。 实际上,所有WinForms控件都是本机Win32控件的包装器,ListBox也不例外。 在您的示例中,肯定已创建本机控件,因为它在窗体上可见,因此if (owner.IsHandleCreated)测试的计算结果为true。 然后它会比较项目的文本,看它们是否相同:

  • 如果它们不同,则会删除原始项目,删除选择,添加新项目,并在选择原始项目时选择它。 这会导致SelectedIndexChanged事件被引发。

  • 如果它们相同且当前选择了该项,则如注释所示,“出于兼容性原因”,将手动引发SelectedIndexChanged事件。

我们刚刚分析的这个SetItemInternal方法从SetItemInternal调用ListBox.ObjectCollection对象的默认属性:

public virtual object this[int index] {
    get {
        if (index < 0 || index >= InnerArray.GetCount(0)) {
            throw new ArgumentOutOfRangeException("index", SR.GetString(SR.InvalidArgument, "index", (index).ToString(CultureInfo.CurrentCulture)));
        }

        return InnerArray.GetItem(index, 0);
    }
    set {
        owner.CheckNoDataSource();
        SetItemInternal(index, value);
    }
}

这是exampleButton_Click事件处理程序中的代码exampleButton_Click

无法阻止此行为发生。 您必须通过在SelectedIndexChanged事件处理程序方法中编写自己的代码来找到解决它的方法。 您可以考虑从内置ListBox类派生自定义控件类,重写OnSelectedIndexChanged方法,并将解决方法放在此处。 这个派生类将为您提供一个方便的位置来存储状态跟踪信息(作为成员变量),它将允许您使用修改后的ListBox控件作为整个项目的替代品,而无需修改SelectedIndexChanged事件处理程序到处。

但老实说,这不应该是一个大问题或任何你甚至需要解决的问题。 您对SelectedIndexChanged事件的处理应该是微不足道的 - 只需更新表单上的某些状态,如依赖控件。 如果没有发生外部可见的变化,它触发的变化基本上就是无操作。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM