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

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

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

注意注意

有关使用 CompositionTarget 的完整代码示例,请参见 Using the CompositionTarget Sample(使用 CompositionTarget 示例)。

示例

在 WPF 呈现过程中,会激发 Rendering 事件。 下面的示例演示如何向 CompositionTarget 上的静态 Rendering 方法中注册 EventHandler 委托。

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

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

下面的示例演示如何在 CompositionTarget 事件处理程序方法中提供自定义绘图。 在本例中,Canvas 的背景色是基于鼠标的坐标位置,用颜色值绘制的。 如果您将鼠标放在 Canvas 内部,则它的背景色将发生变化。 另外,将基于当前的运行时间和所呈现帧的总数来计算平均帧速率。

        ' 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
// 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));
}

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

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

请参见

参考

CompositionTarget

概念

WPF 图形呈现疑难解答