Enable MultiSelect in WPF ListView






3.50/5 (6 votes)
Make your ListView support item selection by dragging,
Introduction
This article demos how to create a component that enables multi-select support for your ListView.
Background
I was developing a file list, and I didn't pay much attention to the selection mode as I thought setting ListView.SelectionMode
to Multiple
/Extended
will select multiple items in a ListView
by clicking and dragging. (That's what my .NET 1.1 book told me.)
Then, when it was almost completed, I had the time to do more tests, and was unable to select more that one item. This was very annoying, so I set the SelectionMode
to Multiple
.
<ListView SelectionMode="Multiple" .../>
I recompile and it did not seem to work. I changed the SelectionMode
to Extended
, and it still was not working.
I figured out that although it allowed me to select multiple items using Control and Shift, I could not use mouse drag to select. I am not sure about others, but this is really a big surprise for me.
It was weird that even though this was a big problem, I could not Google and get a helpful / completed solution that allowed multiple selects, so I developed this component with my limited WPF knowledge.
My design process
When I started, I tried to find the position of each ListViewItem
and compare that with the selected region; I did some research but with no luck:
- There seems to be no common place to store which
ListViewItem
is located where, unless you developed your ownPanel
. - It seems possible to override
OnMouseMove
ofTreeViewItem
, but this will limit yourListView
to be used in aStackPanel
template only, as it requires your mouse above theTreeViewItem
. (See theMultiSelectListView
in the demo project.) - The
Arrange
method exposes the size only, theArrangeCore
method can expose a rect, but it issealed
.
After some Googling, I found a method named VisualTreeHelper.HitTest
, which takes:
- a control,
- a filter function, (retrieve the selected item via this func)
- a result callback, and
- a rect.
and returns child items in the specified rect.
It does what I wanted, but the HitTest
method can return only the visible item, so if I start selecting, then scroll down, it will search only the items on the screen, not the hidden items. This is then fixed by HitTest
twice, first by unselecting all the visible items, and then selecting the selected visible items.
//Unselect all visible selected items no matter if it's current selected or not.
VisualTreeHelper.HitTest(lvSender, UnselectHitTestFilterFunc,
new HitTestResultCallback(SelectResultCallback),
new GeometryHitTestParameters(new RectangleGeometry(unselectRect)));
//Select all visible items in select region.
VisualTreeHelper.HitTest(lvSender, SelectHitTestFilterFunc,
new HitTestResultCallback(SelectResultCallback),
new GeometryHitTestParameters(new RectangleGeometry(selectRect)));
The last problem is to draw the preview rectangle, which is visible when selecting. As my knowledge is limited, all I can do is to draw it in the background. Please post if you have a better solution. (As you can see, the preview rectangle cannot deal with scrolling; I haven't figured out how to obtain the scrolling information yet.)
lvSender.Background = new DrawingBrush(
DrawRectangle(selectRect, lvSender.ActualWidth, lvSender.ActualHeight));
Using the code
I have created a component for this operation, you can include the code in your project and set an Attached
property to enable multi-select. You may have to set the ItemPanel
Template so there is space for starting the dragging.
<ListView cont:ListViewSelectionHelper.MultiSelect="True"
cont:ListViewSelectionHelper.PreviewDrag="True">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Margin="0,0,20,0" IsItemsHost="True" />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.Items>
<TextBlock>Test1</TextBlock>
<TextBlock>Test2</TextBlock>
<TextBlock>Test3</TextBlock>
<TextBlock>Test4</TextBlock>
<TextBlock>Test5</TextBlock>
</ListView.Items>
</ListView>
Setting the MultiSelect
will attach four events to your ListView
:
PreviewMouseDown
, to clear selections.MouseDown
to enable theIsDragging
attached property.MouseUp
to disable theIsDragging
attached property.MouseMove
, ifIsDragging
, selects the items.
How it looks when MultiSelect is enabled on my file list
History
- 05-02-08: Initial version (hopefully not the last version).
- 05-13-08: Version 2, fixed three bugs:
- Cannot drag if scrollbar not visible.
- Strange selection effect after drag.
- Shift+Select not working as intended.