wpf.net-coreavaloneditdoubleanimation

WPF stop an animation when it's not visible on the screen


I'm using AvalonEdit to create an ANSI terminal that properly color codes and implements some styles like underline and reverse (it works wonderfully for that, this isn't a full VT100 kind of thing).

In AvalonEdit a DocumentColorizingTransformer to transform ANSI color codes into colored text or styled text. Last night I was working on implementing the ANSI Blink. I got the blink working with this section of code:

else if (color.AnsiColor is Blink)
{
    // The color animation of the pulse, use the opacity to simulate blinking
    var da = new DoubleAnimation
    {
        Duration = new Duration(TimeSpan.FromMilliseconds(500)),
        From = 0,
        To = 1,
        AutoReverse = true
        RepeatBehavior = RepeatBehavior.Forever
    };

    // Get the foreground
    var foreground = element.TextRunProperties.ForegroundBrush;

    // Clone the foreground so it's not frozen and we can run animations on it.
    var blinkForeground = foreground.Clone();

    // Set the new foreground.
    element.TextRunProperties.SetForegroundBrush(blinkForeground);

    // Blink Blink Blink Blink
    blinkForeground.BeginAnimation(SolidColorBrush.OpacityProperty, da);
}

Obviously I want this text to blink forever while it's on the screen which is why I set the RepeatBehavior to forever. However, it continues to use CPU even after it's scrolled off of the visible part of the screen (it may not be rendering but the Animation seems to still be running).

My question:

How does one go about stopping an animation on a piece of text when it's scrolled out of the visible view (without necessarily having to go through -everything- in the AvalonEdit box which could be very, very large and not feasible to do performance wise). If I could stop the animation when it goes off screen and then restart it when ColorizeLine(DocumentLine line) fires that would be ideal. With AvalonEdit I know when something comes through ColorizeLine it's visible, but I don't necessarily know at that point what's not visible. I considered keeping a list of references to all the double animations but I still unaware of when they're visible on the screen.

Any other ideas or thoughts on how I can approach this?


Solution

  • WPF will render at 60 frames per second if there's any animations running. The rendering is relatively fast if there are no actual changes, but it still causes noticable CPU usage when the application is supposed to be idle. AvalonEdit's caret is toggled on-off (with a timer) instead of using an animation to avoid rendering more frames than necessary.


    In the DocumentColorizingTransformer, you can use this.CurrentContext.VisualLine to access the VisualLine being processed. The VisualLine has an IsDisposed property that will change to true once the AvalonEdit has discarded the line (and would call your colorizer again if the line becomes visible again).

    There is no event telling you when the line has been disposed. But if you keep track of all animations+their containing VisualLines in a list, you can use the TextView.VisualLinesChanged event to scan for VisualLines that were disposed and stop the corresponding animations.