WPF:在应用程序处于非活动状态时创建一个屏幕保护程序特性

本文关键字:一个 屏幕保护 程序 创建 应用程序 于非 活动状态 WPF | 更新日期: 2023-09-27 17:52:42

我正在为kiosk编写一个WPF应用程序。亭子将被闲置很长一段时间,所以我想创建一个"屏幕保护"类似的功能,在那里我可以显示一些促销幻灯片,然后当一个人触摸屏幕并启动应用程序时被打断。我该怎么做呢?

WPF:在应用程序处于非活动状态时创建一个屏幕保护程序特性

我不知道是否有什么原生的东西,但你可以做。

  • 打开一个没有边框的窗口
  • 将其置于屏幕顶部并将其最大化(全屏)。
  • 在其中运行视频(或任何类型的媒体/动画)。(您的宣传幻灯片)
  • 检查是否有人按任何键、触摸屏幕或移动鼠标关闭屏幕。

不要忘记添加一个定时器来启动它。

顺便说一下,我刚刚谷歌了一下你的问题,发现了很多例子:

  • WPFScreensaver CodePlex上
  • WPF中的基本屏幕保护程序
  • WPF屏保Visual Studio模板
  • WPF自定义屏幕保护程序美术
  • 制作c#屏保
  • 使用c#创建屏保

听起来像屏幕保护程序会做你想做的(就像其他人说的),但你要求一个屏幕保护程序的功能。我假设您不需要帮助来制作窗口和显示幻灯片。可能更像是什么时候展示。

几年前,我为一个客户端写了一个应用程序(WPF),它需要跟踪用户在应用程序中什么时候在做什么,什么时候没有在做什么。谷歌(搜索——不是谷歌本身),我想出了下面这些。我希望它对你有用。使用它:

AppActivityTimer activityTimer; // In my app, this is a member variable of the main window
activityTimer = new AppActivityTimer(
     30*1000,   // plain timer, going off every 30 secs - not useful for your question
     5*60*1000, // how long to wait for no activity before firing OnInactive event - 5 minutes
     true);     // Does mouse movement count as activity?
activityTimer.OnInactive += new EventHandler(activityTimer_OnInactive);
activityTimer.OnActive += new PreProcessInputEventHandler(activityTimer_OnActive);
activityTimer.OnTimePassed += new EventHandler(activityTimer_OnTimePassed);
void activityTimer_OnTimePassed(object sender, EventArgs e)
{
     // Regular timer went off
}
void activityTimer_OnActive(object sender, PreProcessInputEventArgs e)
{
     // Activity detected (key press, mouse move, etc) - close your slide show, if it is open
}
void activityTimer_OnInactive(object sender, EventArgs e)
{
     // App is inactive - activate your slide show - new full screen window or whatever
     // FYI - The last input was at:
     // DateTime idleStartTime = DateTime.Now.Subtract(activityTimer.InactivityThreshold);
}

和AppActivityTimer类本身:

public class AppActivityTimer
{
    #region Events - OnActive, OnInactive, OnTimePassed
    public event System.Windows.Input.PreProcessInputEventHandler OnActive;
    public event EventHandler OnInactive;
    public event EventHandler OnTimePassed;
    #endregion
    #region TimePassed
    private TimeSpan _timePassed;
    private DispatcherTimer _timePassedTimer;
    #endregion
    #region Inactivity
    public TimeSpan InactivityThreshold { get; private set; }
    private DispatcherTimer _inactivityTimer;
    private Point _inactiveMousePosition = new Point(0, 0);
    private bool _MonitorMousePosition;
    #endregion
    #region Constructor
    /// <summary>
    /// Timers for activity, inactivity and time passed.
    /// </summary>
    /// <param name="timePassedInMS">Time in milliseconds to fire the OnTimePassed event.</param>
    /// <param name="IdleTimeInMS">Time in milliseconds to be idle before firing the OnInactivity event.</param>
    /// <param name="WillMonitorMousePosition">Does a change in mouse position count as activity?</param>
    public AppActivityTimer(int timePassedInMS, int IdleTimeInMS, bool WillMonitorMousePosition)
    {
        _MonitorMousePosition = WillMonitorMousePosition;
        System.Windows.Input.InputManager.Current.PreProcessInput += new System.Windows.Input.PreProcessInputEventHandler(OnActivity);
        // Time Passed Timer
        _timePassedTimer = new DispatcherTimer();
        _timePassed = TimeSpan.FromMilliseconds(timePassedInMS);
        // Start the time passed timer
        _timePassedTimer.Tick += new EventHandler(OnTimePassedHandler);
        _timePassedTimer.Interval = _timePassed;
        _timePassedTimer.IsEnabled = true;
        // Inactivity Timer
        _inactivityTimer = new DispatcherTimer();
        InactivityThreshold = TimeSpan.FromMilliseconds(IdleTimeInMS);
        // Start the inactivity timer
        _inactivityTimer.Tick += new EventHandler(OnInactivity);
        _inactivityTimer.Interval = InactivityThreshold;
        _inactivityTimer.IsEnabled = true;
    }
    #endregion
    #region OnActivity
    void OnActivity(object sender, System.Windows.Input.PreProcessInputEventArgs e)
    {
        System.Windows.Input.InputEventArgs inputEventArgs = e.StagingItem.Input;
        if (inputEventArgs is System.Windows.Input.MouseEventArgs || inputEventArgs is System.Windows.Input.KeyboardEventArgs)
        {
            if (inputEventArgs is System.Windows.Input.MouseEventArgs)
            {
                System.Windows.Input.MouseEventArgs mea = inputEventArgs as System.Windows.Input.MouseEventArgs;
                // no button is pressed and the position is still the same as the application became inactive
                if (mea.LeftButton == System.Windows.Input.MouseButtonState.Released &&
                    mea.RightButton == System.Windows.Input.MouseButtonState.Released &&
                    mea.MiddleButton == System.Windows.Input.MouseButtonState.Released &&
                    mea.XButton1 == System.Windows.Input.MouseButtonState.Released &&
                    mea.XButton2 == System.Windows.Input.MouseButtonState.Released &&
                    (_MonitorMousePosition == false ||
                        (_MonitorMousePosition == true && _inactiveMousePosition == mea.GetPosition(Application.Current.MainWindow)))
                    )
                    return;
            }
            // Reset idle timer
            _inactivityTimer.IsEnabled = false;
            _inactivityTimer.IsEnabled = true;
            _inactivityTimer.Stop();
            _inactivityTimer.Start();
            if (OnActive != null)
                OnActive(sender, e);
        }
    }
    #endregion
    #region OnInactivity
    void OnInactivity(object sender, EventArgs e)
    {
        // Fires when app has gone idle
        _inactiveMousePosition = System.Windows.Input.Mouse.GetPosition(Application.Current.MainWindow);
        _inactivityTimer.Stop();
        if (OnInactive != null)
            OnInactive(sender, e);
    }
    #endregion
    #region OnTimePassedHandler
    void OnTimePassedHandler(object sender, EventArgs e)
    {
        if (OnTimePassed != null)
            OnTimePassed(sender, e);
    }
    #endregion
}

我相信显示屏保是没有问题的。可以简单地在所有内容的顶部显示一个黑色窗口:

<Window WindowStyle="None" WindowState="Maximized" Background="Black" Topmost="True" ... >

实际问题是何时显示这样的窗口。有很多可能性,其中一个我最喜欢的是利用winapi GetLastInputInfo方法,它返回ms自上次输入。使用优选的轮询方式(例如DispatcherTimer),如果时间超过阈值-显示窗口,否则-隐藏窗口。

当从c#处理winapi时,使用pinvoke.net来获取互操作信息,通常是现成的函数,例如GetLastInputTime。

下面是复制/粘贴的完整代码片段:

[DllImport("user32.dll")]
static extern bool GetLastInputInfo(ref LASTINPUTINFO plii);
[StructLayout(LayoutKind.Sequential)]
struct LASTINPUTINFO
{
    [MarshalAs(UnmanagedType.U4)]
    public UInt32 cbSize;
    [MarshalAs(UnmanagedType.U4)]
    public UInt32 dwTime;
}
// returns seconds since last input
long GetIdleTime()
{
    var info = new LASTINPUTINFO { cbSize = (uint)Marshal.SizeOf(typeof(LASTINPUTINFO)) };
    GetLastInputInfo(ref info);
    return (Environment.TickCount - info.dwTime) / 1000;
}

用法:

var timer = new DispatcherTimer { Interval = TimeSpan.FromSeconds(1), IsEnabled = true };
timer.Tick += (s, e) =>
{
    if (GetIdleTime() > 10*60) // 10 min
        ShowWindow();
    else
        HideWindow();
};