Wpf 如何确定MVVM应用程序中的非活动性?
我有一个MVVM kiosk应用程序,当它在一段时间内处于非活动状态时,我需要重新启动它。我使用Prism和Unity来简化MVVM模式。我已经停止了重新启动,我甚至知道如何处理计时器。我想知道的是如何知道活动(即任何鼠标事件)发生的时间。我知道的唯一方法是订阅主窗口的预览鼠标事件。这打破了MVVM的想法,不是吗 我曾考虑将我的窗口作为一个接口公开给我的应用程序,但这需要窗口实现该接口,该接口似乎也破坏了MVVM。您可以使用EventToCommand行为将MouseMove/MouseLeftButtonDown事件链接到一个命令。这通常是在混合中完成的,因为它非常简单 如果您没有blend,下面是一些xaml示例:Wpf 如何确定MVVM应用程序中的非活动性?,wpf,mvvm,event-hooking,Wpf,Mvvm,Event Hooking,我有一个MVVM kiosk应用程序,当它在一段时间内处于非活动状态时,我需要重新启动它。我使用Prism和Unity来简化MVVM模式。我已经停止了重新启动,我甚至知道如何处理计时器。我想知道的是如何知道活动(即任何鼠标事件)发生的时间。我知道的唯一方法是订阅主窗口的预览鼠标事件。这打破了MVVM的想法,不是吗 我曾考虑将我的窗口作为一个接口公开给我的应用程序,但这需要窗口实现该接口,该接口似乎也破坏了MVVM。您可以使用EventToCommand行为将MouseMove/MouseLeft
<Grid>
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseLeftButtonDown">
<GalaSoft_MvvmLight_Command:EventToCommand Command="{Binding theCommand} />
</i:EventTrigger>
</i:Interaction.Triggers>
</Grid>
我知道的唯一方法是订阅主窗口的预览鼠标事件。这打破了MVVM的想法,不是吗
这真的取决于你怎么做
您可以非常轻松地编写一个行为或附加属性,将其挂接到该事件中,并使用它在ViewModel中触发ICommand。这样,您基本上就是将“发生了什么”事件推送到VM,在VM中您可以在业务逻辑中完全处理此事件。另一个选项是使用Windows API方法
一些洞穴
- 我假设Windows是因为它是WPF
- 检查您的信息亭是否支持GetLastInputInfo
- 我对MVVM一无所知。这个方法使用了一种与用户界面无关的技术,所以我认为它对您是可行的
用法很简单。调用UserIdleMonitor.RegisterForNotification。传入通知方法和时间跨度。如果用户活动发生,然后在指定的时间段内停止,则调用通知方法。您必须重新注册才能获得另一个通知,并且可以随时取消注册。如果在49.7天(加上idlePeriod)内没有活动,则将调用通知方法
public static class UserIdleMonitor
{
static UserIdleMonitor()
{
registrations = new List<Registration>();
timer = new DispatcherTimer(TimeSpan.FromSeconds(1.0), DispatcherPriority.Normal, TimerCallback, Dispatcher.CurrentDispatcher);
}
public static TimeSpan IdleCheckInterval
{
get { return timer.Interval; }
set
{
if (Dispatcher.CurrentDispatcher != timer.Dispatcher)
throw new InvalidOperationException("UserIdleMonitor can only be used from one thread.");
timer.Interval = value;
}
}
public sealed class Registration
{
public Action NotifyMethod { get; private set; }
public TimeSpan IdlePeriod { get; private set; }
internal uint RegisteredTime { get; private set; }
internal Registration(Action notifyMethod, TimeSpan idlePeriod)
{
NotifyMethod = notifyMethod;
IdlePeriod = idlePeriod;
RegisteredTime = (uint)Environment.TickCount;
}
}
public static Registration RegisterForNotification(Action notifyMethod, TimeSpan idlePeriod)
{
if (notifyMethod == null)
throw new ArgumentNullException("notifyMethod");
if (Dispatcher.CurrentDispatcher != timer.Dispatcher)
throw new InvalidOperationException("UserIdleMonitor can only be used from one thread.");
Registration registration = new Registration(notifyMethod, idlePeriod);
registrations.Add(registration);
if (registrations.Count == 1)
timer.Start();
return registration;
}
public static void Unregister(Registration registration)
{
if (registration == null)
throw new ArgumentNullException("registration");
if (Dispatcher.CurrentDispatcher != timer.Dispatcher)
throw new InvalidOperationException("UserIdleMonitor can only be used from one thread.");
int index = registrations.IndexOf(registration);
if (index >= 0)
{
registrations.RemoveAt(index);
if (registrations.Count == 0)
timer.Stop();
}
}
private static void TimerCallback(object sender, EventArgs e)
{
LASTINPUTINFO lii = new LASTINPUTINFO();
lii.cbSize = Marshal.SizeOf(typeof(LASTINPUTINFO));
if (GetLastInputInfo(out lii))
{
TimeSpan idleFor = TimeSpan.FromMilliseconds((long)unchecked((uint)Environment.TickCount - lii.dwTime));
//Trace.WriteLine(String.Format("Idle for {0}", idleFor));
for (int n = 0; n < registrations.Count; )
{
Registration registration = registrations[n];
TimeSpan registeredFor = TimeSpan.FromMilliseconds((long)unchecked((uint)Environment.TickCount - registration.RegisteredTime));
if (registeredFor >= idleFor && idleFor >= registration.IdlePeriod)
{
registrations.RemoveAt(n);
registration.NotifyMethod();
}
else n++;
}
if (registrations.Count == 0)
timer.Stop();
}
}
private static List<Registration> registrations;
private static DispatcherTimer timer;
private struct LASTINPUTINFO
{
public int cbSize;
public uint dwTime;
}
[DllImport("User32.dll")]
private extern static bool GetLastInputInfo(out LASTINPUTINFO plii);
}
公共静态类UserIdleMonitor
{
静态UserIdleMonitor()
{
注册=新列表();
计时器=新的调度程序(TimeSpan.FromSeconds(1.0),DispatcherPriority.Normal,TimerCallback,Dispatcher.CurrentDispatcher);
}
公共静态时间跨度IdleCheckInterval
{
获取{return timer.Interval;}
设置
{
if(Dispatcher.CurrentDispatcher!=timer.Dispatcher)
抛出新的InvalidOperationException(“UserIdleMonitor只能从一个线程使用”);
计时器。间隔=值;
}
}
加盖公章的班级注册
{
公共操作NotifyMethod{get;private set;}
公共时间跨度IdlePeriod{get;private set;}
内部uint RegisteredTime{get;private set;}
内部注册(操作notifyMethod、TimeSpan idlePeriod)
{
NotifyMethod=NotifyMethod;
IdlePeriod=IdlePeriod;
RegisteredTime=(uint)Environment.TickCount;
}
}
公共静态注册寄存器通知(操作notifyMethod,TimeSpan idlePeriod)
{
if(notifyMethod==null)
抛出新ArgumentNullException(“notifyMethod”);
if(Dispatcher.CurrentDispatcher!=timer.Dispatcher)
抛出新的InvalidOperationException(“UserIdleMonitor只能从一个线程使用”);
注册=新注册(notifyMethod,idlePeriod);
注册。添加(注册);
如果(registrations.Count==1)
timer.Start();
申报登记;
}
公共静态无效注销(注册)
{
if(注册==null)
抛出新异常(“注册”);
if(Dispatcher.CurrentDispatcher!=timer.Dispatcher)
抛出新的InvalidOperationException(“UserIdleMonitor只能从一个线程使用”);
int index=注册。IndexOf(注册);
如果(索引>=0)
{
注册。删除(索引);
如果(registrations.Count==0)
timer.Stop();
}
}
私有静态void TimerCallback(对象发送方,事件参数e)
{
LASTINPUTINFO lii=新的LASTINPUTINFO();
lii.cbSize=Marshal.SizeOf(typeof(LASTINPUTINFO));
if(GetLastInputInfo(out lii))
{
TimeSpan idleFor=TimeSpan.FromMillics((长)未选中((uint)Environment.TickCount-lii.dwTime));
//WriteLine(String.Format(“Idle for{0}”,idleFor));
对于(int n=0;n=idleFor&&idleFor>=registation.IdlePeriod)
{
注册。删除(n);
registration.NotifyMethod();
}
else n++;
}
如果(registrations.Count==0)
timer.Stop();
}
}
私有静态列表注册;
专用静态调度定时器;
私有结构LASTINPUTINFO
{
公共机构的规模;
公共时间;
}
[DllImport(“User32.dll”)]
私有外部静态bool GetLastInputInfo(out LASTINPUTINFO plii);
}
已更新
修复了如果尝试从通知方法重新注册可能会死锁的问题
修复了未签名的数学问题,并添加了未选中项
计时器处理程序中的轻微优化,仅在需要时分配通知
注释掉了d
public class UserIdleMonitor
{
private DispatcherTimer _timer;
private TimeSpan _timeout;
private DateTime _startTime;
public event EventHandler Timeout;
public UserIdleMonitor(TimeSpan a_timeout)
{
_timeout = a_timeout;
_timer = new DispatcherTimer(DispatcherPriority.Normal, Dispatcher.CurrentDispatcher);
_timer.Interval = TimeSpan.FromMilliseconds(100);
_timer.Tick += new EventHandler(timer_Tick);
}
public void Start()
{
_startTime = new DateTime();
_timer.Start();
}
public void Stop()
{
_timer.Stop();
}
private void timer_Tick(object sender, EventArgs e)
{
LASTINPUTINFO lii = new LASTINPUTINFO();
lii.cbSize = Marshal.SizeOf(typeof(LASTINPUTINFO));
if (GetLastInputInfo(out lii))
{
TimeSpan idleFor = TimeSpan.FromMilliseconds((long)unchecked((uint)Environment.TickCount - lii.dwTime));
TimeSpan aliveFor = TimeSpan.FromMilliseconds((long)unchecked((uint)Environment.TickCount - _startTime.Millisecond));
Debug.WriteLine(String.Format("aliveFor = {0}, idleFor = {1}, _timeout = {2}", aliveFor, idleFor, _timeout));
if (aliveFor >= idleFor && idleFor >= _timeout)
{
_timer.Stop();
if (Timeout != null)
Timeout.Invoke(this, EventArgs.Empty);
}
}
}
#region Win32 Stuff
private struct LASTINPUTINFO
{
public int cbSize;
public uint dwTime;
}
[DllImport("User32.dll")]
private extern static bool GetLastInputInfo(out LASTINPUTINFO plii);
#endregion
}