web-dev-qa-db-ja.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
    }
}

コードを実装しましたが、それが正しい方法であるかどうかは不明です。

どんなリードもいただければ幸いです。投稿とSTAY SAFEをご利用いただきありがとうございます。

2

Q:「マウスイベントにタイマーを設定する必要があります...ユーザーが何か動きをした場合、タイマーをリセットする必要があります...どんなリードでもありがたいです。」

A:役立つと思われるいくつかのリードを提供するように努めます。 MouseMoveイベントでタイマーをリセットしたいが、問題がある:子コントロールにフォーカスがあるときは常に、マウスイベントを受け取るのは子であり、メインフォームはそうしません。 これは修正可能です。

短い答え:「メインウィンドウクラスにIMessageFilterインターフェイスを実装して、マウスの動きが検出されたときにタイマーがリセットされるようにします。」 MessageFilterを追加すると、フォーカスされたコントロールに送信される前にマウスメッセージをインターセプトできます

だから、私はあなたにすべての詳細を与えなければならないので、ここで長い答えがあります:それはこのようにメインのForm1にIMessageFilterインターフェースを追加することから始まります:

public partial class Form1 : Form, IMessageFilter

IMessageFilterには、クラスに1つのメソッドのみを実装する必要があります。

    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 }

必要なタイマーは1つだけで、タイマーのティック間隔(ここでは5秒に設定)ごとに状態を1つ減らします。マウスが動かない場合は、マウスを下に下げて、最後に終了します。

60秒 video アプリを60秒間実行する例を示します。 5秒ごとに、またはマウスを動かすと、変化が発生することがわかります。サンプルを実行したい場合は、GitHubリポジトリから clone 最新のコミットを実行できます。

残りの詳細は次のとおりです。

  1. MessageFilterを接続する必要があります。フォームにはウィンドウハンドルが必要なので、ここでこれを実行してタイマーを開始します。

    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を非常に確実に使用しており、あなたの投稿に答えるための1つの代替手段としてそれを提案すると確信しています。

1
IVSoftware

私はあなたのコードを深く掘り下げることはしませんが、問題に直接アプローチしたいと思います。この場合、「回り道」がうまくいくと思います。

たとえばマウスが動くたびに確認し、初期位置と比較できます。

これを上記の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;
  }

}

次に、MouseX = 0、MouseY = 0、MouseX1 = 0、MouseY1 = 0、bool XY = falseという名前の4つのintを作成します。

したがって、実際には、カーソルが移動するたびに、位置が記録され、次の位置と比較されます。したがって、マウスがアイドル状態かどうかを確認できます!

Plsはこのコードをテストしていないので、エラーが発生した場合は元に戻してください。

0
D J