在ListBox中执行"Items.Clear()"后,"SelectedIndexChanged"事件未触发

8
对于选择模式设置为单选的列表框(ListBox),我希望跟踪是否有选定的项目或没有选定的项目。为此,我订阅了一个方法到SelectedIndexChanged事件,并检查SelectedIndex是否为-1。但是,我注意到即使SelectedIndex变为-1(如果它不是-1),事件也不会在调用Items.Clear()后触发。
为什么事件不会触发? 我知道我可以通过在清除列表之前将-1分配给SelectedIndex来解决这个问题。但是是否有更好的方法?
以下是一个简单的代码以复制此问题:
using System;
using System.Windows.Forms;

namespace ns
{
    class Program
    {
        static ListBox lst = new ListBox();

        public static void Main()
        {
            lst.SelectedIndexChanged += new EventHandler(lst_SelectedIndexChanged);

            lst.Items.Add(1);

            Console.WriteLine("Setting selected index to 0...");
            lst.SelectedIndex = 0; //event fire here

            Console.WriteLine("(Selected Index == {0})", lst.SelectedIndex);

            Console.WriteLine("Clearing  all items...");
            lst.Items.Clear(); //event *should* fire here?!

            //proof that the selected index has changed
            Console.WriteLine("(Selected Index == {0})", lst.SelectedIndex);
        }

        static void lst_SelectedIndexChanged(object sender, EventArgs e)
        {
            Console.WriteLine("[!] Selected Index Changed:{0}", lst.SelectedIndex);
        }
    }
}

编辑: 我正在考虑通过创建一个继承自ListBox的类或创建一个用户控件来制作自定义列表。然而,我不确定如何处理这个问题。 有没有关于使用继承/用户控件隐藏/覆盖clear方法的想法? 这是否需要隐藏/覆盖其他方法,还是有一种避免这种情况的方法?


建议:在调用 Clear() 方法之后将 SelectedIndex 设置为 -1。你永远不知道这个方法什么时候会失败。 - Adi
一个更好的解决方法可能是直接调用处理程序。但是像您当前的解决方法一样,这仍然需要每次清除列表时编写额外的代码。 - Ben Voigt
你可能想考虑一下为什么需要对Clear()更改索引做出响应。这个事件的存在是因为代码需要在发生无法控制的情况下执行(例如用户更改选择)。调用clear是你主动进行的明确操作,所以需要执行的代码可以立即调用。 - dlev
@Adi:在清除后设置它不会触发事件(Clear()后它已经是-1了,所以不会改变)。我感受到了讽刺的意味?@Ben Voigt - 是的,这就是问题所在。我想调用Clear()而不需要额外的代码来使其正常工作。@dlev - 那仍然会给我重构问题。无论我想在哪里调用Clear(),我都要记得添加一个解决方法来触发事件。这感觉不对。 - Shmuel Valariola
没有我这边的讽刺。你关于Clear()后事件没有被触发是正确的。 - Adi
4个回答

7

在Reflector中查看代码,Items上的Clear()方法只是重置了.Net对象的内部对象列表(并且不会像您注意到的那样触发OnSelectedIndexChanged事件)。

如果内部列表中没有项目,则SelectedIndex属性返回-1,因为属性getter中的逻辑规定应该返回-1。


3
但是触发事件不是很合理吗?这是设计如此吗?还是一个错误? - Shmuel Valariola
1
最接近的答案。如果有人能想到一个好的解决方案来“隐藏”或“覆盖”Clear以确保事件被触发,我将很乐意听取意见。 - Shmuel Valariola

3
Clear()只清除控件的内部集合。Clear()不会触发SelectedIndexChanged事件,因为该事件只会在更改CurrentlySelectedIndex时引发。尝试使用lst.ClearSelected()代替。调用此方法等效于将SelectedIndex属性设置为负一(-1)。您可以使用此方法快速取消选择列表中的所有项目。或者,您可以尝试调用Items.Clear(),然后调用ListBox。RefreshItems

这将把selectedIndex设置为-1,但不会从列表中删除项目。 - Prescott
正如Prescott所说,它不会删除这些项目。而我想要删除这些项目。 - Shmuel Valariola
这将需要额外的操作来清除控件的数据上下文。您还需要另外调用Items.Clear() - Mr. Young
我现在明白了,你的意思是使用ClearSelected而不是直接将索引设置为-1,然后再调用Clear。但是,我仍然认为Clear不应该这样行事。此外,我不知道你建议我如何调用ListBox.RefreshItems,因为它是受保护的。 - Shmuel Valariola
是的,你在RefreshItems上是正确的。我忽略了它被标记为受保护的事实。抱歉。 - Mr. Young

1

可能是一个不太正式的解决方案,但这是我想到的:

class myListBox
    {
        public ListBox myList;

        public myListBox()
        {
            myList = new ListBox();
        }

        public void listClear()
        {
            if (myList.Items.Count > 0)
            {
                myList.SelectedIndex = 0;
            }
            myList.Items.Clear();
        }

    }

然后你可以在你的主窗体中这样调用它:

            myListBox example = new myListBox();
            example.myList.Items.Add("Example");
            example.myList.SelectedIndexChanged += new EventHandler(lst_SelectedIndexChanged);
            this.Controls.Add(example.myList);
            example.listClear();

也许这可以解决你的问题。


感谢您的回答,很抱歉我回复晚了。请注意SelectedIndex需要是-1(“未选择”),而不是0(“第一个项目已选择”)。此外,您可以安全地删除if语句。至于一般提出的解决方案,我发现将重置和清除ListBox的方法与ListBox对象本身分组非常好,就像您所做的那样。然而,我担心使用这个类的任何人可能会忘记ListClear并使用myList.Clear(特别是因为他/她需要使用myList的所有其他内部方法)。如果只有一种方法可以在这里“锁定”myList.CLear()…… - Shmuel Valariola
您可能希望使myListBox扩展ListBox,这样您就可以保留ListBox的全部功能,并且可以在任何期望ListBox类的地方使用它。不幸的是,这并不能解决Items.Clear和自定义的listClear方法之间的混淆。您还应该在自定义清除方法中引发SelectedIndexChanged事件,因为我认为当设置SelectedIndex属性时,该事件不会触发。 - Xilconic

0

这是我的方式,它与现有的代码兼容。

public class DetailsListView : ListView
{
    public new class ListViewItemCollection : ListView.ListViewItemCollection
    {
        private DetailsListView m_owner;
        public ListViewItemCollection(DetailsListView owner)
            : base(owner)
        {
            m_owner = owner;
        }

        public override void Clear()
        {
            base.Clear();
            m_owner.FireChanged();
        }
    }

    private void FireChanged()
    {
        base.OnSelectedIndexChanged(EventArgs.Empty);
    }


    private ListViewItemCollection m_Items;

    public DetailsListView()
    {
        m_Items = new ListViewItemCollection(this);

        View = View.Details;
        GridLines = true;
        HideSelection = false;
        FullRowSelect = true;
    }

    public new ListViewItemCollection Items
    {
        get
        {
            return m_Items;
        }
    }

}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接