应用程序空闲
一个WinForms组件,用于在应用程序空闲达到指定时间间隔时发出通知。
目的
我最近需要为应用程序添加一个自动、定时用户注销功能。我找不到任何内置组件,于是我“谷歌”了一下。我找到了一些建议,但唯一可行的都是基于系统范围的活动,而不是仅限于当前应用程序的活动,所以我开始编写自己的组件。我想要的是,我应用程序中的任何键盘输入或鼠标移动都能让用户保持登录状态,但如果预定义的 فترة 内没有任何活动被检测到,则用户将被自动注销。
被否决的思路
.NET有一个看起来很有前途的事件,`System.Windows.Forms.Application.Idle`。我曾以为结合一个计时器就能解决问题,但它不适合,因为无法控制是什么中断了它。例如,计时器本身就会导致空闲状态的退出,这当然是合乎逻辑的,但却不适用于这种情况。接下来显而易见的做法是处理主窗体上的键盘和鼠标事件。我也否决了这种方法,因为它需要为所有控件等单独处理这些事件……这是一个维护的噩梦。
解决方案
最终,我选择使用`IMessageFilter`的`PreFilterMessage`方法,不是为了实际过滤任何消息,而是为了接收我感兴趣的消息的通知。结合一个计时器,这提供了我所需的功能。为了方便起见,我已经将这些封装到一个组件中,您可以将其拖到应用程序的主窗体上。
组件
ApplicationIdle
组件非常简单易用。文档在下方,但我会在下面介绍基础知识。
第二个版本增加了原始版本的更多功能,但可以使用相同的方式。将其拖到您的窗体上,它将出现在窗体下方的组件栏中(或者直接在您的代码中创建一个实例)。根据需要设置IdleTime,订阅Idle事件,并调用Start方法。当应用程序在您指定的时间内没有鼠标或键盘活动时,将引发您的Idle事件处理方法。
代码
代码围绕一个`TimeSpan _TimeRemaining`构建,该`TimeSpan`在`System.Windows.Forms.Timer`计时器的每次滴答时递减。
Start
方法将剩余时间设置为`IdleTime`的值,注册组件以接收应用程序Windows消息,并启动内部计时器。
public void Start()
{
if (!_IsRunning)
{
_TimeRemaining = _IdleTime;
_TimeElapsed = ZeroTime;
Application.AddMessageFilter(this);
timer.Start();
_IsRunning = true;
OnStartedAsync(EventArgs.Empty);
OnStarted(EventArgs.Empty);
}
}
您会注意到在此方法末尾调用了两个`OnEvent`方法。所有行为事件都有两个版本——一个标准的同步版本和一个异步版本。这是为了响应对第一个版本的评论。
通常,我们同步引发事件。换句话说,事件在所有订阅者完成其处理程序中的工作之前不会返回。如果您决定在事件处理程序中执行一些耗时的操作,它将阻塞组件,这对于像这样的基于时间的组件来说并不理想。答案是在单独的线程上异步引发事件。缺点是,如果事件处理程序需要更新UI,它必须正确处理多线程。例如,它将阻塞组件,这对于像这样的基于时间的组件来说并不理想。答案是在单独的线程上异步引发事件。缺点是,如果事件处理程序需要更新UI,它必须正确处理多线程。
这是一个如何处理`StartedAsync`事件的示例。
void applicationIdle_StartedAsync(object sender, EventArgs e)
{
BeginInvoke(new MethodInvoker(
delegate() { applicationIdle_Started(sender, e); })
);
}
void applicationIdle_Started(object sender, EventArgs e)
{
// do stuff here
}
(更多信息请参阅我的Events Made Simple文章。)
收到消息时,我们只需要响应键盘和鼠标消息。我们感兴趣的消息在`ActivityMessages`枚举中定义。我们所要做的就是检查消息是否已定义并作出相应反应。无论消息是什么,我们都返回false
,表示我们实际上没有处理该消息。
bool IMessageFilter.PreFilterMessage(ref Message m)
{
if (Enum.IsDefined(typeof(ActivityMessages), m.Msg))
{
_TimeRemaining = _IdleTime;
_TimeElapsed = ZeroTime;
ActivityEventArgs e = new ActivityEventArgs((ActivityMessages)m.Msg);
OnActivityAsync(e);
OnActivity(e);
}
return false;
}
每次内部计时器滴答时,我们都会减少剩余时间并检查是否为零。如果是,则引发空闲事件并停止组件。
private void timer_Tick(object sender, EventArgs e)
{
_TimeElapsed = _TimeElapsed.Add(_TickInterval);
_TimeRemaining = _TimeRemaining.Subtract(_TickInterval);
// ...
if (_TimeRemaining == ZeroTime)
{
OnIdleAsync(EventArgs.Empty);
OnIdle(EventArgs.Empty);
Stop();
}
}
当我们停止组件时,我们需要停止计时器,重置一些属性,并取消注册应用程序消息。
public void Stop()
{
if (_IsRunning)
{
timer.Stop();
_TimeRemaining = ZeroTime;
_TimeElapsed = ZeroTime;
_IsRunning = false;
_IsPaused = false;
Application.RemoveMessageFilter(this);
OnStoppedAsync(EventArgs.Empty);
OnStopped(EventArgs.Empty);
}
}
演示
我包含了一个简单的演示应用程序来展示该组件的用法。使用菜单或F12“登录”。在应用程序内移动/单击鼠标或按键将重置计数器。将其保持空闲状态直到Remaining显示00:00:00,您将被自动注销。
结论
这就是基础知识的概述,可能您只需要这些。下一节将概述所有涉及类的所有属性、事件和方法。
Application Idle项目文档
该项目是以下五个文件的类库。这是更大组件库的一部分。它构建为Winforms.Components.dll,组件位于Winforms.Components
命名空间中。其他相关类位于Winforms.Components.ApplicationIdleData
命名空间中。
ApplicationIdle
WinForms组件,用于确定应用程序是否在指定TimeSpan
内未接收到任何定义的ActivityMessages。
属性
设计器可见属性
IdleTime
TickInterval
WarnSetting
WarnTime
一个
TimeSpan
,在此之后,如果未接收到任何定义的ActivityMessages,应用程序将被视为空闲。组件“滴答”的
TimeSpan
。每次滴答时都会检查空闲状态并引发相应的事件。用于控制警告事件生成的WarnSettings值。
根据WarnSetting生成警告事件的
TimeSpan
。其他编辑器可见属性(只读)
IsPaused
IsRunning
TimeElapsed
TimeRemaining
指示组件当前是否已暂停。
指示组件当前是否正在运行。
一个
TimeSpan
,表示自上次检测到活动以来的时间。一个
TimeSpan
,表示假设没有检测到活动,距离空闲状态还有多长时间。事件
属性更改事件
当相应属性发生更改时,会引发所有属性更改事件。
IdleTimeChanged
TickIntervalChanged
WarnSettingChanged
WarnTimeChanged
同步行为事件
下面列出的所有同步事件都有一个异步对应版本。
Activity
Idle
[默认]Paused
Started
Stopped
Tick
UnPaused
Warn(警告)
当组件检测到ActivityMessages中定义的活动时引发。
达到IdleTime时引发。
组件暂停时引发。
组件启动时引发。
组件停止时引发。
组件“滴答”时引发。
组件取消暂停时引发。
当WarnTime达到时,并且在后续的每个Tick时(取决于WarnSetting),可能会引发此事件。
异步行为事件
有关这些事件的描述,请参阅上面的同步行为事件部分。
ActivityAsync
IdleAsync
PausedAsync
StartedAsync
StoppedAsync
TickAsync
UnPausedAsync
WarnAsync
参见Activity。
参见Idle。
参见Paused。
参见Started。
参见Stopped。
参见Tick。
参见UnPaused。
参见Warn。
方法
没有方法接受任何参数。
GetVersion
Restart
Start
停止
TogglePause
获取组件的程序集版本。
停止然后启动组件。
启动组件。
停止组件。
切换组件的暂停状态。
注释
设置IdleTime会强制重新计算TickInterval和WarnTime。
仅当相关属性按以下顺序更改时,才会引发事件。
设置TickInterval会强制重新计算WarnTime。
仅当相关属性按以下顺序更改时,才会引发事件
TickInterval被强制为IdleTime的因子。
WarnTime被强制为TickInterval的倍数,并且是IdleTime的因子。
IdleTime和TickInterval只能在组件未运行时设置。
如果组件未运行,Stop、Restart和TogglePause将不起作用。
当组件“滴答”时,可能会发生几个事件。它们将按以下顺序引发(如果适用)。
ActivityMessages
这是一个简单的枚举,包含我们感兴趣的Windows消息(源自winuser.h头文件)。
ActivityEventArgs
此类派生自System.EventArgs
,并具有以下属性。它用于Activity和ActivityAsync事件。
ActivityMessage
一个ActivityMessages成员,指示检测到的活动。
TickEventArgs
此类派生自System.EventArgs
,并具有以下属性。它用于Tick和TickAsync事件。
IsWarnPeriod
一个
Boolean
,指示TimeRemaining是否小于或等于WarnTime,并且WarnSetting不设置为WarnSettings.Off。WarnSettings
用于WarnSetting属性的枚举。
历史
- 2008年10月22日:初始版本
- 2009年3月1日:版本2