Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
187 changes: 187 additions & 0 deletions Dashboard/Controls/DefaultTraceContent.xaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
<UserControl x:Class="PerformanceMonitorDashboard.Controls.DefaultTraceContent"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800"
Loaded="OnLoaded">
<TabControl>
<!-- Default Trace Events Sub-Tab -->
<TabItem Header="Default Trace Events">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>

<StackPanel Grid.Row="0" Orientation="Horizontal" Margin="10" Background="{DynamicResource BackgroundLightBrush}">
<TextBlock Text="Event Filter:" VerticalAlignment="Center" Margin="5,0" FontWeight="Bold"/>
<ComboBox x:Name="DefaultTraceEventsFilterCombo" Width="200" Margin="2,0" SelectionChanged="DefaultTraceEventsFilter_Changed">
<ComboBoxItem Content="All Events" Tag="" IsSelected="True"/>
<ComboBoxItem Content="Object:Altered" Tag="%Object:Altered%"/>
<ComboBoxItem Content="Object:Created" Tag="%Object:Created%"/>
<ComboBoxItem Content="Object:Deleted" Tag="%Object:Deleted%"/>
<ComboBoxItem Content="Audit%" Tag="Audit%"/>
<ComboBoxItem Content="Database%" Tag="Database%"/>
<ComboBoxItem Content="ErrorLog" Tag="%ErrorLog%"/>
<ComboBoxItem Content="Security%" Tag="Security%"/>
</ComboBox>
<Separator Margin="10,0" Style="{StaticResource {x:Static ToolBar.SeparatorStyleKey}}"/>
<Button Content="Refresh" Click="DefaultTraceEvents_Refresh_Click" Margin="5,0" Padding="10,4" Style="{StaticResource SuccessButton}"/>
</StackPanel>

<Grid Grid.Row="1">
<DataGrid x:Name="DefaultTraceEventsDataGrid" AutoGenerateColumns="False" IsReadOnly="True"
RowHeight="25" GridLinesVisibility="Horizontal" CanUserResizeColumns="True" ContextMenu="{StaticResource DataGridContextMenu}"
ScrollViewer.CanContentScroll="True" ScrollViewer.HorizontalScrollBarVisibility="Auto" ScrollViewer.VerticalScrollBarVisibility="Auto">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding EventTime, StringFormat='{}{0:yyyy-MM-dd HH:mm:ss}'}" Width="150">
<DataGridTextColumn.Header>
<StackPanel>
<TextBlock Text="Event Time" FontWeight="Bold" Margin="0,0,0,2"/>
<TextBox Width="140" Height="22" FontSize="11" TextChanged="DefaultTraceEventsFilterTextBox_TextChanged" Tag="EventTime"/>
</StackPanel>
</DataGridTextColumn.Header>
</DataGridTextColumn>
<DataGridTextColumn Binding="{Binding EventName}" Width="150">
<DataGridTextColumn.Header>
<StackPanel>
<TextBlock Text="Event Name" FontWeight="Bold" Margin="0,0,0,2"/>
<TextBox Width="140" Height="22" FontSize="11" TextChanged="DefaultTraceEventsFilterTextBox_TextChanged" Tag="EventName"/>
</StackPanel>
</DataGridTextColumn.Header>
</DataGridTextColumn>
<DataGridTextColumn Binding="{Binding DatabaseName}" Width="120">
<DataGridTextColumn.Header>
<StackPanel>
<TextBlock Text="Database" FontWeight="Bold" Margin="0,0,0,2"/>
<TextBox Width="110" Height="22" FontSize="11" TextChanged="DefaultTraceEventsFilterTextBox_TextChanged" Tag="DatabaseName"/>
</StackPanel>
</DataGridTextColumn.Header>
</DataGridTextColumn>
<DataGridTextColumn Binding="{Binding ObjectName}" Width="150">
<DataGridTextColumn.Header>
<StackPanel>
<TextBlock Text="Object Name" FontWeight="Bold" Margin="0,0,0,2"/>
<TextBox Width="140" Height="22" FontSize="11" TextChanged="DefaultTraceEventsFilterTextBox_TextChanged" Tag="ObjectName"/>
</StackPanel>
</DataGridTextColumn.Header>
</DataGridTextColumn>
<DataGridTextColumn Binding="{Binding LoginName}" Width="120">
<DataGridTextColumn.Header>
<StackPanel>
<TextBlock Text="Login" FontWeight="Bold" Margin="0,0,0,2"/>
<TextBox Width="110" Height="22" FontSize="11" TextChanged="DefaultTraceEventsFilterTextBox_TextChanged" Tag="LoginName"/>
</StackPanel>
</DataGridTextColumn.Header>
</DataGridTextColumn>
<DataGridTextColumn Binding="{Binding TextData}" Width="300">
<DataGridTextColumn.Header>
<StackPanel>
<TextBlock Text="Text Data" FontWeight="Bold" Margin="0,0,0,2"/>
<TextBox Width="290" Height="22" FontSize="11" TextChanged="DefaultTraceEventsFilterTextBox_TextChanged" Tag="TextData"/>
</StackPanel>
</DataGridTextColumn.Header>
</DataGridTextColumn>
<DataGridTextColumn Binding="{Binding Severity}" Width="60">
<DataGridTextColumn.Header>
<StackPanel>
<TextBlock Text="Severity" FontWeight="Bold" Margin="0,0,0,2"/>
<TextBox Width="50" Height="22" FontSize="11" TextChanged="DefaultTraceEventsNumericFilterTextBox_TextChanged" Tag="Severity"/>
</StackPanel>
</DataGridTextColumn.Header>
</DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
<TextBlock x:Name="DefaultTraceEventsNoDataMessage" Style="{StaticResource NoDataMessage}"/>
</Grid>
</Grid>
</TabItem>

<!-- Trace Analysis Sub-Tab -->
<TabItem Header="Trace Analysis">
<Grid>
<DataGrid x:Name="TraceAnalysisDataGrid" AutoGenerateColumns="False" IsReadOnly="True"
RowHeight="25" GridLinesVisibility="Horizontal" CanUserResizeColumns="True" ContextMenu="{StaticResource DataGridContextMenu}"
ScrollViewer.CanContentScroll="True" ScrollViewer.HorizontalScrollBarVisibility="Auto" ScrollViewer.VerticalScrollBarVisibility="Auto">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding CollectionTime, StringFormat='{}{0:yyyy-MM-dd HH:mm:ss}'}" Width="150">
<DataGridTextColumn.Header>
<StackPanel>
<TextBlock Text="Collected" FontWeight="Bold" Margin="0,0,0,2"/>
<TextBox Width="140" Height="22" FontSize="11" TextChanged="TraceAnalysisFilterTextBox_TextChanged" Tag="CollectionTime"/>
</StackPanel>
</DataGridTextColumn.Header>
</DataGridTextColumn>
<DataGridTextColumn Binding="{Binding EventName}" Width="150">
<DataGridTextColumn.Header>
<StackPanel>
<TextBlock Text="Event Name" FontWeight="Bold" Margin="0,0,0,2"/>
<TextBox Width="140" Height="22" FontSize="11" TextChanged="TraceAnalysisFilterTextBox_TextChanged" Tag="EventName"/>
</StackPanel>
</DataGridTextColumn.Header>
</DataGridTextColumn>
<DataGridTextColumn Binding="{Binding DatabaseName}" Width="120">
<DataGridTextColumn.Header>
<StackPanel>
<TextBlock Text="Database" FontWeight="Bold" Margin="0,0,0,2"/>
<TextBox Width="110" Height="22" FontSize="11" TextChanged="TraceAnalysisFilterTextBox_TextChanged" Tag="DatabaseName"/>
</StackPanel>
</DataGridTextColumn.Header>
</DataGridTextColumn>
<DataGridTextColumn Binding="{Binding LoginName}" Width="100">
<DataGridTextColumn.Header>
<StackPanel>
<TextBlock Text="Login" FontWeight="Bold" Margin="0,0,0,2"/>
<TextBox Width="90" Height="22" FontSize="11" TextChanged="TraceAnalysisFilterTextBox_TextChanged" Tag="LoginName"/>
</StackPanel>
</DataGridTextColumn.Header>
</DataGridTextColumn>
<DataGridTextColumn Binding="{Binding DurationMs, StringFormat=N0}" Width="90">
<DataGridTextColumn.Header>
<StackPanel>
<TextBlock Text="Duration (ms)" FontWeight="Bold" Margin="0,0,0,2"/>
<TextBox Width="80" Height="22" FontSize="11" TextChanged="TraceAnalysisNumericFilterTextBox_TextChanged" Tag="DurationMs"/>
</StackPanel>
</DataGridTextColumn.Header>
</DataGridTextColumn>
<DataGridTextColumn Binding="{Binding CpuMs, StringFormat=N0}" Width="80">
<DataGridTextColumn.Header>
<StackPanel>
<TextBlock Text="CPU (ms)" FontWeight="Bold" Margin="0,0,0,2"/>
<TextBox Width="70" Height="22" FontSize="11" TextChanged="TraceAnalysisNumericFilterTextBox_TextChanged" Tag="CpuMs"/>
</StackPanel>
</DataGridTextColumn.Header>
</DataGridTextColumn>
<DataGridTextColumn Binding="{Binding Reads, StringFormat=N0}" Width="90">
<DataGridTextColumn.Header>
<StackPanel>
<TextBlock Text="Reads (pages)" FontWeight="Bold" Margin="0,0,0,2"/>
<TextBox Width="80" Height="22" FontSize="11" TextChanged="TraceAnalysisNumericFilterTextBox_TextChanged" Tag="Reads"/>
</StackPanel>
</DataGridTextColumn.Header>
</DataGridTextColumn>
<DataGridTextColumn Binding="{Binding Writes, StringFormat=N0}" Width="90">
<DataGridTextColumn.Header>
<StackPanel>
<TextBlock Text="Writes (pages)" FontWeight="Bold" Margin="0,0,0,2"/>
<TextBox Width="80" Height="22" FontSize="11" TextChanged="TraceAnalysisNumericFilterTextBox_TextChanged" Tag="Writes"/>
</StackPanel>
</DataGridTextColumn.Header>
</DataGridTextColumn>
<DataGridTextColumn Binding="{Binding SqlText}" Width="400">
<DataGridTextColumn.Header>
<StackPanel>
<TextBlock Text="SQL Text" FontWeight="Bold" Margin="0,0,0,2"/>
<TextBox Width="390" Height="22" FontSize="11" TextChanged="TraceAnalysisFilterTextBox_TextChanged" Tag="SqlText"/>
</StackPanel>
</DataGridTextColumn.Header>
</DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
<TextBlock x:Name="TraceAnalysisNoDataMessage" Style="{StaticResource NoDataMessage}"/>
</Grid>
</TabItem>
</TabControl>
</UserControl>
155 changes: 155 additions & 0 deletions Dashboard/Controls/DefaultTraceContent.xaml.cs
Original file line number Diff line number Diff line change
@@ -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
}
}
Loading
Loading