2011-02-10 14 views
28

私はWPFアプリケーションでユーザーの非アクティブとアクティビティを処理して、いくつかのものをフェードインまたはフェードアウトしようとしています。多くの研究の後、私は(少なくとも私の意見で)非常にエレガントなソリューションハンスパサントと一緒に行くことに決めたhereを投稿しました。WPFの非アクティブとアクティビティ

カーソルがウィンドウの上にとどまっている限り、PreProcessInputイベントは連続して発生します。私はフルスクリーンのアプリケーションを持っているので、これはそれを殺す。どのように私はこの動作を回避することができますすべてのアイデアは、最も感謝される。

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

更新

Iは、(上記のコードでrectangle1.Visibility更新を参照)より良好な説明行動を絞り込むことができました。カーソルがウィンドウの上に載っていて、たとえばコントロールのVisibilityが変更されている場合は、PreProcessInputが発生します。たぶん私はPreProcessInputイベントの目的と発生時を誤解しています。 MSDNはあまり役に立ちませんでした。

+0

そのコードは私にとってはうまく機能し、 'PreProcessInput'はマウスがまだ' Window'の上にあるときは生成されません。投稿したコードだけで小さなアプリを作成しても同じ効果が得られますか?どの.NETバージョンを使用していますか? –

+0

@Meleak:ありがとう!確かに、それはちょうど上記のコード(私の上に恥)で動作します。とにかく、私のプロジェクトでは、私はまだその変な振る舞いをしています。私はこれをさらに研究して絞り込み、より詳細な情報を提供します。完全性のために、.NET 4を使用しています。 –

+0

@Meleak:動作を実際に理解できるように質問を更新しました。 –

答えて

15

私は説明された行動の原因を突き止めることができました。例えば

制御のVisibilityが変更されたときに、PreProcessInputイベントはタイプInputReportEventArgsPreProcessInputEventArgs.StagingItem.Inputと上昇します。

挙動がOnActivityイベントに種類MouseEventArgsKeyboardEventArgsためInputEventArgsをフィルタリングすることにより回避することができ、何のマウスボタンが押されていないと、アプリケーションが非アクティブになったとして、カーソルの位置はまだ同じであるかどうかを確認します。

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

+1、お勧めします! –

+0

ViewModelからこれを使用していて、 'MainGrid'(またはフォーム要素)にアクセスできない場合は、マウス移動を検出する別の方法がありますか? – colmde

+0

上記の私のコメントへのフォローアップとして、 'if(e.StagingItem.Input is MouseMoveEventArgs)'ステートメント全体を削除しました。マウスを無視してマウスを無視しながら、クリックとマウスの動きを検出してくれました。応用。 – colmde

1

PreProcessInputを聞く代わりに、PreviewMouseMoveを試してみましたか?

+0

代替。唯一の問題は、 'PreProcessInput'のようなビューモデルから直接アクセスできないということです。代わりにあなたのアプリケーションの 'MainWindow'にバインドする必要があります。 –

36

私たちのソフトウェアにも同様のニーズがあります。これはWPFアプリケーションでもセキュリティ機能としても、クライアントはアイドル時にユーザーのログオフ時間を設定できます。

以下は、(Windowsの組み込み機能を利用した)Idle Detectionコードをラップするために作成したクラスです。

アイドル時間が指定されたしきい値...より大きいかどうかを確認するために1秒間だけタイマーティックがあります。CPUは0です。

まず、ここにコードを使用する方法は次のとおりです。

var idleTime = IdleTimeDetector.GetIdleTimeInfo(); 

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

あなたはこれを使用しても、あなたのWPFは、フルスクリーンアプリがあなたのニーズを実現するために「集中」であることを確認することができます:

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

この小さな美しさは私にコーディングの量を節約しました、ありがとう。関数に組み込まれたウィンドウを使用することは素晴らしい提案です。 – Digitalsa1nt

0

このソリューションはIdleDetectorクラスに実装されています。私はコードを少し改良しました。アイドル検出器は、迎撃可能なイジドルを投げる!それはそれを与える!私はいくつかのコメントを待つ。

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(); 
     } 
    } 
} 
関連する問題