File: PerfMargin\StatusIndicator.xaml.cs
Web Access
Project: src\src\VisualStudio\VisualStudioDiagnosticsToolWindow\Roslyn.VisualStudio.DiagnosticsWindow.csproj (Roslyn.VisualStudio.DiagnosticsWindow)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
#nullable disable
 
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Animation;
 
namespace Roslyn.Hosting.Diagnostics.PerfMargin;
 
/// <summary>
/// Interaction logic for StatusIndicator.xaml
/// </summary>
public partial class StatusIndicator : UserControl
{
    private readonly ActivityLevel _activityLevel;
    private bool _changedSinceLastUpdate;
 
    internal StatusIndicator(ActivityLevel activityLevel)
    {
        InitializeComponent();
 
        _activityLevel = activityLevel;
        _changedSinceLastUpdate = activityLevel.IsActive;
    }
 
    // Don't use WPF one way binding since it allocates too much memory for this high-frequency event
    internal void Subscribe()
    {
        _activityLevel.IsActiveChanged += this.ActivityLevel_IsActiveChanged;
    }
 
    internal void Unsubscribe()
    {
        _activityLevel.IsActiveChanged -= this.ActivityLevel_IsActiveChanged;
    }
 
    private void ActivityLevel_IsActiveChanged(object sender, EventArgs e)
    {
        _changedSinceLastUpdate = true;
    }
 
    private const double MinimumScale = 0.2;
    private static readonly DoubleAnimation s_growAnimation = new DoubleAnimation(1.0, new Duration(TimeSpan.FromSeconds(1.0)), FillBehavior.HoldEnd);
    private static readonly DoubleAnimation s_shrinkAnimation = new DoubleAnimation(0.0, new Duration(TimeSpan.FromSeconds(0.33333)), FillBehavior.HoldEnd);
 
    public void UpdateOnUIThread()
    {
        if (!_changedSinceLastUpdate)
        {
            return;
        }
 
        _changedSinceLastUpdate = false;
 
        // Remove existing animation
        this.clipScale.BeginAnimation(ScaleTransform.ScaleXProperty, null);
 
        // For very short durations, the growth animation hasn't even begun yet, so make
        // sure something is visible.
        this.clipScale.ScaleX = Math.Max(this.clipScale.ScaleX, MinimumScale);
 
        var anim = _activityLevel.IsActive ? s_growAnimation : s_shrinkAnimation;
        this.clipScale.BeginAnimation(ScaleTransform.ScaleXProperty, anim, HandoffBehavior.SnapshotAndReplace);
    }
}