Introduction
Have you enabled checkboxes in your .NET ListBox
, ListView
or TreeView
, and double-clicked on an item in your view and frustratingly watched the checkstate change, although your double-click was nowhere near the state icon? This tip details how to detect a double-click, and prevent it from activating the ItemCheck
event. The other option is to capture and restore the checkstate, which is also provided.
Background
You should be familiar with code behind GUI's, and how modifying an component or control within an event handler can in turn trigger other events.
Using the Code
Your class must inherit from the base class of either ListView
, ListBox
or TreeView
, and the CheckBoxes property must be set to True
. Just copy and paste the code into your code.
Just a brief explanation of the code from the bottom up:
OnMouseDown
is overridden to detect when a click is about to occur. It captures two things: the item under the mouse down, and a timestamp of the mouse down event. It uses a previous item and a previous timestamp by which to compare the current item and timestamp. It also uses a click counter to count clicks. If the current item matches the previous item in the last mouse down event, and this mouse down event occurs within the maximum time of a system mouse-double-click time, and the click counter is on 2, then a double-click has occurred. The variable bool initialClick
is used to distinguish an intialClick
from a subsequent click. If a subsequent click occurs within the system double-click time, after an initialClick
, given the same item, then a double-click has occurred. Else, if a subsequent click occurs beyond the system double-click time, then this subsequent click is an intialClick
itself, so intialClick
is set to false
, and the clickCount
is set to 1
for the next mouse down event. Otherwise, initialClick
defaults to true
and the clickCount
is reset to 0
, for some future click.
OnMouseDoubleClick
is overridden to reset the variable boolDoubleClick
to false
, when a double-click has occurred. This allows OnMouseDown
to determine whether or not future click pairs are double-clicks or separate single clicks.
OnItemCheck
is overridden to only call the base OnItemCheck
event when a single-click has occurred, i.e., boolDoubleClick
is false
. This prevents a client's ItemCheck
event handler from being triggered on an item double-click. Also note, that when an item double-click does occur, it maintains the item's checkstate by assigning the ItemCheckEventArgs NewValue
property to the CurrentValue
property, which properties are the items new checkstate and current checkstate, respectively.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Windows.Forms;
using System.Text;
namespace MyViews
{
public partial class MyListView: ListView
{
public MyListView(): base()
{
InitializeComponent();
}
public MyListView(IContainer container): base()
{
container.Add(this);
InitializeComponent();
}
private bool
boolDoubleClick = false,
initialClick = true;
private int
clickCount = 0;
DateTime
currTimeStamp = new DateTime(),
prevTimeStamp = new DateTime();
private ListViewItem prevLVItem = null;
private TimeSpan tSpan = new TimeSpan();
public bool BoolDoubleClick { get { return boolDoubleClick; } }
protected override void OnItemCheck(ItemCheckEventArgs ice)
{
if(!boolDoubleClick)
base.OnItemCheck(ice);
else
ice.NewValue = ice.CurrentValue;
}
protected override void OnMouseDoubleClick(MouseEventArgs e)
{
ListViewItem lvItem = this.GetItemAt(e.X, e.Y);
base.OnMouseDoubleClick(e);
boolDoubleClick = false;
}
protected override void OnMouseDown(MouseEventArgs e)
{
ListViewItem lvItem = this.GetItemAt(e.X, e.Y);
base.OnMouseDown(e);
boolDoubleClick = false;
if(lvItem!=null && (initialClick || lvItem==prevLVItem))
{ ++clickCount;
prevTimeStamp = currTimeStamp;
currTimeStamp = DateTime.Now;
tSpan = currTimeStamp - prevTimeStamp;
if(initialClick || tSpan.TotalMilliseconds<=SystemInformation.DoubleClickTime)
{ if(clickCount==2)
{ boolDoubleClick = true;
initialClick = true;
clickCount = 0;
}
else
initialClick = false;
}
else if(!initialClick && tSpan.TotalMilliseconds>SystemInformation.DoubleClickTime)
{ initialClick = false;
clickCount = 1;
}
else
{ initialClick = true;
clickCount = 0;
}
}
prevLVItem = lvItem;
} }}
Points of Interest
BIG NOTE: You cannot debug the code by setting a breakpoint inside OnMouseDown
. If you do so, it will never detect a double-click, because the timestamp captures will always occur too far apart, due to the breakpoint. Instead set the break point in either OnItemCheck
or OnMouseDoubleClick
to see the value of boolDoubleClick
change in response to clicks.
History
- 21st November, 2013: Initial version