Similar to the TreeView, there is yet another WPF control that is severely limited in its functionality for no obvious reason – the
ListBox. Inheriting from
ListBox implements support for a very important feature – multiple selection, which makes it possible for the user to select multiple items using either of the two
Extended– where you can hold the Shift key to select multiple items
Multiple– where you can select multiple items by just clicking them.
ListBox control even has a property,
SelectedItems, where you can see what models were selected, but it doesn't have a public setter. Besides that, despite
ListBox having support for value selectors (via
SelectedValue), it does not expose a similar property for multiple selected items, meaning that there is no
SelectedValues, as one might expect.
I haven’t looked very far, but there were two ways of setting
SelectedItems from code that I found:
ListBox.SetSelectedItems(…)protected method that takes a non-generic
- Accessing the
SelectedItemsproperty, which is a non-generic
IList, to clear the existing items and add new ones as necessary
Both of them are essentially equivalent, but I chose the second one because it appeared more trivial.
Here’s how my basic behavior looked:
The main logic is placed inside the
SelectItems() method which just boils down to clearing and populating the
SelectedItems property of the
ListBox. It’s also worth to note that,
ListBox.SelectedItems.Add(…) will just silently return without adding anything if given an invalid parameter (non-existent item, for example).
Make sure to not forget the
_XXXhandled flags, as they prevent endless recursions that would otherwise occur – a property changed from view will trigger an event that changes the property in source, which in turn triggers an event that changes the property in view and so on (or the other way around).
ListBox in my project was actually utilizing
SelectedValuePath so I’d also need to implement
SelectedValues to make full use of that. For those unfamiliar –
SelectedValuePath is a pretty convenient mechanic to bind to object’s properties without losing binding to the object reference itself. You can read a bit more on it here.
SelectedValue works in WPF controls is actually pretty simple – when an item is selected, its property value is populated to
SelectedValue using the property path stored in
SelectedValuePath; and vice versa - when a
SelectedValue is changed from source, a respective item, whose property value is equal to it, is selected.
To get the value of a property by its path I used reflection in the following way:
And then I added
SelectedValues dependency property to complement the other one, as well as methods to convert between
At this point it can get really confusing. Important thing to remember is that S
electedValues takes precedence over
SelectedItems, because in the worst case scenario (when
SelectedValuePath is not set), it will be equal to
SelectedItems. That means when we’re not sure which of them changed last, we assume
SelectedValues to be the most up to date.
SelectedValues is not actually changed in any of the event handlers (
OnListBoxItemsChanged), because it’s updated in the dependency property callback (
OnSelectedItemsChanged). In other words, when the view changes – an event is raised (
OnListBoxSelectionChanged), which in turn updates
SelectedItems, which raises the callback event of the dependency property (
OnSelectedItemsChanged), which converts
SelectedValuePath, setting the property to the new value.
_XXXhandled flags also play an important role here, avoiding even more possible unwanted recursions (or event loops).
Finally, this is how you use it:
This behavior, among others, is implemented in my WPF extension library.