Search code examples
c#.netwpflistboxselectedvalue

How to handle SelectedValue changing in ListBox correctly (WPF, C#)?


I must handle SelectedValue property changing to refresh some dependent UI elements when a user selects some item in a ListBox. I use SelectedValue because I need to know a concrete value of the item, not an index in the ListBox (the index may be different under variuos circumstances) or something another. My ListBox is binded to a DataTable and SelectedValue contains ID of a row in the table.

The problem is that when I try to get SelectedValue of the ListBox within SelectionChanged event handler, I receive a prior value, not the current! So, after I spent about a hour searching for ways of solving this problem, I noted that it is relatively well-known, but solutions I've seen do not seem to be very clear. Nevertheless, I "compiled" few ideas I have encountered to the next working snippet of code:

((DataRowView)this.LISTBOXNAME.SelectedItem)["ID"]

where this is my current window and SelectedItem may be casted to DataRowView as the ListBox is binded to a DataTable as I've mentioned before. This line of code gives a proper value of the selected ListBoxItem.

But anyway, I still want to know how to make SelectedValue get proper value directly? Is it possible? Or, maybe, must I use another event (not SelectionChanged)? And if it is impossible, can somebody explain me the way of thinking of kind guys from Microsoft and why that property has such a feature?


Solution

  • SelectedValueChanged event works fine for getting the correct SelectedValue property, so I think the issue may be with how your listbox is setup.

    You say you have it bound to a DataTable, so the items in your collection are DataRowView objects.

    If you set SelectedValuePath to something like "Id", then the SelectedValue property will attempt to set the SelectedValue to DataRowView.Id, which I don't think is a valid property.

    Your two alternatives here are to either

    • Build a collection of objects with proper properties to use for the SelectedValue

      <ListBox ItemsSource="{Binding MyCollection}"
               SelectedValue="{Binding SelectedId}"
               SelectedValuePath="Id" ... />
      

      where MyCollection is a collection of objects that each contain an .Id property.

    • Or use SelectedItem instead of SelectedValue, and work with your DataRowView objects as you are now. SelectedItem will be set to the selected item in your collection, which is of type DataRowView, so the code you have posted is how it is accessed.

      ((DataRowView)this.LISTBOXNAME.SelectedItem)["ID"]
      

    Of the two, the first approach is usually preferred for WPF.


    Edit: Here's an answer to your question in the comments below since it was too long to fit in a comment.

    A ListBox is meant to be bound to a collection of items. When you set the ItemsSource to a DataTable, you are really binding to a collection of DataRowView objects.

    There are 3 ways to get selected item from here: SelectedItem, SelectedIndex, and SelectedValue.

    • SelectedItem returns you the object that is selected. In this case, that's the DataRowView object, which is why you can cast it to a DataRowView.

    • SelectedIndex returns you the index of the selected item within the data source.

    • SelectedValue can only be used if you have also set the SelectedValuePath property. SelectedValuePath tells WPF which property on each data object contains the "Value" of the item, so if you set it to something like "Id", it will try to find the .Id property on each object. In your case, there is no valid property on object DataRowView that would contain your Id column data, so this doesn't work.

    Hope that answers your question :)