如何在Windows应用程序中设置用户空闲或不活动时的定时器。

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何在Windows应用程序中设置用户空闲或不活动时的定时器。相关的知识,希望对你有一定的参考价值。

我在Windows窗体中有一个关于设置用户闲置或不活动时的定时器的问题。我需要在任何鼠标事件上设置定时器。如果用户做任何时刻,那么我需要重置定时器。所以这是需求。下面是代码。

using System;
using System.Windows.Forms;
using Timer = System.Windows.Forms.Timer;

namespace FormsTimerSetup.Globals
{
    public class SetApplicationTimeOut : Form
    {
        #region
        /// <summary>
        /// Private Timer Property
        /// </summary>
        private static Timer _timer;

        /// <summary>
        /// Timer Property
        /// </summary>
        public static Timer Timer
        {
            get
            {
                return _timer;
            }
            set
            {
                if (_timer != null)
                {
                    _timer.Tick -= Timer_Tick;
                }

                _timer = value;

                if (_timer != null)
                {
                    _timer.Tick += Timer_Tick;
                }
            }
        }
        #endregion

        #region Events
        public event EventHandler UserActivity;
        #endregion

        #region Constructor
        /// <summary>
        /// Default/Parameterless SetApplicationTimeOut Constructor
        /// </summary>
        public SetApplicationTimeOut()
        {
            KeyPreview = true;

            FormClosed += ObservedForm_FormClosed;
            MouseMove += ObservedForm_MouseMove;
            KeyDown += ObservedForm_KeyDown;
        }
        #endregion

        #region Inherited Methods
        /// <summary>
        /// 
        /// </summary>
        /// <param name="e"></param>
        protected virtual void OnUserActivity(EventArgs e)
        {
            // Invoking the UserActivity delegate
            UserActivity?.Invoke(this, e);
        }

        /// <summary>
        /// 
        /// </summary>
        public void SetTimeOut()
        {
            // postpone auto-logout by 30 minutes
            _timer = new Timer
            {
                Interval = (30 * 60 * 1000) // Timer set for 30 minutes
            };

            Application.Idle += Application_Idle;

            _timer.Tick += new EventHandler(Timer_Tick);
        }
        #endregion

        #region Private Methods
        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ObservedForm_MouseMove(object sender, MouseEventArgs e)
        {
            OnUserActivity(e);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ObservedForm_KeyDown(object sender, KeyEventArgs e)
        {
            OnUserActivity(e);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ObservedForm_FormClosed(object sender, FormClosedEventArgs e)
        {
            FormClosed -= ObservedForm_FormClosed;
            MouseMove -= ObservedForm_MouseMove;
            KeyDown -= ObservedForm_KeyDown;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private static void Application_Idle(object sender, EventArgs e)
        {
            _timer.Stop();
            _timer.Start();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private static void Timer_Tick(object sender, EventArgs e)
        {
            _timer.Stop();
            Application.Idle -= Application_Idle;
            MessageBox.Show("Application Terminating");
            Application.Exit();
        }
        #endregion
    }
}

我已经实现了代码,但不确定它是否是正确的方式来做。

任何线索将被感激。谢谢你看完帖子,注意安全!

答案

问:"我需要在任何鼠标事件上设置定时器......如果用户做出任何动作,那么我需要重置定时器......任何线索都将被感激。"

答:我试着提供一些线索,希望你觉得有用。你说你想让MouseMove事件来重置计时器,但是有一个问题:任何时候子控件有焦点的时候,都是子控件收到鼠标事件,而主窗体没有。这个问题是可以解决的。

简短的答案是:"实现子控件的MouseMove事件。"实现 IMessageFilter 接口上的主窗口类,这样当检测到鼠标移动时,定时器就会被重置。" 在主窗口类上添加一个 信息过滤 可以拦截鼠标信息 之前 它们被发送到重点控制。

所以,现在我必须给你所有的细节,所以这里是长答案。首先要加入... IMessageFilter 接口到我们的主Form1,就像这样。

public partial class Form1 : Form, IMessageFilter

IMessageFilter要求我们的类只需要实现一个方法。

    public bool PreFilterMessage(ref Message m)
    {
        switch (m.Msg)
        {
            case WM_MOUSEMOVE:
            // vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
            // Commit f9367d7c added at OP's request
            case WM_KEYDOWN:  
            // This makes WakeUp persist if user is typing in the textbox.
            // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
                TimeOutState = TimeOutState.WakeUp;
                break;
        }
        return false; // Do not suppress downstream message
    }
    const int  // WinOS Messages
        WM_KEYDOWN = 0x0100,
        WM_MOUSEMOVE = 0x0200;

你可以看到,现在任何鼠标移动都会将我们的应用程序的TimeOutState设置为 "WakeUp"。

enum TimeOutState{ WakeUp, Sleeping, Warning, Exit }

我们只需要一个定时器,定时器的每一个tick间隔(这里设置为5秒)就会减少一个状态。如果鼠标不动,它就会一路递减,最后退出。

下面是一个60秒的 视频 的运行应用程序60秒。你可以看到每5秒或鼠标移动时发生的变化。如果您想运行该样本,您可以 克隆 的最新提交。

下面是其余的细节。

  1. MessageFilter需要被连接。因为我们需要我们的Form有它的窗口句柄,所以我们在这里做这个并启动定时器。

    protected override void OnHandleCreated(EventArgs e)
    {
        base.OnHandleCreated(e);
        // When our main window is ready for messages, add the MessageFilter
        Application.AddMessageFilter(this);
        // ...and start the timer for the first time.
        TimeOutState = TimeOutState.WakeUp;
    }
    
  2. 我们需要实例化定时器,但在CTor中只需要一次。

    public Form1()
    {
        InitializeComponent();
        _wdt = new Timer();            
        _wdt.Interval = 5000; // Use a very short time-out for this demo
        _wdt.Tick += _wdt_Tick;
    }
    Timer _wdt; // Watch-Dog Timer
    
  3. 我们需要处理Timer.Tick。

    private void _wdt_Tick(object sender, System.EventArgs e)
    {
        // A tick reduces the TimeOutState by 1
        TimeOutState = (TimeOutState)(TimeOutState - 1);
    }
    
  4. 最后,处理TimeOutState的状态变化,并显示我们的消息。

    TimeOutState TimeOutState
    {
        get => _timeOutState;
        set
        {
            switch (value)
            {
                case TimeOutState.WakeUp:
                    _wdt.Stop();
                    _wdt.Start();
                    break;
                case TimeOutState.Exit:
                    _wdt.Stop();
                    Application.Exit();
                    return;
            }
            if (value != _timeOutState)  // If state changes, write message
            {
                Debug.WriteLine(value.ToString(), _timeOutState.ToString());
                // In a timer callback that changes the UI, it's
                // best to post the action in the message queue.
                BeginInvoke((MethodInvoker)delegate
                {
                    textBox1.AppendText(_timeOutState.ToString());
                    if (TimeOutState == TimeOutState.Warning)
                    {
                        textBox1.AppendText(
                            ": Closing in " + (_wdt.Interval / 1000).ToString() + " seconds.");
                    }
                    textBox1.AppendText(Environment.NewLine);
                    textBox1.Select(textBox1.TextLength, 0);
                });
            }
            _timeOutState = value;
        }
    }
    TimeOutState _timeOutState = (TimeOutState)(-1);    // Initialize to invalid state
    

我使用了 IMessageFilter 在我自己的应用程序中非常可靠,我有信心向你建议,作为回答你的帖子的一个选择。

另一答案

我不会深入研究你的代码,但我想直接解决这个问题。我认为在这种情况下,一个 "迂回 "的方法是可行的,例如,你可以在鼠标移动时检查并与初始位置进行比较。

例如,你可以在鼠标移动时检查并与初始位置进行比较。

在Initialize Component()上面添加以下内容。

     GlobalMouseHandler gmh = new GlobalMouseHandler();
     gmh.TheMouseMoved += new MouseMovedEvent(gmh_TheMouseMoved);
     Application.AddMessageFilter(gmh);

然后添加这个。

  void gmh_TheMouseMoved()
  {
     if(XY==false)
     {
        MouseX = Convert.ToInt32(Cursor.Position.X);
        MouseY = Convert.ToInt32(Cursor.Position.Y);
     }
    else
    {
        MouseX1 = Convert.ToInt32(Cursor.Position.X);
        MouseY1 = Convert.ToInt32(Cursor.Position.Y);
        XY = true;
        if(MouseX1==MouseX && MouseY1==MouseY)
        {
            if(yourTimerNameHere.Enabled==false)
            {
                yourTimerNameHere.Start();
            }                
        }
        else
        {
            yourTimerNameHere.Stop();
            yourTimerNameHere.Start();
        }
    }

  }

在你的窗体类之外添加这个。

public delegate void MouseMovedEvent();

public class GlobalMouseHandler : IMessageFilter
{
   private const int WM_MOUSEMOVE = 0x0200;

  public event MouseMovedEvent TheMouseMoved;

  public bool PreFilterMessage(ref Message m)
  {
     if (m.Msg == WM_MOUSEMOVE)
     {
        if (TheMouseMoved != null)
        {
           TheMouseMoved();
        }
     }
     return false;
  }

}

接下来创建4个ints,分别命名为MouseX=0、MouseY=0、MouseX1=0和MouseY1=0,以及一个bool XY=false。

所以实际上,每当光标移动时,位置就会被记录下来,并与下一个位置进行比较。所以你可以检查鼠标是否空闲!

请注意,我还没有测试过这段代码,所以如果有任何错误,请随时回访。

以上是关于如何在Windows应用程序中设置用户空闲或不活动时的定时器。的主要内容,如果未能解决你的问题,请参考以下文章

如何防止Windows进入空闲状态?

Windows 应用程序中的系统空闲检测

如何在选项卡式活动中设置片段从右到左开始

如何在 Windows 中设置文件夹权限?

如何在导航栏中设置活动指示器?

如何在不是 Activity 的简单类中设置位置