File: PerfMargin\PerfMarginPanel.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.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Threading;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Internal.Log;
 
namespace Roslyn.Hosting.Diagnostics.PerfMargin;
 
public sealed class PerfMarginPanel : UserControl
{
    private static readonly DataModel s_model = new();
    private static readonly PerfEventActivityLogger s_logger = new(s_model);
 
    private readonly ListView _mainListView;
    private readonly Grid _mainGrid;
 
    private readonly DispatcherTimer _timer;
    private readonly List<StatusIndicator> _indicators = [];
 
    private ListView _detailsListView;
    private bool _stopTimer;
 
    public PerfMarginPanel()
    {
        Logger.SetLogger(AggregateLogger.AddOrReplace(s_logger, Logger.GetLogger(), l => l is PerfEventActivityLogger));
 
        // grid
        _mainGrid = new Grid();
        _mainGrid.ColumnDefinitions.Add(new ColumnDefinition());
        _mainGrid.RowDefinitions.Add(new RowDefinition() { Height = GridLength.Auto });
        _mainGrid.RowDefinitions.Add(new RowDefinition() { Height = GridLength.Auto });
 
        // set diagnostic list
        _mainListView = CreateContent(new ActivityLevel[] { s_model.RootNode }.Concat(s_model.RootNode.Children), useWrapPanel: true);
        _mainListView.SelectionChanged += OnPerfItemsListSelectionChanged;
        Grid.SetRow(_mainListView, 0);
 
        _mainGrid.Children.Add(_mainListView);
 
        Content = _mainGrid;
 
        _timer = new DispatcherTimer(TimeSpan.FromMilliseconds(500), DispatcherPriority.Background, UpdateUI, Dispatcher);
        StartTimer();
 
        s_model.RootNode.IsActiveChanged += (s, e) =>
        {
            if (_stopTimer)
            {
                StartTimer();
            }
        };
    }
 
    private void StartTimer()
    {
        _timer.Start();
        _stopTimer = false;
    }
 
    private void UpdateUI(object sender, EventArgs e)
    {
        foreach (var item in _indicators)
        {
            item.UpdateOnUIThread();
        }
 
        if (_stopTimer)
        {
            _timer.Stop();
        }
        else
        {
            // Stop it next time if there was no activity.
            _stopTimer = true;
        }
    }
 
    private ListView CreateContent(IEnumerable<ActivityLevel> items, bool useWrapPanel)
    {
        var listView = new ListView();
 
        foreach (var item in items)
        {
            var s = new StackPanel() { Orientation = Orientation.Horizontal };
 
            var indicator = new StatusIndicator(item);
            indicator.Subscribe();
            indicator.Width = 30;
            indicator.Height = 10;
            s.Children.Add(indicator);
            _indicators.Add(indicator);
 
            var label = new TextBlock();
            label.Text = item.Name;
            Grid.SetColumn(label, 1);
            s.Children.Add(label);
 
            s.ToolTip = item.Name;
            s.Tag = item;
 
            listView.Items.Add(s);
        }
 
        if (useWrapPanel)
        {
            listView.SelectionMode = SelectionMode.Single;
            listView.SetValue(ScrollViewer.HorizontalScrollBarVisibilityProperty, ScrollBarVisibility.Disabled);
 
            var wrapPanelFactory = new FrameworkElementFactory(typeof(WrapPanel));
            wrapPanelFactory.SetValue(WrapPanel.ItemWidthProperty, 120d);
            listView.ItemsPanel = new ItemsPanelTemplate(wrapPanelFactory);
        }
 
        return listView;
    }
 
    private void OnPerfItemsListSelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        if (_detailsListView != null)
        {
            _mainGrid.ColumnDefinitions.RemoveAt(1);
            _mainGrid.Children.Remove(_detailsListView);
            foreach (StackPanel item in _detailsListView.Items)
            {
                var indicator = item.Children[0] as StatusIndicator;
                _indicators.Remove(indicator);
                indicator.Unsubscribe();
            }
 
            _detailsListView = null;
        }
 
        if (_mainListView.SelectedItem is not StackPanel selectedItem)
        {
            return;
        }
 
        if (selectedItem.Tag is ActivityLevel context && context.Children != null && context.Children.Any())
        {
            _detailsListView = CreateContent(context.Children, useWrapPanel: false);
            _mainGrid.Children.Add(_detailsListView);
            _mainGrid.ColumnDefinitions.Add(new ColumnDefinition());
            Grid.SetColumn(_detailsListView, 1);
 
            // Update the UI
            StartTimer();
        }
    }
}