Is there any way to force a listview control to treat all clicks as though they were done through the Control key?
I need to replicate the functionality of using the control key (selecting an item sets and unsets its selection status) in order to allow the user to easily select multiple items at the same time.
Thank you in advance.
It's not the standard behaviour of the ListView control, even when MultiSelect is set to true.
If you wanted to create your own custom control you would need to do the following:
Should be simple enough to implement and feel like multi-select without using the control key!
Here is a complete solution that is a modification of the solution provided by Matthew M. above.
It offers an improvement as well as a bit of added functionality.
Improvement:
Added functionality:
MultiSelectionLimit
) that allows you to put a limit on how many items can be selected at once.After my first posting I realised a minor problem with the code. Clearing multiple selections would lead to the ItemSelectionChanged
event being invoked multiple times.
I could find no way to avoid this with the current inheritance, so instead I adopted a solution where the bool property SelectionsBeingCleared
will be true while until all selected items have been deselected.
This way a simple call to that property will make it possible to avoid updating effects until all of the multiple selections have been cleared.
public class ListViewMultiSelect : ListView
{
public const int WM_LBUTTONDOWN = 0x0201;
public const int WM_RBUTTONDOWN = 0x0204;
private bool _selectionsBeingCleared;
/// <summary>
/// Returns a boolean indicating if multiple items are being deselected.
/// </summary>
/// <remarks> This value can be used to avoid updating through events before all deselections have been carried out.</remarks>
public bool SelectionsBeingCleared
{
get
{
return this._selectionsBeingCleared;
}
private set
{
this._selectionsBeingCleared = value;
}
}
private int _multiSelectionLimit;
/// <summary>
/// The limit to how many items that can be selected simultaneously. Set value to zero for unlimited selections.
/// </summary>
public int MultiSelectionLimit
{
get
{
return this._multiSelectionLimit;
}
set
{
this._multiSelectionLimit = Math.Max(value, 0);
}
}
public ListViewMultiSelect()
{
this.ItemSelectionChanged += this.multiSelectionListView_ItemSelectionChanged;
}
public ListViewMultiSelect(int selectionsLimit)
: this()
{
this.MultiSelectionLimit = selectionsLimit;
}
private void multiSelectionListView_ItemSelectionChanged(object sender, ListViewItemSelectionChangedEventArgs e)
{
if (e.IsSelected)
{
if (this.MultiSelectionLimit > 0 && this.SelectedItems.Count > this.MultiSelectionLimit)
{
this._selectionsBeingCleared = true;
List<ListViewItem> itemsToDeselect = this.SelectedItems.Cast<ListViewItem>().Except(new ListViewItem[] { e.Item }).ToList();
foreach (ListViewItem item in itemsToDeselect.Skip(1)) {
item.Selected = false;
}
this._selectionsBeingCleared = false;
itemsToDeselect[0].Selected = false;
}
}
}
protected override void WndProc(ref Message m)
{
switch (m.Msg)
{
case WM_LBUTTONDOWN:
if (this.SelectedItems.Count == 0 || !this.MultiSelect) { break; }
if (this.MultiSelectionLimit > 0 && this.SelectedItems.Count > this.MultiSelectionLimit) { this.ClearSelections(); }
int x = (m.LParam.ToInt32() & 0xffff);
int y = (m.LParam.ToInt32() >> 16) & 0xffff;
ListViewHitTestInfo hitTest = this.HitTest(x, y);
if (hitTest != null && hitTest.Item != null) { hitTest.Item.Selected = !hitTest.Item.Selected; }
this.Focus();
return;
case WM_RBUTTONDOWN:
if (this.SelectedItems.Count > 0) { this.ClearSelections(); }
break;
}
base.WndProc(ref m);
}
private void ClearSelections()
{
this._selectionsBeingCleared = true;
SelectedListViewItemCollection itemsToDeselect = this.SelectedItems;
foreach (ListViewItem item in itemsToDeselect.Cast<ListViewItem>().Skip(1)) {
item.Selected = false;
}
this._selectionsBeingCleared = false;
this.SelectedItems.Clear();
}
}
You might want to also consider using Checkboxes on the list view. It's an obvious way to communicate the multi-select concept to your average user who may not know about Ctrl+Click.
From the MSDN page:
The CheckBoxes property offers a way to select multiple items in the ListView control without using the CTRL key. Depending on your application, using check boxes to select items rather than the standard multiple selection method may be easier for the user. Even if the MultiSelect property of the ListView control is set to false, you can still display checkboxes and provide multiple selection capabilities to the user. This feature can be useful if you do not want multiple items to be selected yet still want to allow the user to choose multiple items from the list to perform an operation within your application.
Here is the complete solution that I used to solve this problem using WndProc. Basically, it does a hit test when the mouse is clicked.. then if MutliSelect is on, it will automatically toggle the item on/off [.Selected] and not worry about maintaining any other lists or messing with the ListView functionality.
I haven't tested this in all scenarios, ... it worked for me. YMMV.
public class MultiSelectNoCTRLKeyListView : ListView {
public MultiSelectNoCTRLKeyListView() {
}
public const int WM_LBUTTONDOWN = 0x0201;
protected override void WndProc(ref Message m) {
switch (m.Msg) {
case WM_LBUTTONDOWN:
if (!this.MultiSelect)
break;
int x = (m.LParam.ToInt32() & 0xffff);
int y = (m.LParam.ToInt32() >> 16) & 0xffff;
var hitTest = this.HitTest(x, y);
if (hitTest != null && hitTest.Item != null)
hitTest.Item.Selected = !hitTest.Item.Selected;
return;
}
base.WndProc(ref m);
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With