Reputation: 135
I want to hide my mouse cursor after an idle time and it will be showed up when I move the mouse. I tried to use a timer but it didn't work well. Can anybody help me? Please!
Upvotes: 12
Views: 18109
Reputation: 196
If you try to hide the cursor from another thread you need to call Invoke(...) on the winform to enter the UI thread. Had cost me some headegg why Cursor.Hide() wasn't working. Also "PInvoke ShowCursor(bool bShow)" has the benefit to return the Show/Hide-Count.
[DllImport("user32.dll")]
static extern int ShowCursor(bool bShow);
private System.Timers.Timer _timerMouseAutoHide;
Point _lastPos = new Point(int.MaxValue);
readonly Stopwatch _stopwatch = new Stopwatch();
private int _cursorVisibilityCount = 0;
private void InitMouseAutoHide()
{
if (_timerMouseAutoHide != null)
return;
_timerMouseAutoHide = new Timer(100){AutoReset = false};
_timerMouseAutoHide.Elapsed += MouseAutoHideHit;
_timerMouseAutoHide.Start();
_stopwatch.Restart();
}
private void MouseAutoHideHit(object pSender, ElapsedEventArgs pE)
{
try
{
if (IsDisposed)
return;
var lCurrentPos = Cursor.Position;
var lCursorHasMoved = _lastPos != lCurrentPos;
_lastPos = lCurrentPos;
if (_cursorVisibilityCount < 0 && lCursorHasMoved)
{
//Cursor is not Visible and Mouse has moved
_cursorVisibilityCount = ShowCursor();
Debug.WriteLine($"Visible {lCurrentPos}, ShowCursor={_cursorVisibilityCount}");
}
else if (_cursorVisibilityCount > -1 && _stopwatch.ElapsedMilliseconds > 3000)
{
//Cursor is Visible and Mouse didn't move fox x ms.
_cursorVisibilityCount = HideCursor();
Debug.WriteLine($"Hidden {lCurrentPos}, ShowCursor={_cursorVisibilityCount}");
}
if (lCursorHasMoved)
_stopwatch.Restart();
}
catch (Exception lEx)
{
GLog.Error(lEx);
}
finally
{
_timerMouseAutoHide?.Start();
}
}
public int ShowCursor()
{
var lCursorVisibilityCount = 0;
Invoke(new Action(() =>
{
do
{
lCursorVisibilityCount = ShowCursor(true);
} while (lCursorVisibilityCount < 0);
}));
return lCursorVisibilityCount;
}
public int HideCursor()
{
var lCursorVisibilityCount = 0;
Invoke(new Action(() =>
{
do
{
lCursorVisibilityCount = ShowCursor(false);
} while (lCursorVisibilityCount > -1);
}));
return lCursorVisibilityCount;
}
Upvotes: 0
Reputation: 2153
Need to account for Environment.Tickcount being negative:
public static class User32Interop
{
public static TimeSpan GetLastInput()
{
var plii = new LASTINPUTINFO();
plii.cbSize = (uint)Marshal.SizeOf(plii);
if (GetLastInputInfo(ref plii))
{
int idleTime = unchecked(Environment.TickCount - (int)plii.dwTime);
return TimeSpan.FromMilliseconds(idleTime);
}
else
throw new Win32Exception(Marshal.GetLastWin32Error());
}
[DllImport("user32.dll", SetLastError = true)]
static extern bool GetLastInputInfo(ref LASTINPUTINFO plii);
struct LASTINPUTINFO
{
public uint cbSize;
public uint dwTime;
}
}
Upvotes: 4
Reputation: 38356
If you are using WinForms and will only deploy on Windows machines then it's quite easy to use user32 GetLastInputInfo
to handle both mouse and keyboard idling.
public static class User32Interop
{
public static TimeSpan GetLastInput()
{
var plii = new LASTINPUTINFO();
plii.cbSize = (uint)Marshal.SizeOf(plii);
if (GetLastInputInfo(ref plii))
return TimeSpan.FromMilliseconds(Environment.TickCount - plii.dwTime);
else
throw new Win32Exception(Marshal.GetLastWin32Error());
}
[DllImport("user32.dll", SetLastError = true)]
static extern bool GetLastInputInfo(ref LASTINPUTINFO plii);
struct LASTINPUTINFO
{
public uint cbSize;
public uint dwTime;
}
}
And then in your Form
public partial class MyForm : Form
{
Timer activityTimer = new Timer();
TimeSpan activityThreshold = TimeSpan.FromMinutes(2);
bool cursorHidden = false;
public Form1()
{
InitializeComponent();
activityTimer.Tick += activityWorker_Tick;
activityTimer.Interval = 100;
activityTimer.Enabled = true;
}
void activityWorker_Tick(object sender, EventArgs e)
{
bool shouldHide = User32Interop.GetLastInput() > activityThreshold;
if (cursorHidden != shouldHide)
{
if (shouldHide)
Cursor.Hide();
else
Cursor.Show();
cursorHidden = shouldHide;
}
}
}
Upvotes: 22
Reputation: 10048
Here is a contrived example of how to do it. You probably had some missing logic that was overriding the cursor's visibility:
public partial class Form1 : Form
{
public TimeSpan TimeoutToHide { get; private set; }
public DateTime LastMouseMove { get; private set; }
public bool IsHidden { get; private set; }
public Form1()
{
InitializeComponent();
TimeoutToHide = TimeSpan.FromSeconds(5);
this.MouseMove += new MouseEventHandler(Form1_MouseMove);
}
void Form1_MouseMove(object sender, MouseEventArgs e)
{
LastMouseMove = DateTime.Now;
if (IsHidden)
{
Cursor.Show();
IsHidden = false;
}
}
private void timer1_Tick(object sender, EventArgs e)
{
TimeSpan elaped = DateTime.Now - LastMouseMove;
if (elaped >= TimeoutToHide && !IsHidden)
{
Cursor.Hide();
IsHidden = true;
}
}
}
Upvotes: 12