WPF inactivity and activity

匿名 (未验证) 提交于 2019-12-03 08:33:39

问题:

I'm trying to handle user inactivity and activity in a WPF application to fade some stuff in and out. After a lot of research, I decided to go with the (at least in my opinion) very elegant solution Hans Passant posted here.

There's only one downside: As long as the cursor stays on top of the window, the PreProcessInput event gets continously fired. I'm having a full-screen application, so this kills it. Any ideas how I can bypass this behaviour would be most appreciated.

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();     } } 

Update

I could narrow down the described behaviour better (see the rectangle1.Visibility update in the above code). As long as the cursor rests on top of the window and for example the Visibility of a control is changed, the PreProcessInput is raised. Maybe I'm misunderstanding the purpose of the PreProcessInput event and when it fires. MSDN wasn't very helpful here.

回答1:

We've had a similar need for our software... it's a WPF application as well, and as a security feature - a client can configure a time that their user's will be logged off if they are idle.

Below is the class that I made to wrap the Idle Detection code (which utilizes built in Windows functionality).

We simply have a timer tick ever 1 second to check if the idle time is greater than the specified threshold ... takes 0 CPU.

First, here's how to use the code:

var idleTime = IdleTimeDetector.GetIdleTimeInfo();  if (idleTime.IdleTime.TotalMinutes >= 5) {     // They are idle! } 

You can use this and also make sure that your WPF full screened app is "focused" to achieve your needs:

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;     } } 


回答2:

I could figure out what caused the described behaviour.

For example when the Visibility of a control is changed, the PreProcessInput event is raised with PreProcessInputEventArgs.StagingItem.Input of the type InputReportEventArgs.

The behaviour can be avoided by filtering the InputEventArgs for the types MouseEventArgs and KeyboardEventArgs in the OnActivity event and to verify if no mouse button is pressed and the position of the cursor is still the same as the application became inactive.

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();         }     } } 


回答3:

Instead of listening to PreProcessInput have you tried PreviewMouseMove?



回答4:

I implements the solution in a IdleDetector class. I have improved a little bit the code. The Iddle detector throw an IsIdle That can be intercepte ! It give that ! I wait for some comments.

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();         }     } } 


标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!