我正在尝试创建一种雷达。 Radar 是由 360 DrawingVisual(代表雷达波束)组成的 VisualCollection。雷达放置在 Viewbox 上。
class Radar : FrameworkElement
{
private VisualCollection visuals;
private Beam[] beams = new Beam[BEAM_POSITIONS_AMOUNT]; // all geometry calculation goes here
public Radar()
{
visuals = new VisualCollection(this);
for (int beamIndex = 0; beamIndex < BEAM_POSITIONS_AMOUNT; beamIndex++)
{
DrawingVisual dv = new DrawingVisual();
visuals.Add(dv);
using (DrawingContext dc = dv.RenderOpen())
{
dc.DrawGeometry(Brushes.Black, null, beams[beamIndex].Geometry);
}
}
DrawingVisual line = new DrawingVisual();
visuals.Add(line);
// DISCRETES_AMOUNT is about 500
this.Width = DISCRETES_AMOUNT * 2;
this.Height = DISCRETES_AMOUNT * 2;
}
public void Draw(int beamIndex, Brush brush)
{
using (DrawingContext dc = ((DrawingVisual)visuals[beamIndex]).RenderOpen())
{
dc.DrawGeometry(brush, null, beams[beamIndex].Geometry);
}
}
protected override Visual GetVisualChild(int index)
{
return visuals[index];
}
protected override int VisualChildrenCount
{
get { return visuals.Count; }
}
}
每个 DrawingVisual 都为 DrawingContext.DrawGeometry(画笔、笔、几何)预先计算几何。 Pen 为空,画笔是具有大约 500 个 GradientStops 的 LinearGradientBrush。画笔每隔几毫秒更新一次,在这个例子中假设是 16 毫秒。这就是变得滞后的原因。这里是整体逻辑。
在 MainWindow() 构造函数中,我创建了雷达并启动了一个后台线程:
private Radar radar;
public MainWindow()
{
InitializeComponent();
radar = new Radar();
viewbox.Child = radar;
Thread t = new Thread(new ThreadStart(Run));
t.Start();
}
在 Run() 方法中有一个无限循环,其中生成随机画笔,调用 Dispatcher.Invoke() 并设置 16 ms 的延迟:
private int beamIndex = 0;
private Random r = new Random();
private const int turnsPerMinute = 20;
private static long delay = 60 / turnsPerMinute * 1000 / (360 / 2);
private long deltaDelay = delay;
public void Run()
{
int beginTime = Environment.TickCount;
while (true)
{
GradientStopCollection gsc = new GradientStopCollection(DISCRETES_AMOUNT);
for (int i = 1; i < Settings.DISCRETES_AMOUNT + 1; i++)
{
byte color = (byte)r.Next(255);
gsc.Add(new GradientStop(Color.FromArgb(255, 0, color, 0), (double)i / (double)DISCRETES_AMOUNT));
}
LinearGradientBrush lgb = new LinearGradientBrush(gsc);
lgb.StartPoint = Beam.GradientStarts[beamIndex];
lgb.EndPoint = Beam.GradientStops[beamIndex];
lgb.Freeze();
viewbox.Dispatcher.Invoke(new Action( () =>
{
radar.Draw(beamIndex, lgb);
}));
beamIndex++;
if (beamIndex >= BEAM_POSITIONS_AMOUNT)
{
beamIndex = 0;
}
while (Environment.TickCount - beginTime < delay) { }
delay += deltaDelay;
}
}
每个 Invoke() 调用都执行一件简单的事情:dc.DrawGeometry(),它在当前的 beamIndex 下重绘光束。然而,有时似乎像在 UI 更新之前一样,radar.Draw() 被调用了几次,而不是每 16 毫秒绘制 1 个波束,而是每 32-64 毫秒绘制 2-4 个波束。这令人不安。我真的很想实现流畅的运动。我需要一个光束来绘制每个确切的时间段。不是这种随机的东西。这是我到目前为止尝试过的列表(没有任何帮助):
我没有尝试的:
所以,就是这样。我在乞求帮助。
编辑:这些滞后与 PC 资源无关 - 没有延迟,雷达每秒可以完成大约 5 个完整的循环(移动速度非常快)。很可能是关于多线程/UI/Dispatcher 或其他我还没有理解的东西。
EDIT2:附加一个 .exe 文件,以便您查看实际情况:https://dl.dropboxusercontent.com/u/8761356/Radar.exe
EDIT3: DispatcherTimer(DispatcherPriority.Render) 也没有帮助。
最佳答案
对于流畅的 WPF 动画,您应该使用
CompositionTarget.Rendering
事件。
无需线程或与调度程序混淆。该事件将在每个新帧之前自动触发,类似于 HTML 的 requestAnimationFrame()
.
如果更新您的 WPF 场景,您就完成了!
有一个complete example可在 MSDN 上找到。
关于wpf - 如何实现每 16 毫秒流畅的 UI 更新?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23889294/