Best way to change ListBox selection to be a toggle selection
Best way to change ListBox selection to be a toggle selection
I have a ListBox control that I want to change into having a toggle selection. i.e. Click once on an item selects it, click again it deselects it. Also, clicking another item in the list should do the default action of deselecting the previous item and selecting the new one.
What's the best way of achieving this?
5 Answers
5
What you want is a RadioButtonList. You can create one by creating an ItemDataTemplate and put a RadioButton inside it. Then you can modify the template of the RadioButton to looks like a button.
I would keep the standard ListBox as that way you will keep the default behaviour you want, then for deselecting an item could you just handle it in the mouse down event? i.e. use something like this:
Point newPoint = e.GetPosition(backgroundImage);
HitTestResult result = VisualTreeHelper.HitTest(this, newPoint);
if (result.VisualHit is ListBoxItem)
I'm not sure if this is the best way, the only other way I would go about doing this is to derive my own ListBox from the standard windows control and add in a dependency property to allow me to handle the deselection in the style. If you want to take this approach I shouldn't remove the default behaviour you want to keep either, I used this approach to create a ListBox that would display an image next to the text.
If you need any pointers on where to start, then just give me a shout.
I've managed to achieve this myself by slightly creating two custom classes, ToggleListBox & ToggleListBoxItem. There are a couple of handy overrides (the most important in ToggleListBox is the one which creates your ListBoxItem).
From there it was easy to handle clicking on the same item twice, or pressing the space / enter key. 40 lines of code for both classes and works fine.
Just in case anyone is interested, I found a different solution to this which worked for me quite well. I set the ListBox.SelectionMode
to Multiple
and handled the MouseDown event thusly:
ListBox.SelectionMode
Multiple
EventManager.RegisterClassHandler(typeof(ListBoxItem),
ListBoxItem.MouseLeftButtonDownEvent,
new RoutedEventHandler(this.HandleListBox_MouseDown));
...
void HandleListBox_MouseDown(object sender, RoutedEventArgs e)
var listBoxItem = (ListBoxItem)sender;
if (ShouldDeselectOtherListItems(listBoxItem))
listBox.SelectedIndex = -1;
bool ShouldDeselectOtherListItems(ListBoxItem listBoxItem)
return !listBoxItem.IsSelected
&& listBox.SelectedItems.Count > 0;
Where listBox is the parent ListBox control. It ensures that all list box items are deselected before processing the system MouseDown event handlers, allowing the same list box item's selection state to be toggled.
I set out looking for a "built in" way to do this. When I saw there were none, and noticed the kinds of answers here which I didn't really like, I thought I'd post what I think the best way to do this is.
This answer is most similar to Bijington's answer
I simply added a PreviewMouseDown handler to the ListBox
<ListBox ... PreviewMouseLeftButtonDown="ListBox_OnPreviewMouseLeftButtonDown"... />
Then in code
private void ListBox_OnPreviewMouseLeftButtonDown (object sender, MouseButtonEventArgs e)
// I have a special extension for GetParent, numerous examples on the internet of how you would do that
var lbi = ((DependencyObject) e.OriginalSource).GetParent<ListBoxItem>();
if (lbi != null && lbi.IsSelected)
lbi.IsSelected = false;
e.Handled = true;
I then thought it would be nice to make that into an attached property, that could easily be done like so...
public static class ListBoxEx
private static DependencyProperty ToggleSelectionProperty = ...
private static bool GetToggleSelection (ListBox obj) ...
private static void SetToggleSelection (ListBox obj, bool shouldToggle)
obj.SetValue(ToggleSelectionProperty, shouldToggle);
if (shouldToggle)
obj.PreviewMouseLeftButtonDown += ToggleListBox_OnPreviewMouseLeftButtonDown ;
else
obj.PreviewMouseLeftButtonDown -= ToggleListBox_OnPreviewMouseLeftButtonDown ;
private static void ToggleListBox_OnPreviewMouseLeftButtonDown (object sender, MouseButtonEventArgs e)
// I have a special extension for GetParent, numerous examples on the internet of how you would do that
var lbi = ((DependencyObject) e.OriginalSource).GetParent<ListBoxItem>();
if (lbi != null && lbi.IsSelected)
lbi.IsSelected = false;
e.Handled = true;
Then in code:
<ListBox ... yourNamespace:ListBoxEx.ToggleSelection="True" />
Relevant Information:
Attached Property Help: Microsoft Documentation
Link
GetParent utility example: MSDN
Link
Thanks for contributing an answer to Stack Overflow!
But avoid …
To learn more, see our tips on writing great answers.
Required, but never shown
Required, but never shown
By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.