Click, Double Click and Triple Click

How does C# distinguish between two single click events and a double click event?
Suppose I have a key and I want it to display "1" in the textbox if pressed once; "2" in the textbox if pressed twice and "3" if pressed three times.
If the gap between key presses is more than a second reset counter to zero.
AlHal2Asked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

it_saigeDeveloperCommented:
C# doesn't distinguish the difference, normally your operating system does.  In the simplest terms, your operating system has settings for the amount of time between clicks which determines which event is broadcast to any listeners.

You could always use a low level hook and hard code a timer which will wait for a period of time to determine the click succession:

Low-Level Hook Demonstrated on a previous EE_PAQ - https:/Q_28714279.html#a40980172

As well as here - https://blogs.msdn.microsoft.com/toub/2006/05/03/low-level-mouse-hook-in-c/

Low-Level Hook Demonstrating using a Timer to determine click succession - https:/Q_21313562.html#a13411576

-saige-
1
Russ SuterCommented:
It's determined by the operating system. There's an option in the Mouse Properties control panel that lets you adjust the double click speed.
Mouse Control PanelThe OS will always send a click event message on a single click. If another click is registered before the interval has elapsed (and that click is within a given distance from the first click) it will send an double click message as well. If the second click is registered after the time has elapsed then you just get another single click event.

As it_saige suggested above, you can hook into the windows messaging system and create your own handler to simulate a double-click event using timing other than what the operating system uses.
0
AndyAinscowFreelance programmer / ConsultantCommented:
In addition it is considered really bad practice to modify system settings without asking the user first - so adjusting the double click interval in the operating system by your app should not be an option.

The op system does not recognise a triple click, that is something you have to do yourself.

A simple alternative to that above is for your app to have the same handler for a double click as for a single click and you track the interval between mouse click events yourself and act appropriately.
0
Exploring SQL Server 2016: Fundamentals

Learn the fundamentals of Microsoft SQL Server, a relational database management system that stores and retrieves data when requested by other software applications.

AlHal2Author Commented:
Hi IT_Saige,

I've no doubt that your hook will work.  The only problem is my difficulty understanding it.
Why do you need to call DLLs?
Perhaps the complexity is due to the requestor asking about co-ordinates.  Do you have a version which just does what I need?
The code below doesn't work properly with an old mobile keypad.  I'm trying to see if it's a simple bug in my code or if I'm missing something more fundamental.

public Button clickedButton1 = null;
        public Button clickedButton2 = null;
        public Button clickedButton3 = null;
        public Button clickedButton4 = null;
        public DateTime clickTime1 = new DateTime(2011,1,1,1,1,1,1);
        public DateTime clickTime2 = new DateTime(2011, 1, 1, 1, 1, 1, 1);
        public DateTime clickTime3 = new DateTime(2011, 1, 1, 1, 1, 1, 1);
        public DateTime clickTime4 = new DateTime(2011, 1, 1, 1, 1, 1, 1);
        TimeSpan timeDiff1;
        TimeSpan timeDiff2;
        TimeSpan timeDiff3;
        TimeSpan timeDiff1Now;
        TimeSpan timeDiff2Now;
        public string LetterToDisplay = "";
        public int cmdAbcPressed = 0;
        public int cmdDefPressed = 0;
private void ProcessButton(object sender, EventArgs e)
        {

            clickedButton4 = clickedButton3;
            clickTime4 = clickTime3;
            clickedButton3 = clickedButton2;
            clickTime3 = clickTime2;
            clickedButton2 = clickedButton1;
            clickTime2 = clickTime1;
            clickedButton1 = sender as Button;
            clickTime1 = DateTime.Now;

            timeDiff1 = clickTime1-clickTime2;
            timeDiff2 = clickTime2 - clickTime3;
            timeDiff3 = clickTime3 - clickTime4;

            timeDiff1Now = DateTime.Now-clickTime2;
            timeDiff2Now = DateTime.Now - clickTime3;
            //MessageBox.Show(clickedButton1.Name + " clicked at " + clickTime1);
        }

        private void cmdAbc_Click(object sender, EventArgs e)
        {
            //string LetterToDisplay = "";
            cmdAbcPressed += 1;
            cmdDefPressed = 0;
            //if (LetterToDisplay!="")
            //{
            //    DisplayText(LetterToDisplay);
            //}
            ProcessButton(sender,e);
            if (clickedButton2 == null)
            {
                LetterToDisplay = "a";
            }
            else if(clickedButton2 != clickedButton1)
            {
                LetterToDisplay = "a";
            }
            else if  (timeDiff1.TotalMilliseconds > 500)
                //else if (((clickTime1 - clickTime2).Milliseconds > 500))
            {
                LetterToDisplay = "a";
            }
            else
            {
                if (clickedButton3 == null)
                {
                    LetterToDisplay = "b";
                }
                else if (clickedButton3 != clickedButton2)
                {
                    LetterToDisplay = "b";
                }
                else if (timeDiff2.TotalMilliseconds > 500)
                //else if (((clickTime2 - clickTime3).Milliseconds > 500))

                {
                    LetterToDisplay = "b";
                }
                else
                {
                    LetterToDisplay = "c";
                }
            }
  
            DisplayText(LetterToDisplay);
            txtDebug.Text = txtDebug.Text + (timeDiff1.TotalMilliseconds + " | " + timeDiff2.TotalMilliseconds + " | " + clickTime1 + " | " + clickTime2 + " | " + clickTime3 + " | " + clickTime4 + (char)13 + (char)10);
            if (clickedButton3 != null)
            {
                if ((timeDiff2Now).TotalMilliseconds > 500)
                {
                    clickedButton1 = null;
                    clickedButton2 = null;
                    clickedButton3 = null;
                    clickTime3 = new DateTime(2011, 1, 1, 1, 1, 1, 1);
                    clickTime2 = new DateTime(2011, 1, 1, 1, 1, 1, 1);
                    clickTime1 = new DateTime(2011, 1, 1, 1, 1, 1, 1);
                }
            }
            if (clickedButton2 != null)
            {
                if ((timeDiff1Now).TotalMilliseconds > 500)
                {
                    clickedButton1 = null;
                    clickedButton2 = null;
                    clickTime2 = new DateTime(2011, 1, 1, 1, 1, 1, 1);
                    clickTime1 = new DateTime(2011, 1, 1, 1, 1, 1, 1);
                }
            }
            if (clickedButton1 != null)
            {
                if ((DateTime.Now - clickTime1).TotalMilliseconds > 500)
                {
                    clickedButton1 = null;
                    clickTime1 = new DateTime(2011, 1, 1, 1, 1, 1, 1);
                }
            }

        }

Open in new window

0
Russ SuterCommented:
For it_saige's suggestion to work, you need to hook into the low-level windows messaging engine. That's why DLLs are needed. The .NET framework provides event handlers for the low-level messages and message timings can get in the way of accurately determining a "triple click". You might be able create a crude model that will do what you're looking for without low level message hooking but you'd have to start playing around with threaded timers and handle both the click and double_click events. Here's a code sample I whipped up quickly that will add a triple click event to a form. Note, the code could be made more complete by also checking to make sure that the mouse cursor hasn't moved beyond the distance specified by SystemInformation.DoubleClickSize.
using System;
using System.Drawing;
using System.Threading;
using System.Windows.Forms;

namespace TripleClick
{
	public partial class ClickyForm : Form
	{
		private System.Threading.Timer _clickTimer;
		private int _clickCount = 0;

		public ClickyForm()
		{
			InitializeComponent();
			this.Click += ClickyForm_Click;
			this.DoubleClick += ClickyForm_DoubleClick;
			this.TripleClick += ClickyForm_TripleClick;
		}

		private void ClickyForm_Click(object sender, EventArgs e)
		{
			++_clickCount;
			if (_clickTimer == null)
			{
				_clickTimer = new System.Threading.Timer(clickTimer_Callback, null, SystemInformation.DoubleClickTime * 2, Timeout.Infinite);
			}
			if (_clickCount == 4)
			{
				// this will be the third click
				_clickCount = 0;
				_clickTimer.Dispose();
				_clickTimer = null;
				TripleClick?.Invoke(sender, e);
				return;
			}
			// Handle regular click logic here
			System.Diagnostics.Debug.Print("click");
		}

		private void ClickyForm_DoubleClick(object sender, EventArgs e)
		{
			System.Diagnostics.Debug.Print("double click");
			if (_clickTimer != null)
			{
				_clickCount += 2;
			}
		}

		private void ClickyForm_TripleClick(object sender, EventArgs e)
		{
			System.Diagnostics.Debug.Print("triple click");
		}

		private void clickTimer_Callback(object state)
		{
			System.Diagnostics.Debug.Print("cancel click timer");
			_clickCount = 0;
			_clickTimer.Dispose();
			_clickTimer = null;
		}

		public event EventHandler TripleClick;
	}
}

Open in new window

Having run a few quick tests I have determined that this does produce a triple click event as expected under normal circumstances. However, I personally would not do it this way. Hooking into the windows messaging system would definitely be the preferred method.
0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
AlHal2Author Commented:
I've put Saige's solution into program.cs in a windows form application.
I want to put two buttons onto Form1 and check their click, double click and triple click events.  Also, I need to see if the user has pressed button 2 after a single or double click of button 1.
What do I put in form1.cs?
0
Russ SuterCommented:
AlHal2,

Where are you on this issue? Do you still need help?
0
AlHal2Author Commented:
I realised I could use key presses instead of mouse clicks and came up with this code.  It seems to work, but let me know what you think.


using System;

using System.Text.RegularExpressions;
using System.Windows.Forms;
using System.IO;
namespace 
{
    public partial class Form1 : Form
    {
        public DateTime FirstClickTime = new DateTime(2011, 1, 1, 1, 1, 1, 1);
        public DateTime SecondClickTime = new DateTime(2011, 1, 1, 1, 1, 1, 1);
        public DateTime ThirdClickTime = new DateTime(2011, 1, 1, 1, 1, 1, 1);
        public DateTime FourthClickTime = new DateTime(2011, 1, 1, 1, 1, 1, 1);
        TimeSpan timeDiff1;
        TimeSpan timeDiff2;
        TimeSpan timeDiff3;
        TimeSpan timeDiff1Now;
        public string FirstKey = "";
        public string SecondKey = "";
        public string ThirdKey = "";
        public string FourthKey = "";
        public DateTime StartTime;
        public DateTime EndTime;
        public int Keystrokes = 0;
        public int TimeTaken = 0;
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            StartTime = DateTime.Now;
            using (StreamWriter sw = new StreamWriter(@"D:\OneDrive\Documents\Tests\OldMobileKeyboard\MobKeyboard4\Results.txt", false))
            {
                sw.WriteLine("Started at " + StartTime);
            }
        }
        private void textBox1_KeyUp(object sender, KeyEventArgs e)
        {
            textBox1.Text = Regex.Replace(textBox1.Text, @"[\d-]", "");
            textBox1.Text = textBox1.Text.Replace("?", "");
        }
        private string ReturnKeys(string KeyPressed)
        {
            
            string OutputKey = "";
            switch (KeyPressed)
            {
                case "2":
                    OutputKey = "abc";
                    break;

                case "3":
                    OutputKey = "def";
                    break;
                case "4":
                    OutputKey = "ghi";
                    break;
                case "5":
                    OutputKey = "jkl";
                    break;
                case "6":
                    OutputKey = "mno";
                    break;
                case "7":
                    OutputKey = "pqrs";
                    break;
                case "8":
                    OutputKey = "tuv";
                    break;
                case "9":
                    OutputKey = "wxyz";
                    break;
                case "0":
                    OutputKey = " ";
                    break;
                case "#":
                    OutputKey = "#";
                    break;
                case "*":
                    OutputKey = "*";
                    break;

                default:
                    OutputKey = "???";
                    MessageBox.Show("Please enter one of the following keys: 234567890*#");
                    break;
            }
                    return OutputKey;
            
        }

        private void textBox1_KeyPress(object sender, KeyPressEventArgs e)
        {
            
            try
            {
                using (StreamWriter sw = new StreamWriter(@"D:\OneDrive\Documents\Tests\OldMobileKeyboard\MobKeyboard4\Results.txt", true))
                {
                Keystrokes += 1;
                textBox1.Text = Regex.Replace(textBox1.Text, @"[\d-]", "");
                textBox1.Text = textBox1.Text.Replace("?", "");
                FourthKey = ThirdKey;
                FourthClickTime = ThirdClickTime;
                ThirdKey = SecondKey;
                ThirdClickTime = SecondClickTime;
                SecondKey = FirstKey;
                SecondClickTime = FirstClickTime;
                FirstKey = e.KeyChar.ToString();
                FirstClickTime = DateTime.Now;
                string LetterToDisplay = "";
                timeDiff1Now = DateTime.Now - FirstClickTime;
                timeDiff1 = FirstClickTime - SecondClickTime;
                timeDiff2 = SecondClickTime - ThirdClickTime;
                timeDiff3 = ThirdClickTime - FourthClickTime;
                int NumberOfClicks = 0;
                
                    switch (FirstKey)
                    {
                        case ("2"):
                        case ("3"):
                        case ("4"):
                        case ("5"):
                        case ("6"):
                        case ("7"):
                        case ("8"):
                        case ("9"):
                        case ("0"):
                        case ("*"):
                        case ("#"):
                            break;
                        default:
                            e.Handled = true;
                            break;
                    }
                if (FirstKey != SecondKey)
                {
 
                        LetterToDisplay = ReturnKeys(FirstKey).Substring(0, 1);
                        NumberOfClicks = 1;
                        TimeTaken += 500;

                }
                else if (FirstKey == SecondKey && timeDiff1.TotalMilliseconds >= 500)
                {
                    LetterToDisplay = ReturnKeys(FirstKey).Substring(0, 1);
                    NumberOfClicks = 1;
                    TimeTaken += 500;
                    }
                else if (SecondKey == ThirdKey && timeDiff2.TotalMilliseconds < 500 && ThirdKey == FourthKey && timeDiff3.TotalMilliseconds >= 500)
                {
                    LetterToDisplay = ReturnKeys(SecondKey).Substring(2, 1);
                    NumberOfClicks = 3;
                    TimeTaken += 500;
                    }
                else if (SecondKey == ThirdKey && timeDiff2.TotalMilliseconds < 500 && ThirdKey == FourthKey && timeDiff3.TotalMilliseconds < 500)
                {
                    LetterToDisplay = ReturnKeys(SecondKey).Substring(3, 1);
                    NumberOfClicks = 4;
                    TimeTaken += 100;
                    }
                else if (FirstKey == SecondKey && timeDiff1.TotalMilliseconds < 500 && SecondKey != ThirdKey)
                {
                    LetterToDisplay = ReturnKeys(FirstKey).Substring(1, 1);
                    NumberOfClicks = 2;
                    TimeTaken += 100;
                }
                else if (FirstKey == SecondKey && timeDiff1.TotalMilliseconds < 500 && SecondKey == ThirdKey && timeDiff2.TotalMilliseconds >= 500)
                {
                    LetterToDisplay = ReturnKeys(FirstKey).Substring(1, 1);
                    NumberOfClicks =2;
                    TimeTaken += 100;
                }
                else if (FirstKey == SecondKey && timeDiff1.TotalMilliseconds < 500 && SecondKey == ThirdKey && timeDiff2.TotalMilliseconds < 500)
                {
                    LetterToDisplay = ReturnKeys(FirstKey).Substring(2, 1);
                    NumberOfClicks = 3;
                    TimeTaken += 100;
                }
                else
                {
                    LetterToDisplay = ReturnKeys(FirstKey).Substring(0, 1);
                    NumberOfClicks = 1;
                    TimeTaken += 500;
                }

                if (NumberOfClicks >1)
                {
                    textBox1.Text = textBox1.Text.Remove(textBox1.Text.Length -  1,  1);
                }
                
                textBox1.Text = textBox1.Text + LetterToDisplay;
                sw.WriteLine(e.KeyChar);
                textBox1.Text= Regex.Replace(textBox1.Text, @"[\d-]", "");
                textBox1.Text = textBox1.Text.Replace("?", "");
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message.ToString());
            }
        }

        private void button1_Click(object sender, EventArgs e)
        {
            EndTime = DateTime.Now;
            using (StreamWriter sw = new StreamWriter(@"D:\OneDrive\Documents\Tests\OldMobileKeyboard\MobKeyboard4\Results.txt", true))
            {
                sw.WriteLine("Ended at   " + EndTime);
                sw.WriteLine("Duration" + (EndTime - StartTime));
                sw.WriteLine("Minimum Duration " + TimeTaken +" milliseconds");
                sw.WriteLine("keystrokes " + (Keystrokes));
                sw.WriteLine("To make this more configurable I would put each key in a cross reference file with the chracters assigned to it.");
            }
            Environment.Exit(0);
        }
    }
}

Open in new window

0
Russ SuterCommented:
I'm not sure what you're trying to accomplish here but it's a completely different problem than your initial question. May I suggest that if you require assistance with the code immediately above that you open a new question. As for the original question about single, double, and triple clicking, both it_saige and myself offered viable solutions.
0
AlHal2Author Commented:
Thanks all.
0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
C#

From novice to tech pro — start learning today.