4

I am new C# and I am using windows forms. I built an application where I want to show a massage if the mouse stops (no activities are going) for 5 seconds using a timer and when the mouse moves the timer gets reset.

I have Form1 with some controls (Buttons and TextBoxes) and a Timer. I am just trying to do something like a screensaver, so when Form1 loads and there are no activities for a certain time (mouse stops) an action has to be taken (for example show message).

I tried the following code (as an example) but it doesn't work well, when Form1 loads the timers start counting and if I move mouse (before i == 5) the timer resets and it never starts counting again.

int i = 0;
private void Form1_Load(object sender, EventArgs e)
{
    timer1.Start();
}

private void Form1_MouseMove(object sender, MouseEventArgs e)
{
    i = 0;
    timer1.Stop();
    textBox1.Text = i.ToString();
}

private void Form1_MouseHover(object sender, EventArgs e)
{
    timer1.Start();
}

private void timer1_Tick(object sender, EventArgs e)
{
    i = i + 1;
    textBox1.Text = i.ToString();

    if(i==5)
    {
        MessageBox.Show("Time is over");
    }
}

I do not know if I am using the right mouse events and I do not even know if it is correct to use those events in such a situation. How can I show a message if the mouse doesn't move for 5 seconds?

Kate
  • 935
  • 4
  • 14
  • 34
  • 1
    instead of MouseHover event put `timer1.Start();` directly at the end of MouseMove event... – M.kazem Akhgary Jun 03 '16 at 16:49
  • 1
    You have to look at the position of the mouse with each tick. How else do you know if it moved? If the position is the same then you know it is not moving. – Hanlet Escaño Jun 03 '16 at 16:49
  • btw HanletEscano suggests better solution in terms of efficiency... – M.kazem Akhgary Jun 03 '16 at 16:51
  • You forgot to call timer1.Start() again. It isn't the right way to do it, you need to implement IMessageFilter so you can see *every* move. [Like this](http://stackoverflow.com/a/12641376/17034). – Hans Passant Jun 03 '16 at 16:51

1 Answers1

2

Unfortunately WinForms doesn't have a PreviewMouseMove event like WPF, so when you move your mouse over a Control, the Form will never know of it. You can however use the system function SetWindowsHookEx and reset your timer every time you register a mouse event in your form.

If you are interested in the last time the user did any kind of input anywhere on the OS (like screensavers do), you should take a look at the GetLastInputInfo function too.

using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        public enum HookType : int
        {
            WH_JOURNALRECORD = 0,
            WH_JOURNALPLAYBACK = 1,
            WH_KEYBOARD = 2,
            WH_GETMESSAGE = 3,
            WH_CALLWNDPROC = 4,
            WH_CBT = 5,
            WH_SYSMSGFILTER = 6,
            WH_MOUSE = 7,
            WH_HARDWARE = 8,
            WH_DEBUG = 9,
            WH_SHELL = 10,
            WH_FOREGROUNDIDLE = 11,
            WH_CALLWNDPROCRET = 12,
            WH_KEYBOARD_LL = 13,
            WH_MOUSE_LL = 14
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct POINT
        {
            public int X;
            public int Y;
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct MouseHookStruct
        {
            public POINT pt;
            public int hwnd;
            public int hitTestCode;
            public int dwExtraInfo;
        }

        [DllImport("user32.dll", SetLastError = true)]
        static extern int SetWindowsHookEx(HookType hook, HookProc callback, IntPtr hInstance, uint dwThreadId);

        [DllImport("user32.dll", SetLastError = true)]
        static extern int CallNextHookEx(int hook, int code, IntPtr wParam, IntPtr lParam);

        [DllImport("kernel32.dll")]
        static extern int GetCurrentThreadId();

        public delegate int HookProc(int nCode, IntPtr wParam, IntPtr lParam);
        private static int _hHook;

        private readonly Timer _timer1;

        public Form1()
        {
            InitializeComponent();

            _timer1 = new Timer();
            // setting the interval to 5000 is a lot easier than counting up to 5 ;)
            _timer1.Interval = 5000;
            _timer1.Tick += Timer1OnTick;
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            // hook up to mouse events (or keyboard with WH_KEYBOARD)
            _hHook = SetWindowsHookEx(HookType.WH_MOUSE, MouseHookProc, IntPtr.Zero, (uint)GetCurrentThreadId());
            _timer1.Start();
        }

        // This function will get called every time there is a mouse event
        private int MouseHookProc(int code, IntPtr wParam, IntPtr lParam)
        {
            // Mouse event --> reset Timer
            _timer1.Stop();
            _timer1.Start();

            return CallNextHookEx(_hHook, code, wParam, lParam);
        }

        // 5000 ms without any mouse events --> show message
        private void Timer1OnTick(object sender, EventArgs eventArgs)
        {
            //Stop timer, Show message, start timer
            _timer1.Stop();
            MessageBox.Show("You have been idle for " + _timer1.Interval + " ms!");
            _timer1.Start();
        }
    }
}
Manfred Radlwimmer
  • 13,257
  • 13
  • 53
  • 62