如何在WPF中创建渲染循环?
如何在WPF中消息循环空闲时创建一个连续执行的循环?
这里的目标是执行一些长时间运行的图形更新,例如刷新PicktureBox
,它能够消耗任何可用的免费资源,但不应该冻结UI或以其他方式优先于消息队列中的任何其他操作。
我注意到这篇博客文章提供了在winforms应用程序中执行此操作的代码,但我不知道如何将其转换为WPF应用程序。 下面是我根据另一篇文章制作的WinForms渲染循环类的代码:
using System; using System.Runtime.InteropServices; using System.Threading; using System.Windows.Forms; namespace Utilities.UI { /// /// WinFormsAppIdleHandler implements a WinForms Render Loop (max FPS possible). /// Reference: http://blogs.msdn.com/b/tmiller/archive/2005/05/05/415008.aspx /// 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; } /// /// Singleton from: /// http://csharpindepth.com/Articles/General/Singleton.aspx /// private static readonly Lazy lazy = new Lazy(() => new WinFormsAppIdleHandler()); public static WinFormsAppIdleHandler Instance { get { return lazy.Value; } } /// /// Gets or sets if must fire ApplicationLoopDoWork event. /// public bool Enabled { get; set; } /// /// Gets or sets the minimum time betwen ApplicationLoopDoWork fires. /// public int SleepTime { get; set; } /// /// Fires while the UI is free to work. Sleeps for "SleepTime" ms. /// 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; } } /// /// FINALMENTE! Imagem ao vivo sem travar! Muito bom! /// /// /// 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); } } /// /// Gets if the app still idle. /// /// 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 } }
执行此操作的最佳方法是使用静态CompositionTarget.Rendering
事件提供的每帧回调。