LionAM
LionAM

Reputation: 1401

WPF determine optimal UI update rate

Is there any way in WPF to determine the optimal UI update rate, so that

  1. No frames are skipped and
  2. UI stays responsible.

It does not make sense to to a lot work to notify the UI that some values have changed when the render process is not able to keep pace. Indeed, I notice that when I try to update too often, the UI gets unresponsive and only a small fraction of the frames are displayed (e.g. InvalidateVisual() is called 10 times, but only one new frame is displayed). Instead, I would prefer that the UI is updated at the native rendering rate.

Up to now, I already tried with a DispatcherTimer, but even if I reduce the Priority to Background, the update method is called more often than the Rendering occurs (frames are skipped) if the update rate is too high. But this rate obviously depends on the Hardware and the number of elements on the screen. If I further reduce the Priority, the timer is not called at all (perhaps as there is a background thread running to update the values).

Alex

Upvotes: 2

Views: 2023

Answers (1)

AwkwardCoder
AwkwardCoder

Reputation: 25651

I just wrote a quick test to see what the frame rate I'm getting in a test .Net WPF app.

I'm seeing a rate of between 60-63 frames per second, the monitor refresh rate is 60 Hz, so I guess I'm seeing the UI updating at the native rate.

I wrote this using the CompositionTarget.Rendering event and implemented it as a Behavior and attached it to a TextBlock in a very simple MVVM style app:

XAML:

<TextBlock>
    <i:Interaction.Behaviors>
         <views:FrameRateBehavior />
    </i:Interaction.Behaviors>
</TextBlock>

C# Code:

public sealed class FrameRateBehavior : Behavior<TextBlock>
{
    private readonly Queue<long> _ticks = new Queue<long>();

    public FrameRateBehavior()
    {
        _ticks = new Queue<long>();
    }

    protected override void OnAttached()
    {
        base.OnAttached();

        CompositionTarget.Rendering += CalculateFrameRate;
    }

    protected override void OnDetaching()
    {
        base.OnDetaching();

        CompositionTarget.Rendering -= CalculateFrameRate;
        _ticks.Clear();
    }

    private void CalculateFrameRate(object sender, EventArgs e)
    {
        var now = DateTime.Now;
        var endTime = now.Ticks;
        var startTime = now.AddSeconds(-1).Ticks;

        while (_ticks.Any())
        {
            if (_ticks.Peek() < startTime)
            {
                _ticks.Dequeue();

                continue;
            }

            break;
        }

        _ticks.Enqueue(endTime);
        var count = _ticks.Count;

        AssociatedObject.Text = "FPS: " + count;
    }
}

Upvotes: 1

Related Questions