diff --git a/Dashboard/Controls/DefaultTraceContent.xaml b/Dashboard/Controls/DefaultTraceContent.xaml
new file mode 100644
index 00000000..6552c711
--- /dev/null
+++ b/Dashboard/Controls/DefaultTraceContent.xaml
@@ -0,0 +1,187 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Dashboard/Controls/DefaultTraceContent.xaml.cs b/Dashboard/Controls/DefaultTraceContent.xaml.cs
new file mode 100644
index 00000000..ed7aea1f
--- /dev/null
+++ b/Dashboard/Controls/DefaultTraceContent.xaml.cs
@@ -0,0 +1,155 @@
+/*
+ * Copyright (c) 2026 Erik Darling, Darling Data LLC
+ *
+ * This file is part of the SQL Server Performance Monitor.
+ *
+ * Licensed under the MIT License. See LICENSE file in the project root for full license information.
+ */
+
+using System;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Controls;
+using PerformanceMonitorDashboard.Helpers;
+using PerformanceMonitorDashboard.Services;
+
+namespace PerformanceMonitorDashboard.Controls
+{
+ public partial class DefaultTraceContent : UserControl
+ {
+ private DatabaseService? _databaseService;
+
+ private int _defaultTraceEventsHoursBack = 24;
+ private DateTime? _defaultTraceEventsFromDate;
+ private DateTime? _defaultTraceEventsToDate;
+ private string? _defaultTraceEventsFilter;
+
+ private int _traceAnalysisHoursBack = 24;
+ private DateTime? _traceAnalysisFromDate;
+ private DateTime? _traceAnalysisToDate;
+
+ public DefaultTraceContent()
+ {
+ InitializeComponent();
+ }
+
+ public void Initialize(DatabaseService databaseService)
+ {
+ _databaseService = databaseService;
+ }
+
+ public void SetTimeRange(int hoursBack, DateTime? fromDate = null, DateTime? toDate = null)
+ {
+ _defaultTraceEventsHoursBack = hoursBack;
+ _defaultTraceEventsFromDate = fromDate;
+ _defaultTraceEventsToDate = toDate;
+
+ _traceAnalysisHoursBack = hoursBack;
+ _traceAnalysisFromDate = fromDate;
+ _traceAnalysisToDate = toDate;
+ }
+
+ public async Task RefreshAllDataAsync()
+ {
+ if (_databaseService == null) return;
+
+ await Task.WhenAll(
+ RefreshDefaultTraceEventsAsync(),
+ RefreshTraceAnalysisAsync()
+ );
+ }
+
+ private void OnLoaded(object sender, RoutedEventArgs e)
+ {
+ TabHelpers.AutoSizeColumnMinWidths(DefaultTraceEventsDataGrid);
+ TabHelpers.AutoSizeColumnMinWidths(TraceAnalysisDataGrid);
+ TabHelpers.FreezeColumns(DefaultTraceEventsDataGrid, 1);
+ TabHelpers.FreezeColumns(TraceAnalysisDataGrid, 1);
+ }
+
+ #region Default Trace Events
+
+ private void DefaultTraceEventsFilter_Changed(object sender, SelectionChangedEventArgs e)
+ {
+ if (DefaultTraceEventsFilterCombo.SelectedItem is ComboBoxItem selected)
+ {
+ _defaultTraceEventsFilter = selected.Tag?.ToString();
+ if (string.IsNullOrEmpty(_defaultTraceEventsFilter))
+ {
+ _defaultTraceEventsFilter = null;
+ }
+ DefaultTraceEvents_Refresh_Click(sender, new RoutedEventArgs());
+ }
+ }
+
+ private async void DefaultTraceEvents_Refresh_Click(object? sender, RoutedEventArgs e)
+ {
+ try
+ {
+ await RefreshDefaultTraceEventsAsync();
+ }
+ catch (Exception ex)
+ {
+ Logger.Error($"Error refreshing Default Trace Events data: {ex.Message}", ex);
+ }
+ }
+
+ private async Task RefreshDefaultTraceEventsAsync()
+ {
+ if (_databaseService == null) return;
+
+ try
+ {
+ var data = await _databaseService.GetDefaultTraceEventsAsync(_defaultTraceEventsHoursBack, _defaultTraceEventsFromDate, _defaultTraceEventsToDate, _defaultTraceEventsFilter);
+ DefaultTraceEventsDataGrid.ItemsSource = data;
+ DefaultTraceEventsNoDataMessage.Visibility = data.Count == 0 ? Visibility.Visible : Visibility.Collapsed;
+ }
+ catch (Exception ex)
+ {
+ Logger.Error($"Error loading default trace events: {ex.Message}");
+ }
+ }
+
+ private void DefaultTraceEventsFilterTextBox_TextChanged(object sender, TextChangedEventArgs e)
+ {
+ DataGridFilterService.ApplyFilter(DefaultTraceEventsDataGrid, sender as TextBox);
+ }
+
+ private void DefaultTraceEventsNumericFilterTextBox_TextChanged(object sender, TextChangedEventArgs e)
+ {
+ DataGridFilterService.ApplyFilter(DefaultTraceEventsDataGrid, sender as TextBox);
+ }
+
+ #endregion
+
+ #region Trace Analysis
+
+ private async Task RefreshTraceAnalysisAsync()
+ {
+ if (_databaseService == null) return;
+
+ try
+ {
+ var data = await _databaseService.GetTraceAnalysisAsync(_traceAnalysisHoursBack, _traceAnalysisFromDate, _traceAnalysisToDate);
+ TraceAnalysisDataGrid.ItemsSource = data;
+ TraceAnalysisNoDataMessage.Visibility = data.Count == 0 ? Visibility.Visible : Visibility.Collapsed;
+ }
+ catch (Exception ex)
+ {
+ Logger.Error($"Error loading trace analysis: {ex.Message}");
+ }
+ }
+
+ private void TraceAnalysisFilterTextBox_TextChanged(object sender, TextChangedEventArgs e)
+ {
+ DataGridFilterService.ApplyFilter(TraceAnalysisDataGrid, sender as TextBox);
+ }
+
+ private void TraceAnalysisNumericFilterTextBox_TextChanged(object sender, TextChangedEventArgs e)
+ {
+ DataGridFilterService.ApplyFilter(TraceAnalysisDataGrid, sender as TextBox);
+ }
+
+ #endregion
+ }
+}
diff --git a/Dashboard/Controls/ResourceMetricsContent.xaml b/Dashboard/Controls/ResourceMetricsContent.xaml
index d30c14cb..4eeb5f6c 100644
--- a/Dashboard/Controls/ResourceMetricsContent.xaml
+++ b/Dashboard/Controls/ResourceMetricsContent.xaml
@@ -295,183 +295,6 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/Dashboard/Controls/ResourceMetricsContent.xaml.cs b/Dashboard/Controls/ResourceMetricsContent.xaml.cs
index 125dfc7b..276f8390 100644
--- a/Dashboard/Controls/ResourceMetricsContent.xaml.cs
+++ b/Dashboard/Controls/ResourceMetricsContent.xaml.cs
@@ -16,7 +16,6 @@
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
-using System.Windows.Controls.Primitives;
using System.Windows.Media;
using Microsoft.Win32;
using PerformanceMonitorDashboard.Models;
@@ -29,7 +28,7 @@ namespace PerformanceMonitorDashboard.Controls
///
/// UserControl for the Resource Metrics tab content.
/// Displays Latch Stats, Spinlock Stats, TempDB Stats, CPU Spikes, Session Stats,
- /// File I/O Latency, Server Trends, Perfmon Counters, Default Trace Events, and Trace Analysis.
+ /// File I/O Latency, Server Trends, and Perfmon Counters.
///
public partial class ResourceMetricsContent : UserControl
{
@@ -75,16 +74,6 @@ public partial class ResourceMetricsContent : UserControl
private List? _allPerfmonCountersData;
private List? _perfmonCounterItems;
- // Default Trace Events state
- private int _defaultTraceEventsHoursBack = 24;
- private DateTime? _defaultTraceEventsFromDate;
- private DateTime? _defaultTraceEventsToDate;
-
- // Trace Analysis state
- private int _traceAnalysisHoursBack = 24;
- private DateTime? _traceAnalysisFromDate;
- private DateTime? _traceAnalysisToDate;
-
// Wait Stats Detail state
private int _waitStatsDetailHoursBack = 24;
private DateTime? _waitStatsDetailFromDate;
@@ -105,19 +94,7 @@ public partial class ResourceMetricsContent : UserControl
private Helpers.ChartHoverHelper? _serverTrendsTempdbHover;
private Helpers.ChartHoverHelper? _serverTrendsMemoryHover;
private Helpers.ChartHoverHelper? _serverTrendsPerfmonHover;
- // Column filter popup and state
- private Popup? _filterPopup;
- private ColumnFilterPopup? _filterPopupContent;
- private string? _currentFilterDataGridName;
-
// Filter state dictionaries for each DataGrid
- private Dictionary _defaultTraceEventsFilters = new();
- private Dictionary _traceAnalysisFilters = new();
-
- // Unfiltered data for each DataGrid
- private List? _defaultTraceEventsUnfilteredData;
- private List? _traceAnalysisUnfilteredData;
-
// Legend panel references for edge-based legends (ScottPlot issue #4717 workaround)
// Must store and remove these by reference before creating new ones
private Dictionary _legendPanels = new();
@@ -147,12 +124,8 @@ public ResourceMetricsContent()
private void OnLoaded(object sender, RoutedEventArgs e)
{
// Apply minimum column widths based on header text
- TabHelpers.AutoSizeColumnMinWidths(DefaultTraceEventsDataGrid);
- TabHelpers.AutoSizeColumnMinWidths(TraceAnalysisDataGrid);
// Freeze identifier columns
- TabHelpers.FreezeColumns(DefaultTraceEventsDataGrid, 1);
- TabHelpers.FreezeColumns(TraceAnalysisDataGrid, 1);
}
private void SetupChartContextMenus()
@@ -230,14 +203,6 @@ public void SetTimeRange(int hoursBack, DateTime? fromDate = null, DateTime? toD
_perfmonCountersFromDate = fromDate;
_perfmonCountersToDate = toDate;
- _defaultTraceEventsHoursBack = hoursBack;
- _defaultTraceEventsFromDate = fromDate;
- _defaultTraceEventsToDate = toDate;
-
- _traceAnalysisHoursBack = hoursBack;
- _traceAnalysisFromDate = fromDate;
- _traceAnalysisToDate = toDate;
-
_waitStatsDetailHoursBack = hoursBack;
_waitStatsDetailFromDate = fromDate;
_waitStatsDetailToDate = toDate;
@@ -262,8 +227,6 @@ await Task.WhenAll(
LoadFileIoLatencyChartsAsync(),
RefreshServerTrendsAsync(),
RefreshPerfmonCountersTabAsync(),
- RefreshDefaultTraceEventsAsync(),
- RefreshTraceAnalysisAsync(),
RefreshWaitStatsDetailTabAsync()
);
}
@@ -1284,134 +1247,6 @@ private void LoadServerTrendsPerfmonChart(IEnumerable perfmonD
#endregion
- // Filtering logic moved to DataGridFilterService.ApplyFilter()
-
-
- #region Shared Filter Popup Methods
-
- private void ShowFilterPopup(Button button, string columnName, string dataGridName, Dictionary filters)
- {
- if (_filterPopup == null)
- {
- _filterPopupContent = new ColumnFilterPopup();
- _filterPopupContent.FilterApplied += FilterPopup_FilterApplied;
- _filterPopupContent.FilterCleared += FilterPopup_FilterCleared;
-
- _filterPopup = new Popup
- {
- Child = _filterPopupContent,
- StaysOpen = false,
- Placement = PlacementMode.Bottom,
- AllowsTransparency = true
- };
- }
-
- _currentFilterDataGridName = dataGridName;
- filters.TryGetValue(columnName, out var existingFilter);
- _filterPopupContent!.Initialize(columnName, existingFilter);
- _filterPopup.PlacementTarget = button;
- _filterPopup.IsOpen = true;
- }
-
- private void FilterPopup_FilterApplied(object? sender, FilterAppliedEventArgs e)
- {
- if (_filterPopup != null) _filterPopup.IsOpen = false;
-
- switch (_currentFilterDataGridName)
- {
- case "DefaultTraceEvents":
- UpdateFilterState(_defaultTraceEventsFilters, e.FilterState);
- ApplyDefaultTraceEventsFilters();
- UpdateDataGridFilterButtonStyles(DefaultTraceEventsDataGrid, _defaultTraceEventsFilters);
- break;
- case "TraceAnalysis":
- UpdateFilterState(_traceAnalysisFilters, e.FilterState);
- ApplyTraceAnalysisFilters();
- UpdateDataGridFilterButtonStyles(TraceAnalysisDataGrid, _traceAnalysisFilters);
- break;
- }
- }
-
- private void FilterPopup_FilterCleared(object? sender, EventArgs e)
- {
- if (_filterPopup != null) _filterPopup.IsOpen = false;
- }
-
- private void UpdateFilterState(Dictionary filters, ColumnFilterState filterState)
- {
- if (filterState.IsActive)
- filters[filterState.ColumnName] = filterState;
- else
- filters.Remove(filterState.ColumnName);
- }
-
- private void UpdateDataGridFilterButtonStyles(DataGrid dataGrid, Dictionary filters)
- {
- foreach (var column in dataGrid.Columns)
- {
- if (column.Header is StackPanel headerPanel)
- {
- var filterButton = headerPanel.Children.OfType
+
+
+
+
+
diff --git a/Dashboard/ServerTab.xaml.cs b/Dashboard/ServerTab.xaml.cs
index 4259f3d1..58acbcf6 100644
--- a/Dashboard/ServerTab.xaml.cs
+++ b/Dashboard/ServerTab.xaml.cs
@@ -114,6 +114,7 @@ public ServerTab(ServerConnection serverConnection, int utcOffsetMinutes = 0)
// Initialize Overview sub-tab UserControls
DailySummaryTab.Initialize(_databaseService);
CriticalIssuesTab.Initialize(_databaseService);
+ DefaultTraceTab.Initialize(_databaseService);
MemoryTab.Initialize(_databaseService);
PerformanceTab.Initialize(_databaseService, s => StatusText.Text = s);
SystemEventsContent.Initialize(_databaseService);
@@ -699,7 +700,8 @@ private async void ServerTab_Loaded(object sender, RoutedEventArgs e)
ResourceMetricsContent.SetTimeRange(_globalHoursBack, _globalFromDate, _globalToDate);
SystemEventsContent.SetTimeRange(_globalHoursBack, _globalFromDate, _globalToDate);
CriticalIssuesTab.SetTimeRange(_globalHoursBack, _globalFromDate, _globalToDate);
-
+ DefaultTraceTab.SetTimeRange(_globalHoursBack, _globalFromDate, _globalToDate);
+
await LoadDataAsync();
}
catch (Exception ex)
@@ -862,6 +864,7 @@ private async void ApplyToAllTabs_Click(object sender, RoutedEventArgs e)
ResourceMetricsContent.SetTimeRange(_globalHoursBack, _globalFromDate, _globalToDate);
SystemEventsContent.SetTimeRange(_globalHoursBack, _globalFromDate, _globalToDate);
CriticalIssuesTab.SetTimeRange(_globalHoursBack, _globalFromDate, _globalToDate);
+ DefaultTraceTab.SetTimeRange(_globalHoursBack, _globalFromDate, _globalToDate);
// Refresh all data
StatusText.Text = GetLoadingMessage();
@@ -1162,8 +1165,10 @@ private async Task ApplyAndRefreshCurrentTabAsync()
_resourceOverviewFromDate = _globalFromDate;
_resourceOverviewToDate = _globalToDate;
CriticalIssuesTab.SetTimeRange(_globalHoursBack, _globalFromDate, _globalToDate);
+ DefaultTraceTab.SetTimeRange(_globalHoursBack, _globalFromDate, _globalToDate);
CollectionHealth_Refresh_Click(null, new RoutedEventArgs());
await CriticalIssuesTab.RefreshDataAsync();
+ await DefaultTraceTab.RefreshAllDataAsync();
await RefreshResourceOverviewAsync();
break;
@@ -1272,13 +1277,14 @@ private async Task LoadDataAsync()
var resourceMetricsTask = ResourceMetricsContent.RefreshAllDataAsync();
var dailySummaryTask = DailySummaryTab.RefreshDataAsync();
var criticalIssuesTask = CriticalIssuesTab.RefreshDataAsync();
+ var defaultTraceTask = DefaultTraceTab.RefreshAllDataAsync();
var systemEventsTask = SystemEventsContent.RefreshAllDataAsync();
// Wait for everything to complete before _isRefreshing resets
await Task.WhenAll(
healthTask, blockingEventsTask, deadlocksTask, blockingStatsTask, lockWaitStatsTask,
performanceTask, memoryTask, resourceOverviewTask, runningJobsTask,
- resourceMetricsTask, dailySummaryTask, criticalIssuesTask, systemEventsTask);
+ resourceMetricsTask, dailySummaryTask, criticalIssuesTask, defaultTraceTask, systemEventsTask);
// Populate grids with fetched data
var healthData = await healthTask;