在WPF中无操作后返回

在很多时候,我们需要使用WPF来实现一些程序,例如展示程序。最近我就做了这样的程序。但是有一个问题,就是如果做展示程序(类似触摸屏的那种),那么有必要让程序在无操作后返回到一个类似屏保的界面(可以参考windows的屏幕保护或者自动锁定)。很遗憾,我没有在wpf中发现具有这样功能的类/函数,只能转向求助于windows API。

在windows api中(“user32.dll”),有一个名为“ GetLastInputInfo”的API,在MSDN上,它的解释是这样的:

This fundtion is useful for input idle detection. However, GetLastInputInfo does not provide system-wide user input information across all running sessions. Rather, GetLastInputInfo provides session-specific user input information for only the session thar invoked the function.

The tick count when the last input event was received (see LastInputInfo) is not guaranteed to be incremental. In some cases, the value might be less than the tick count of a prior event. For example, this can be caused by a timing gap between the raw input thread and the desktop thread or an event raised by SendInput, which supplies its own tick count.

上面第一段话的意思是,GetLastInputInfo并不提供全局(在整个桌面环境中)的输入信息,而是提供特定的会话的输入信息。

第二段的意思是,接收到的输入时间的tick count并不是完全准确的,在某些情况下,可能会比实际的值要小,这种情况可能会由纯输入线程和桌面线程之间的时间差,或者是由SendInput引起。

_注:由于个人水平有限,以上引语的翻译可能不准确,所以最好阅读原文。另外,以上内容不会随着其出处更新而更新,用时请参考其出处_

所以最终我选用这个GetLastInputInfo,在C#/WPF中写好引用即可。
下面是本功能的核心代码

using System.Runtime.InteropServices; // 如果需要调用外部的dll的话,这个引用是必要的
using System.Windows;
using System.Windows.Threading;  // 此处是为了使用WPF的DispatcherTimer,
                                                                 // 如果你需要使用其他的Timer,请自行using
namespace
{
    public class TestClass
    {
        private DispatcherTimer timer; // 我们需要使用的timer,在timer里面我们要检测上一次操作的信息
        private readonly static int defultIdleAllowed = 5; // 默认的允许idle时间(单位为s),在这个时间里,如果没有操作,那么就返回,或者执行其他的操作
        private static int currentIdleTimeRemained = defaultIdleAllowed; // 当前剩余的可以idle的时间,单位为s

        public TestClass()
        {
            timer = new DispatcherTimer();
            timer.Tick += new EventHandler(Timer_Tick);
            timer.Interval = new TimeSpan(0, 0, 1);
            timer.Start();
        }

        private void Timer_Tick(object sender, EventArgs e)
        {
            if(HasActionInLastSecond())
            {
                if(--currentIdleTimeRemained == 0)
                {
                    currentIdleTime = defaultIdleAllowed;
                    timer.Stop();

                    // 在这里你可以做一些类似返回主屏幕的操作

                    timer.Start();
                }
            }
            else
            {
                currentIdleTime = defaultIdleAllowed;
            }
        }

        /// <summary>
        /// 未操作时间超过1s才开始计数
        /// </summary>
        /// <returns>bool,表示未操作时间是否超过1s</returns>
        private bool HasActionInLastSecond()
        {
            return GetIdleTime() / 1000 > 1;
        }

        /// <summary>
        /// 通过window api获得上次键鼠操作的时间,返回值为ms
        /// </summary>
        /// <param name = "pLastInputInfo">ref, 存放时间的一个结构体,在下文中会有定义</param>
        /// <returns></returns>
        [DllImport("user32.dll")]
        static extern bool GetLastInputInfo(ref PLASTINPUTINFO pLastInputInfo);

        private static long GetIdleTime()
        {
            PLASTINPUTINFO pLastInputInfo = new PLASTINPUTINFO;
            pLastInputInfo.cbSize = Marshal.SizeOf(pLastInputInfo);
            if(!GetLastInputInfo(ref pLastInputInfo))
            {
                return 0;
            }
            return Environment.TickCount- pLastInputInfo.dwTime;
        }

        // PLASTINPUTINFO结构体定义
        [StructLayout(LayoutKind.Sequential)]
        private struct PLASTINPUTINFO
        {
            [MarshalAs(UnmanagedType.U4)]
            public int cbSize;
            [MarshalAs(UnmanagedType.U4)]
            public uint dwTime;
        }
分享到: