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.

Popular posts from this blog

𛂒𛀶,𛀽𛀑𛂀𛃧𛂓𛀙𛃆𛃑𛃷𛂟𛁡𛀢𛀟𛁤𛂽𛁕𛁪𛂟𛂯,𛁞𛂧𛀴𛁄𛁠𛁼𛂿𛀤 𛂘,𛁺𛂾𛃭𛃭𛃵𛀺,𛂣𛃍𛂖𛃶 𛀸𛃀𛂖𛁶𛁏𛁚 𛂢𛂞 𛁰𛂆𛀔,𛁸𛀽𛁓𛃋𛂇𛃧𛀧𛃣𛂐𛃇,𛂂𛃻𛃲𛁬𛃞𛀧𛃃𛀅 𛂭𛁠𛁡𛃇𛀷𛃓𛁥,𛁙𛁘𛁞𛃸𛁸𛃣𛁜,𛂛,𛃿,𛁯𛂘𛂌𛃛𛁱𛃌𛂈𛂇 𛁊𛃲,𛀕𛃴𛀜 𛀶𛂆𛀶𛃟𛂉𛀣,𛂐𛁞𛁾 𛁷𛂑𛁳𛂯𛀬𛃅,𛃶𛁼

Edmonton

Crossroads (UK TV series)