如何:使用 CompositionTarget 以每帧间隔呈现

WPF 动画引擎提供了许多用于创建基于帧的动画的功能。 但是,在某些应用程序方案中,需要基于每个帧以更细的粒度控制呈现。 使用 CompositionTarget 对象,可以基于每个帧回叫来创建自定义动画。

CompositionTarget 是一个静态类,表示应用程序要在其上进行绘制的显示图面。 每当绘制应用程序场景时,都会引发 Rendering 事件。 呈现帧速率是指每秒绘制场景的次数。

注意

有关使用 CompositionTarget 的完整代码示例,请参阅使用 CompositionTarget 示例

示例

Rendering 事件在 WPF 呈现过程中引发。 以下示例演示如何将 EventHandler 委托注册到 CompositionTarget 上的静态 Rendering 方法。

// Add an event handler to update canvas background color just before it is rendered.
CompositionTarget.Rendering += UpdateColor;
' Add an event handler to update canvas background color just before it is rendered.
AddHandler CompositionTarget.Rendering, AddressOf UpdateColor

可以使用呈现事件处理程序方法来创建自定义绘图内容。 每帧会调用一次此事件处理程序方法。 每次 WPF 将可视化树中的持久呈现数据封送到组合场景图中时,都会调用事件处理程序方法。 另外,如果对可视化树的更改强制更新组合场景图,也会调用事件处理程序方法。 请注意,计算布局后会调用事件处理程序方法。 但是,可以修改事件处理程序方法中的布局,这意味着在呈现之前将再计算一次布局。

以下示例演示如何在 CompositionTarget 事件处理程序方法中提供自定义绘图。 在本例中,Canvas 的背景色基于鼠标的坐标位置使用颜色值进行绘制。 如果在 Canvas 内移动鼠标,其背景色会更改。 另外,将基于当前的运行时间和所呈现的帧总数来计算平均帧速率。

// Called just before frame is rendered to allow custom drawing.
protected void UpdateColor(object sender, EventArgs e)
{
    if (_frameCounter++ == 0)
    {
        // Starting timing.
        _stopwatch.Start();
    }

    // Determine frame rate in fps (frames per second).
    long frameRate = (long)(_frameCounter / this._stopwatch.Elapsed.TotalSeconds);
    if (frameRate > 0)
    {
        // Update elapsed time, number of frames, and frame rate.
        myStopwatchLabel.Content = _stopwatch.Elapsed.ToString();
        myFrameCounterLabel.Content = _frameCounter.ToString();
        myFrameRateLabel.Content = frameRate.ToString();
    }

    // Update the background of the canvas by converting MouseMove info to RGB info.
    byte redColor = (byte)(_pt.X / 3.0);
    byte blueColor = (byte)(_pt.Y / 2.0);
    myCanvas.Background = new SolidColorBrush(Color.FromRgb(redColor, 0x0, blueColor));
}
' Called just before frame is rendered to allow custom drawing.
Protected Sub UpdateColor(ByVal sender As Object, ByVal e As EventArgs)

    If _frameCounter = 0 Then
        ' Starting timing.
        _stopwatch.Start()
    End If
    _frameCounter = _frameCounter + 1

    ' Determine frame rate in fps (frames per second).
    Dim frameRate As Long = CLng(Fix(_frameCounter / Me._stopwatch.Elapsed.TotalSeconds))
    If frameRate > 0 Then
        ' Update elapsed time, number of frames, and frame rate.
        myStopwatchLabel.Content = _stopwatch.Elapsed.ToString()
        myFrameCounterLabel.Content = _frameCounter.ToString()
        myFrameRateLabel.Content = frameRate.ToString()
    End If

    ' Update the background of the canvas by converting MouseMove info to RGB info.
    Dim redColor As Byte = CByte(_pt.X / 3.0)
    Dim blueColor As Byte = CByte(_pt.Y / 2.0)
    myCanvas.Background = New SolidColorBrush(Color.FromRgb(redColor, &H0, blueColor))
End Sub

可能会发现自定义绘图在不同计算机上以不同的速度运行。 这是由于自定义绘图与帧速率有关。 根据所运行的系统以及该系统的工作负荷,每秒调用 Rendering 事件的次数可能会有所不同。 有关为运行 WPF 应用程序的设备确定图形硬件功能和性能的信息,请参阅图形呈现层

在引发事件时对呈现 EventHandler 委托的添加或移除会延迟到事件完成引发之后。 这与基于 MulticastDelegate 的事件在公共语言运行时 (CLR) 中的处理方式一致。 另请注意,无法保证按照任何特定顺序来调用呈现事件。 如果有多个 EventHandler 委托依赖特定的顺序,则应当注册一个 Rendering 事件,然后自行按照正确的顺序多路复用这些委托。

另请参阅