Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
240 views
in Technique[技术] by (71.8m points)

c# - How to make a render loop in WPF?

How can I create a loop that will be continuously executed whenever the message loop is idle in WPF?

The goal here is to perform some long running graphical update, such as refreshing a PicktureBox, that is capable of consuming whatever free resources are available but shouldn't freeze the UI or otherwise take priority over any other operations in the message queue.

I noticed this blog post which provides the code to do this in a winforms application, but I don't know how to translate it to a WPF application. Below is the code of a WinForms render loop class that I made based on the other article:

using System;
using System.Runtime.InteropServices;
using System.Threading;
using System.Windows.Forms;

namespace Utilities.UI
{
    /// <summary>
    /// WinFormsAppIdleHandler implements a WinForms Render Loop (max FPS possible).
    /// Reference: http://blogs.msdn.com/b/tmiller/archive/2005/05/05/415008.aspx
    /// </summary>
    public sealed class WinFormsAppIdleHandler
    {
        private readonly object _completedEventLock = new object();
        private event EventHandler _applicationLoopDoWork;

        //PRIVATE Constructor
        private WinFormsAppIdleHandler()
        {
            Enabled = false;
            SleepTime = 10;
            Application.Idle += Application_Idle;
        }

        /// <summary>
        /// Singleton from:
        /// http://csharpindepth.com/Articles/General/Singleton.aspx
        /// </summary>
        private static readonly Lazy<WinFormsAppIdleHandler> lazy = new Lazy<WinFormsAppIdleHandler>(() => new WinFormsAppIdleHandler());
        public static WinFormsAppIdleHandler Instance { get { return lazy.Value; } }

        /// <summary>
        /// Gets or sets if must fire ApplicationLoopDoWork event.
        /// </summary>
        public bool Enabled { get; set; }

        /// <summary>
        /// Gets or sets the minimum time betwen ApplicationLoopDoWork fires.
        /// </summary>
        public int SleepTime { get; set; }

        /// <summary>
        /// Fires while the UI is free to work. Sleeps for "SleepTime" ms.
        /// </summary>
        public event EventHandler ApplicationLoopDoWork
        {
            //Reason of using locks:
            //http://stackoverflow.com/questions/1037811/c-thread-safe-events
            add
            {
                lock (_completedEventLock)
                    _applicationLoopDoWork += value;
            }

            remove
            {
                lock (_completedEventLock)
                    _applicationLoopDoWork -= value;
            }
        }

        /// <summary>
        /// FINALMENTE! Imagem ao vivo sem travar! Muito bom!
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Application_Idle(object sender, EventArgs e)
        {
            //Try to update interface
            while (Enabled && IsAppStillIdle())
            {
                OnApplicationIdleDoWork(EventArgs.Empty);
                //Give a break to the processor... :)
                //8 ms -> 125 Hz
                //10 ms -> 100 Hz
                Thread.Sleep(SleepTime);
            }
        }

        private void OnApplicationIdleDoWork(EventArgs e)
        {
            var handler = _applicationLoopDoWork;
            if (handler != null)
            {
                handler(this, e);
            }
        }

        /// <summary>
        /// Gets if the app still idle.
        /// </summary>
        /// <returns></returns>
        private static bool IsAppStillIdle()
        {
            bool stillIdle = false;
            try
            {
                Message msg;
                stillIdle = !PeekMessage(out msg, IntPtr.Zero, 0, 0, 0);
            }
            catch (Exception e)
            {
                //Should never get here... I hope...
                MessageBox.Show("IsAppStillIdle() Exception. Message: " + e.Message);
            }
            return stillIdle;
        }

        #region  Unmanaged Get PeekMessage
        // http://blogs.msdn.com/b/tmiller/archive/2005/05/05/415008.aspx
        [System.Security.SuppressUnmanagedCodeSecurity] // We won't use this maliciously
        [DllImport("User32.dll", CharSet = CharSet.Auto)]
        public static extern bool PeekMessage(out Message msg, IntPtr hWnd, uint messageFilterMin, uint messageFilterMax, uint flags);

        #endregion
    }
}
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

The best way to do this is to use the per-frame callbacks provided by the static CompositionTarget.Rendering event.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...