none
The ListBox.SelectionChanged event is not launched when expected.

    Question

  • The SelectionChanged event is launched before the item is actually selected. It should be called SelectionChanging to reflect what it's actually happening. The problem this is causing is that in that event I'm changing the ItemSource, so by the time the control actually sets the selectedItem (after the evenr was fired), the item is not present in its dictionary and fails. This is the exception.

    e.ExceptionObject
    {System.Collections.Generic.KeyNotFoundException: The given key was not present in the dictionary.
       at System.ThrowHelper.ThrowKeyNotFoundException()
       at System.Collections.Generic.Dictionary`2.get_Item(TKey key)
       at System.Windows.Controls.ListBox.SetSelectedItem(Object item)
       at System.Windows.Controls.ListBox.NotifyListItemClicked(ListBoxItem listBoxItem)
       at System.Windows.Controls.ListBoxItem.OnMouseLeftButtonDown(MouseButtonEventArgs e)
       at System.Windows.Controls.ListBoxItem.<.ctor>b__0(Object sender, MouseButtonEventArgs e)
       at System.Windows.CoreInvokeHandler.InvokeEventHandler(Int32 typeIndex, Delegate handlerDelegate, Object sender, Object args)
       at MS.Internal.JoltHelper.FireEvent(IntPtr unmanagedObj, IntPtr unmanagedObjArgs, Int32 argsTypeIndex, String eventName)}
        [System.Collections.Generic.KeyNotFoundException]: {System.Collections.Generic.KeyNotFoundException: The given key was not present in the dictionary.
       at System.ThrowHelper.ThrowKeyNotFoundException()
       at System.Collections.Generic.Dictionary`2.get_Item(TKey key)
       at System.Windows.Controls.ListBox.SetSelectedItem(Object item)
       at System.Windows.Controls.ListBox.NotifyListItemClicked(ListBoxItem listBoxItem)
       at System.Windows.Controls.ListBoxItem.OnMouseLeftButtonDown(MouseButtonEventArgs e)
       at System.Windows.Controls.ListBoxItem.<.ctor>b__0(Object sender, MouseButtonEventArgs e)
       at System.Windows.CoreInvokeHandler.InvokeEventHandler(Int32 typeIndex, Delegate handlerDelegate, Object sender, Object args)
       at MS.Internal.JoltHelper.FireEvent(IntPtr unmanagedObj, IntPtr unmanagedObjArgs, Int32 argsTypeIndex, String eventName)}
        Data: {System.Collections.ListDictionaryInternal}
        InnerException: null
        Message: "The given key was not present in the dictionary."
        StackTrace: "   at System.ThrowHelper.ThrowKeyNotFoundException()\r\n   at System.Collections.Generic.Dictionary`2.get_Item(TKey key)\r\n   at System.Windows.Controls.ListBox.SetSelectedItem(Object item)\r\n   at System.Windows.Controls.ListBox.NotifyListItemClicked(ListBoxItem listBoxItem)\r\n   at System.Windows.Controls.ListBoxItem.OnMouseLeftButtonDown(MouseButtonEventArgs e)\r\n   at System.Windows.Controls.ListBoxItem.<.ctor>b__0(Object sender, MouseButtonEventArgs e)\r\n   at System.Windows.CoreInvokeHandler.InvokeEventHandler(Int32 typeIndex, Delegate handlerDelegate, Object sender, Object args)\r\n   at MS.Internal.JoltHelper.FireEvent(IntPtr unmanagedObj, IntPtr unmanagedObjArgs, Int32 argsTypeIndex, String eventName)"

    The problem here is that SetSelectedItem is called afeter NotifyListItemClicked.

    I know a possidble workaround would be to use an ObservableCollection and update the list instead of creating a new one and setting the ItemsSource, but if the Item's are different we would end up with the same problem. Something that DO worked for me is to use the M-V-VM pattern so I really dont do anything on the event (fired by NotifyListItemClicked), but instead in the ViewModel's proterty setter bound to the SelectedItem which gets updated during the SetSelectedItem. 

    The code to reproduce this is really simple
    <ListBox SelectionChanged="ListBox_SelectionChanged" DisplayMemberPath="Name" x:Name="listBox"/>

    public partial class Page : UserControl
        {
            public Page()
            {
                InitializeComponent();
                listBox.ItemsSource = Person.GetPeople();
            }

            private void ListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
            {
                listBox.ItemsSource = Person.GetPeople();
            }
        }

        public class Person
        {
            public string Name { get; set; }
            public static IEnumerable GetPeople()
            {
                return new Person[]
                           {
                               new Person(){ Name= "Miguel"},
                               new Person(){ Name= "Carina"},
                               new Person(){ Name= "Jeff"},
                               new Person(){ Name= "Delay"},
                           };
            }
        }

    I know you said to try this on WPF and it doesnt have this problem.

     

    Monday, June 30, 2008 12:00 PM

Answers

  • Actually... I am using ObservableCollection, and I ran into the same issue.

     It occurs because in the SelectChanged event I fire my own in events.. at some point an item (the item that was selected) is removed from the collection... afterwards.. and suddenly, it jumps into application_unhandled exception.

    If I do not remove the item, it all works fine.  Unfortunately I need this to work.  This DOES appear to be an issue with setting the selected item - the error is System.Collections.Generic.KeyNotFoundException.

    Here is the stack:

    -  base {System.Collections.Generic.KeyNotFoundException: The given key was not present in the dictionary.
       at System.ThrowHelper.ThrowKeyNotFoundException()
       at System.Collections.Generic.Dictionary`2.get_Item(TKey key)
       at System.Windows.Controls.ListBox.SetSelectedItem(Object item)
       at System.Windows.Controls.ListBox.NotifyListItemClicked(ListBoxItem listBoxItem)
       at System.Windows.Controls.ListBoxItem.OnMouseLeftButtonDown(MouseButtonEventArgs e)
       at System.Windows.Controls.ListBoxItem.<.ctor>b__0(Object sender, MouseButtonEventArgs e)
       at System.Windows.CoreInvokeHandler.InvokeEventHandler(Int32 typeIndex, Delegate handlerDelegate, Object sender, Object args)
       at MS.Internal.JoltHelper.FireEvent(IntPtr unmanagedObj, IntPtr unmanagedObjArgs, Int32 argsTypeIndex, String eventName)} System.Exception {System.Collections.Generic.KeyNotFoundException}

    Seems to me it IS trying to set the selected item after notifying that it was selected.  Booo.

    Thursday, July 03, 2008 6:32 PM

All replies

  • Miguel,

    I agree the scenario you have isn't working, but I think your diagnosis may not be entirely accurate. Try adding the following to the top of your SelectionChanged handler:
        System.Diagnostics.Debug.WriteLine(listBox.SelectedItem);
    And add the following to Person:
        public override string ToString() { return Name; }
    When you run the app and click on an item, you should see that the correct name is output - indicating that SelectionChanged is called after the selection is changed as expected. Furthermore, you can verify this by looking at ListBox.ProcessSelectionPropertyChange - the event is raised *after* processing the selection.

    What's confusing here is why Silverlight seems to be re-triggering ListBoxItem.OnMouseLeftButtonDown when you re-set ItemsSource. It's that action that causes the stack trace you've pasted and that action doesn't seem to make sense the way I'm understanding things right now. But I'm glad you've found a workaround! :)

    Monday, June 30, 2008 1:53 PM
  • David,

    Thanks for the quick response. I was actually wrong. I knew the SelectedItem was actually selected, but I tought the call to SetSelectedItem was to update the visuals or something, but I checked the stack of the Exception and the call to the even handler and it's actually a second call to SetSelectedItem. I've not had a chance to look at the Source of the Controls.

    I hope this could be corrected for following releases.

    The workaroun is not that simple, having to use Databinding instead of eventHandler is less than ideal for some escenarios.

    Miguel

    Monday, June 30, 2008 3:11 PM
  • Actually... I am using ObservableCollection, and I ran into the same issue.

     It occurs because in the SelectChanged event I fire my own in events.. at some point an item (the item that was selected) is removed from the collection... afterwards.. and suddenly, it jumps into application_unhandled exception.

    If I do not remove the item, it all works fine.  Unfortunately I need this to work.  This DOES appear to be an issue with setting the selected item - the error is System.Collections.Generic.KeyNotFoundException.

    Here is the stack:

    -  base {System.Collections.Generic.KeyNotFoundException: The given key was not present in the dictionary.
       at System.ThrowHelper.ThrowKeyNotFoundException()
       at System.Collections.Generic.Dictionary`2.get_Item(TKey key)
       at System.Windows.Controls.ListBox.SetSelectedItem(Object item)
       at System.Windows.Controls.ListBox.NotifyListItemClicked(ListBoxItem listBoxItem)
       at System.Windows.Controls.ListBoxItem.OnMouseLeftButtonDown(MouseButtonEventArgs e)
       at System.Windows.Controls.ListBoxItem.<.ctor>b__0(Object sender, MouseButtonEventArgs e)
       at System.Windows.CoreInvokeHandler.InvokeEventHandler(Int32 typeIndex, Delegate handlerDelegate, Object sender, Object args)
       at MS.Internal.JoltHelper.FireEvent(IntPtr unmanagedObj, IntPtr unmanagedObjArgs, Int32 argsTypeIndex, String eventName)} System.Exception {System.Collections.Generic.KeyNotFoundException}

    Seems to me it IS trying to set the selected item after notifying that it was selected.  Booo.

    Thursday, July 03, 2008 6:32 PM
  • Leaf,

    Really the problem is that the event is thrown again after changing the itemSource. If you're firing your own events, it sounds like you're using MVP or similar, try using M-V-VM or simply bound the SelectedItem property to a Property of your model, that way instead of firing one event that probably your presenter would do and in turn modify the Model, your Model can do something on the setter. That's what have worked for us.  

    Thursday, July 03, 2008 7:41 PM