[英]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?如何创建一个循环,只要 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.这里的目标是执行一些长时间运行的图形更新,例如刷新
PicktureBox
,它能够消耗任何可用的免费资源,但不应冻结 UI 或以其他方式优先于消息队列中的任何其他操作。
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.我注意到 这篇博客文章提供了在 winforms 应用程序中执行此操作的代码,但我不知道如何将其转换为 WPF 应用程序。 Below is the code of a WinForms render loop class that I made based on the other article:
下面是我根据另一篇文章制作的 WinForms 渲染循环类的代码:
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
}
}
最好的方法是使用静态CompositionTarget.Rendering
事件提供的每帧回调。
To elaborate a bit on the answer of Oren, you can attach a method to the Rendering event like this:为了详细说明 Oren 的答案,您可以将一个方法附加到 Rendering 事件,如下所示:
CompositionTarget.Rendering += Loop;
The Loop function can then update the properties of an element, in this example an element positioned on a Canvas:然后 Loop 函数可以更新一个元素的属性,在这个例子中是一个位于 Canvas 上的元素:
private void Loop(object sender, EventArgs e)
{
LeftPos++;
Canvas.SetLeft(ball, LeftPos);
}
https://docs.microsoft.com/en-us/dotnet/api/system.windows.media.compositiontarget.rendering?view=net-5.0 https://docs.microsoft.com/en-us/dotnet/api/system.windows.media.compositiontarget.rendering?view=net-5.0
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.