Другим вариантом является использование метода Windows API GetLastInputInfo .
Некоторые пещеры
- Я предполагаю, что Windows, потому что это WPF
- Проверьте, поддерживает ли ваш киоск GetLastInputInfo
- Я ничего не знаю о MVVM. Этот метод использует технику, которая не зависит от пользовательского интерфейса, поэтому я думаю, что она будет работать для вас.
Использование просто. Вызовите UserIdleMonitor.RegisterForNotification. Вы передаете метод уведомления и TimeSpan. Если пользовательская активность происходит, а затем прекращается в течение указанного периода, вызывается метод уведомления. Вы должны повторно зарегистрироваться, чтобы получить другое уведомление, и можете отменить регистрацию в любое время. Если в течение 49,7 дней нет активности (плюс idlePeriod), будет вызван метод уведомления.
public static class UserIdleMonitor
{
static UserIdleMonitor()
{
registrations = new List<Registration>();
timer = new DispatcherTimer(TimeSpan.FromSeconds(1.0), DispatcherPriority.Normal, TimerCallback, Dispatcher.CurrentDispatcher);
}
public static TimeSpan IdleCheckInterval
{
get { return timer.Interval; }
set
{
if (Dispatcher.CurrentDispatcher != timer.Dispatcher)
throw new InvalidOperationException("UserIdleMonitor can only be used from one thread.");
timer.Interval = value;
}
}
public sealed class Registration
{
public Action NotifyMethod { get; private set; }
public TimeSpan IdlePeriod { get; private set; }
internal uint RegisteredTime { get; private set; }
internal Registration(Action notifyMethod, TimeSpan idlePeriod)
{
NotifyMethod = notifyMethod;
IdlePeriod = idlePeriod;
RegisteredTime = (uint)Environment.TickCount;
}
}
public static Registration RegisterForNotification(Action notifyMethod, TimeSpan idlePeriod)
{
if (notifyMethod == null)
throw new ArgumentNullException("notifyMethod");
if (Dispatcher.CurrentDispatcher != timer.Dispatcher)
throw new InvalidOperationException("UserIdleMonitor can only be used from one thread.");
Registration registration = new Registration(notifyMethod, idlePeriod);
registrations.Add(registration);
if (registrations.Count == 1)
timer.Start();
return registration;
}
public static void Unregister(Registration registration)
{
if (registration == null)
throw new ArgumentNullException("registration");
if (Dispatcher.CurrentDispatcher != timer.Dispatcher)
throw new InvalidOperationException("UserIdleMonitor can only be used from one thread.");
int index = registrations.IndexOf(registration);
if (index >= 0)
{
registrations.RemoveAt(index);
if (registrations.Count == 0)
timer.Stop();
}
}
private static void TimerCallback(object sender, EventArgs e)
{
LASTINPUTINFO lii = new LASTINPUTINFO();
lii.cbSize = Marshal.SizeOf(typeof(LASTINPUTINFO));
if (GetLastInputInfo(out lii))
{
TimeSpan idleFor = TimeSpan.FromMilliseconds((long)unchecked((uint)Environment.TickCount - lii.dwTime));
//Trace.WriteLine(String.Format("Idle for {0}", idleFor));
for (int n = 0; n < registrations.Count; )
{
Registration registration = registrations[n];
TimeSpan registeredFor = TimeSpan.FromMilliseconds((long)unchecked((uint)Environment.TickCount - registration.RegisteredTime));
if (registeredFor >= idleFor && idleFor >= registration.IdlePeriod)
{
registrations.RemoveAt(n);
registration.NotifyMethod();
}
else n++;
}
if (registrations.Count == 0)
timer.Stop();
}
}
private static List<Registration> registrations;
private static DispatcherTimer timer;
private struct LASTINPUTINFO
{
public int cbSize;
public uint dwTime;
}
[DllImport("User32.dll")]
private extern static bool GetLastInputInfo(out LASTINPUTINFO plii);
}
Обновлено
Исправлена ошибка, из-за которой при попытке перерегистрации из метода уведомления вы могли зайти в тупик.
Исправлена математика без знака и добавлена непроверенная.
Небольшая оптимизация в обработчике таймера для распределения уведомлений только по мере необходимости.
Закомментировал вывод отладочной информации.
Изменено для использования DispatchTimer.
Добавлена возможность отмены регистрации.
Добавлены проверки потоков в общедоступных методах, поскольку они больше не являются потоко-безопасными.