"SelectedIndexChanged" not firing after "Items.Clear()" in ListBox
Asked Answered
F

4

9

For a ListBox (With Selection mode set to One), I wish to track whether there's a selected item or none selected. To do so, I subscribed a method to SelectedIndexChanged and checked if the SelectedIndex is -1 or not. However, I noticed that the event doesn't fire after calling Items.Clear(), even though SelectedIndex changes to -1 (if it wasn't already -1).

Why doesn't it fire? I know I can work around this by assigning -1 to SelectedIndex before clearing the list. But is there a better way?

Here's a simple code to replicate this:

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);
        }
    }
}

Edit: I am considering making a custom list by making a class that inherits from ListBox, or by making a user control. However I'm not sure how to approach this. Any ideas on hiding/overriding the clear method using either inheritance/userControl? Would it require hiding/overriding other methods as well or is there a way to avoid this?

Fincher answered 30/3, 2011 at 20:39 Comment(5)
Suggestion: set SelectedIndex to -1 AFTER the call to Clear(). You never know when this method might fail.Testudinal
A better workaround might be to just call the handler directly. But like your current workaround, that still requires extra code every time the list is cleared.Lockup
You might want to consider why you need to respond to Clear() changing the index. The event exists because the code needs to be executed in response to an occurrence out of your control (i.e. the user changing their selection.) Calling clear is an explicit act on your part, so whatever code needs to be executed can just be called right then.Ooze
@Adi: Setting it after won't raise the event (it is already -1 after Clear() so it doesn't change). Am I sensing sarcasm? @Ben Voigt - Yes, that's the problem. I want to call Clear() without requiring an extra code to make it work right. @Ooze - That'll still give me refractoring problems. Wherever I'll want to call Clear(), I'll to remeber to add a workaround to make the event fire. That doesn't feel right.Fincher
No sarcasm from my side. And you are right about the event not being raised after Clear().Testudinal
O
7

Looking at the code in Reflector, the Clear() method on Items just resets the .Net object's internal object list (and does not, as you noticed, fire OnSelectedIndexChanged).

The SelectedIndex property returns -1 because the logic in the property's getter dictates that -1 should be returned if there are no items in the internal list.

Ooze answered 30/3, 2011 at 20:48 Comment(2)
But isn't it sensible the it should fire the event? Is this by design? Or a bug?Fincher
Closest answer. Accepted. However, if anyone can think of a good workaround that can "hide" or "override" Clear so as to make sure the event is fired, I'd be happy to hear about it.Fincher
T
3

Clear() only clears the internal collection of the control. Clear() won't fire the SelectedIndexChanged event because that event will only be raised by changing the CurrentlySelectedIndex. Try using lst.ClearSelected() instead. Calling this method is equivalent to setting the SelectedIndex property to negative one (-1). You can use this method to quickly unselect all items in the list. Alternatively you can try calling Items.Clear() and follow it with a call to ListBox.RefreshItems

Thing answered 30/3, 2011 at 20:51 Comment(5)
This will set the selectedIndex to -1, but it will not remove the items from the list.Digit
as Prescott said it will not remove the items. And I want the items removed.Fincher
That would require an additional operation to clear the data context of the control. You'll need to additionally call Items.Clear()Thing
I can see the point now, you meant using ClearSelected instead of setting the indext to -1 dierctly and then calling Clear. However, I still don't think that Clear should behave like that. Also, I don't see the how you suggest I call ListBox.RefreshItems, it is protected.Fincher
Yeah, you are correct on RefreshItems. I overlooked that it was marked as protected. Apologies.Thing
F
1

probably a hackish solution but this is what i thought of:

class myListBox
    {
        public ListBox myList;

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

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

    }

than you can call this like this in your main form:

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

maybe that could solve your problem.

Fatal answered 12/4, 2011 at 21:53 Comment(2)
Thank you for your answer and sorry for my late response. Do note that SelectedIndex needs to be -1 ("none selected") and not 0 ("first item selected"). Also, you can safely remove the if statement. As for the general proposed solution, I find it neat to group the method that resets & clears the ListBox with the ListBox object itself as you did. However, I'm concerned that anyone using this class might forget of ListClear and will use myList.Clear (especially since s/he need to use all other inner methods of myList). if only there was a way to "lock" myList.CLear() here...Fincher
You might want to make myListBox extend ListBox. That way you keep full functionality of ListBox, can use it everywhere that expects a ListBox class. Sadly that does not solve the confusion between Items.Clear and a custom listClear method. You should also raise SelectedIndexChanged event in the custom clear method, because I think that event does not fire when setting the SelectedIndex property.Wrath
O
0

This is my way, it's compatible with existed code.

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;
        }
    }

}
Odilia answered 14/3, 2014 at 3:5 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.