c# - remove - WPF inactividad y actividad
subscribe to custom event c# (4)
En vez de escuchar PreProcessInput
¿has probado PreviewMouseMove ?
Intento manejar la inactividad y la actividad del usuario en una aplicación WPF para atenuar algunas cosas. Después de mucha investigación, decidí ir con (al menos en mi opinión) una solución muy elegante que Hans Passant publicó here .
Solo hay un inconveniente: siempre que el cursor permanezca en la parte superior de la ventana, el evento PreProcessInput
se dispara continuamente. Tengo una aplicación de pantalla completa, así que esto lo mata. Cualquier idea sobre cómo puedo evitar este comportamiento sería muy apreciada.
public partial class MainWindow : Window
{
readonly DispatcherTimer activityTimer;
public MainWindow()
{
InitializeComponent();
InputManager.Current.PreProcessInput += Activity;
activityTimer = new DispatcherTimer
{
Interval = TimeSpan.FromSeconds(10),
IsEnabled = true
};
activityTimer.Tick += Inactivity;
}
void Inactivity(object sender, EventArgs e)
{
rectangle1.Visibility = Visibility.Hidden; // Update
// Console.WriteLine("INACTIVE " + DateTime.Now.Ticks);
}
void Activity(object sender, PreProcessInputEventArgs e)
{
rectangle1.Visibility = Visibility.Visible; // Update
// Console.WriteLine("ACTIVE " + DateTime.Now.Ticks);
activityTimer.Stop();
activityTimer.Start();
}
}
Actualizar
Podría reducir el comportamiento descrito mejor (consulte la actualización de rectangle1.Visibility
en el código anterior). Siempre que el cursor descanse en la parte superior de la ventana y, por ejemplo, se PreProcessInput
la Visibility
de un control, se PreProcessInput
. Tal vez estoy malinterpretando el propósito del evento PreProcessInput
y cuando se dispara. MSDN no fue muy útil aquí.
Hemos tenido una necesidad similar de nuestro software ... también es una aplicación de WPF, y como característica de seguridad, un cliente puede configurar una hora en la que su usuario será desconectado si está inactivo.
A continuación se muestra la clase que hice para ajustar el código de detección de inactividad (que utiliza la funcionalidad integrada de Windows).
Simplemente tenemos un tic de temporizador cada 1 segundo para verificar si el tiempo de inactividad es mayor que el umbral especificado ... toma 0 CPU.
Primero, aquí está cómo usar el código:
var idleTime = IdleTimeDetector.GetIdleTimeInfo();
if (idleTime.IdleTime.TotalMinutes >= 5)
{
// They are idle!
}
Puede usar esto y también asegurarse de que su aplicación de filtrado completo de WPF esté "enfocada" para lograr sus necesidades:
using System;
using System.Runtime.InteropServices;
namespace BlahBlah
{
public static class IdleTimeDetector
{
[DllImport("user32.dll")]
static extern bool GetLastInputInfo(ref LASTINPUTINFO plii);
public static IdleTimeInfo GetIdleTimeInfo()
{
int systemUptime = Environment.TickCount,
lastInputTicks = 0,
idleTicks = 0;
LASTINPUTINFO lastInputInfo = new LASTINPUTINFO();
lastInputInfo.cbSize = (uint)Marshal.SizeOf(lastInputInfo);
lastInputInfo.dwTime = 0;
if (GetLastInputInfo(ref lastInputInfo))
{
lastInputTicks = (int)lastInputInfo.dwTime;
idleTicks = systemUptime - lastInputTicks;
}
return new IdleTimeInfo
{
LastInputTime = DateTime.Now.AddMilliseconds(-1 * idleTicks),
IdleTime = new TimeSpan(0, 0, 0, 0, idleTicks),
SystemUptimeMilliseconds = systemUptime,
};
}
}
public class IdleTimeInfo
{
public DateTime LastInputTime { get; internal set; }
public TimeSpan IdleTime { get; internal set; }
public int SystemUptimeMilliseconds { get; internal set; }
}
internal struct LASTINPUTINFO
{
public uint cbSize;
public uint dwTime;
}
}
Implemento la solución en una clase IdleDetector. He mejorado un poco el código. ¡El detector Iddle lanza un IsIdle que puede ser interceptado! ¡Da eso! Espero algunos comentarios.
public class IdleDetector
{
private readonly DispatcherTimer _activityTimer;
private Point _inactiveMousePosition = new Point(0, 0);
private IInputElement _inputElement;
private int _idleTime = 300;
public event EventHandler IsIdle;
public IdleDetector(IInputElement inputElement, int idleTime)
{
_inputElement = inputElement;
InputManager.Current.PreProcessInput += OnActivity;
_activityTimer = new DispatcherTimer { Interval = TimeSpan.FromSeconds(idleTime), IsEnabled = true };
_activityTimer.Tick += OnInactivity;
}
public void ChangeIdleTime(int newIdleTime)
{
_idleTime = newIdleTime;
_activityTimer.Stop();
_activityTimer.Interval = TimeSpan.FromSeconds(newIdleTime);
_activityTimer.Start();
}
void OnInactivity(object sender, EventArgs e)
{
_inactiveMousePosition = Mouse.GetPosition(_inputElement);
_activityTimer.Stop();
IsIdle?.Invoke(this, new EventArgs());
}
void OnActivity(object sender, PreProcessInputEventArgs e)
{
InputEventArgs inputEventArgs = e.StagingItem.Input;
if (inputEventArgs is MouseEventArgs || inputEventArgs is KeyboardEventArgs)
{
if (e.StagingItem.Input is MouseEventArgs)
{
MouseEventArgs mouseEventArgs = (MouseEventArgs)e.StagingItem.Input;
// no button is pressed and the position is still the same as the application became inactive
if (mouseEventArgs.LeftButton == MouseButtonState.Released &&
mouseEventArgs.RightButton == MouseButtonState.Released &&
mouseEventArgs.MiddleButton == MouseButtonState.Released &&
mouseEventArgs.XButton1 == MouseButtonState.Released &&
mouseEventArgs.XButton2 == MouseButtonState.Released &&
_inactiveMousePosition == mouseEventArgs.GetPosition(_inputElement))
return;
}
_activityTimer.Stop();
_activityTimer.Start();
}
}
}
Pude averiguar qué causó el comportamiento descrito.
Por ejemplo, cuando se modifica la Visibility
de un control, el evento PreProcessInput
se PreProcessInputEventArgs.StagingItem.Input
con PreProcessInputEventArgs.StagingItem.Input
del tipo InputReportEventArgs
.
El comportamiento se puede evitar filtrando InputEventArgs
para los tipos MouseEventArgs
y KeyboardEventArgs
en el evento OnActivity
y para verificar si no se presiona ningún botón del mouse y la posición del cursor sigue siendo la misma ya que la aplicación se volvió inactiva.
public partial class MainWindow : Window
{
private readonly DispatcherTimer _activityTimer;
private Point _inactiveMousePosition = new Point(0, 0);
public MainWindow()
{
InitializeComponent();
InputManager.Current.PreProcessInput += OnActivity;
_activityTimer = new DispatcherTimer { Interval = TimeSpan.FromMinutes(5), IsEnabled = true };
_activityTimer.Tick += OnInactivity;
}
void OnInactivity(object sender, EventArgs e)
{
// remember mouse position
_inactiveMousePosition = Mouse.GetPosition(MainGrid);
// set UI on inactivity
rectangle.Visibility = Visibility.Hidden;
}
void OnActivity(object sender, PreProcessInputEventArgs e)
{
InputEventArgs inputEventArgs = e.StagingItem.Input;
if (inputEventArgs is MouseEventArgs || inputEventArgs is KeyboardEventArgs)
{
if (e.StagingItem.Input is MouseEventArgs)
{
MouseEventArgs mouseEventArgs = (MouseEventArgs)e.StagingItem.Input;
// no button is pressed and the position is still the same as the application became inactive
if (mouseEventArgs.LeftButton == MouseButtonState.Released &&
mouseEventArgs.RightButton == MouseButtonState.Released &&
mouseEventArgs.MiddleButton == MouseButtonState.Released &&
mouseEventArgs.XButton1 == MouseButtonState.Released &&
mouseEventArgs.XButton2 == MouseButtonState.Released &&
_inactiveMousePosition == mouseEventArgs.GetPosition(MainGrid))
return;
}
// set UI on activity
rectangle.Visibility = Visibility.Visible;
_activityTimer.Stop();
_activityTimer.Start();
}
}
}