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
59 changes: 40 additions & 19 deletions Dashboard/Controls/QueryPerformanceContent.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -693,40 +693,63 @@ private async Task RefreshQueryStoreGridAsync()

private void PopulateQueryStatsGrid(List<QueryStatsItem> data)
{
QueryStatsDataGrid.ItemsSource = data;
SetItemsSourcePreservingSort(QueryStatsDataGrid, data, "AvgCpuTimeMs", ListSortDirection.Descending);
QueryStatsNoDataMessage.Visibility = data.Count == 0 ? Visibility.Visible : Visibility.Collapsed;
SetInitialSort(QueryStatsDataGrid, "AvgCpuTimeMs", ListSortDirection.Descending);
}

private void PopulateProcStatsGrid(List<ProcedureStatsItem> data)
{
ProcStatsDataGrid.ItemsSource = data;
SetItemsSourcePreservingSort(ProcStatsDataGrid, data, "AvgCpuTimeMs", ListSortDirection.Descending);
ProcStatsNoDataMessage.Visibility = data.Count == 0 ? Visibility.Visible : Visibility.Collapsed;
SetInitialSort(ProcStatsDataGrid, "AvgCpuTimeMs", ListSortDirection.Descending);
}

private void PopulateQueryStoreGrid(List<QueryStoreItem> data)
{
QueryStoreDataGrid.ItemsSource = data;
SetItemsSourcePreservingSort(QueryStoreDataGrid, data, "AvgCpuTimeMs", ListSortDirection.Descending);
QueryStoreNoDataMessage.Visibility = data.Count == 0 ? Visibility.Visible : Visibility.Collapsed;
SetInitialSort(QueryStoreDataGrid, "AvgCpuTimeMs", ListSortDirection.Descending);
}

private void SetStatus(string message)
{
_statusCallback?.Invoke(message);
}

private static void SetInitialSort(DataGrid grid, string bindingPath, ListSortDirection direction)
private static void SetItemsSourcePreservingSort(
DataGrid grid, System.Collections.IEnumerable? newSource,
string? defaultSortProperty = null,
ListSortDirection defaultDirection = ListSortDirection.Descending)
{
foreach (var column in grid.Columns)
var savedSorts = grid.Items.SortDescriptions.ToList();

grid.ItemsSource = newSource;

if (savedSorts.Count > 0)
{
if (column is DataGridBoundColumn bc &&
bc.Binding is Binding b &&
b.Path.Path == bindingPath)
foreach (var sort in savedSorts)
grid.Items.SortDescriptions.Add(sort);

foreach (var column in grid.Columns)
{
column.SortDirection = direction;
return;
if (column is DataGridBoundColumn bc &&
bc.Binding is Binding b)
{
var match = savedSorts.FirstOrDefault(s => s.PropertyName == b.Path.Path);
column.SortDirection = match.PropertyName != null ? match.Direction : null;
}
}
}
else if (defaultSortProperty != null)
{
grid.Items.SortDescriptions.Add(new SortDescription(defaultSortProperty, defaultDirection));
foreach (var column in grid.Columns)
{
if (column is DataGridBoundColumn bc &&
bc.Binding is Binding b &&
b.Path.Path == defaultSortProperty)
{
column.SortDirection = defaultDirection;
return;
}
}
}
}
Expand Down Expand Up @@ -851,7 +874,7 @@ private async Task RefreshActiveQueriesAsync()
data = await _databaseService.GetQuerySnapshotsAsync(_activeQueriesHoursBack, _activeQueriesFromDate, _activeQueriesToDate);
}

ActiveQueriesDataGrid.ItemsSource = data;
SetItemsSourcePreservingSort(ActiveQueriesDataGrid, data);
ActiveQueriesNoDataMessage.Visibility = data.Count == 0 ? Visibility.Visible : Visibility.Collapsed;
SetStatus($"Loaded {data.Count} query snapshots");
LoadActiveQueriesSlicerAsync().ConfigureAwait(false);
Expand Down Expand Up @@ -1015,7 +1038,7 @@ private async Task RefreshCurrentActiveQueriesAsync()
var data = await _databaseService.GetCurrentActiveQueriesAsync();

_currentActiveUnfilteredData = data;
CurrentActiveQueriesDataGrid.ItemsSource = data;
SetItemsSourcePreservingSort(CurrentActiveQueriesDataGrid, data);
CurrentActiveNoDataMessage.Visibility = data.Count == 0 ? Visibility.Visible : Visibility.Collapsed;
CurrentActiveTimestamp.Text = $"Last refreshed: {DateTime.Now:HH:mm:ss} — {data.Count} queries";

Expand Down Expand Up @@ -1757,9 +1780,8 @@ private async Task RefreshQueryStoreRegressionsAsync()
{
SetStatus("Loading query store regressions...");
var data = await _databaseService.GetQueryStoreRegressionsAsync(_qsRegressionsHoursBack, _qsRegressionsFromDate, _qsRegressionsToDate);
QueryStoreRegressionsDataGrid.ItemsSource = data;
SetItemsSourcePreservingSort(QueryStoreRegressionsDataGrid, data, "DurationRegressionPercent", ListSortDirection.Descending);
QueryStoreRegressionsNoDataMessage.Visibility = data.Count == 0 ? Visibility.Visible : Visibility.Collapsed;
SetInitialSort(QueryStoreRegressionsDataGrid, "DurationRegressionPercent", ListSortDirection.Descending);
SetStatus($"Loaded {data.Count} query store regression records");
}
catch (Exception ex)
Expand Down Expand Up @@ -1858,9 +1880,8 @@ private async Task RefreshLongRunningPatternsAsync()
{
SetStatus("Loading long running query patterns...");
var data = await _databaseService.GetLongRunningQueryPatternsAsync(_lrqPatternsHoursBack, _lrqPatternsFromDate, _lrqPatternsToDate);
LongRunningQueryPatternsDataGrid.ItemsSource = data;
SetItemsSourcePreservingSort(LongRunningQueryPatternsDataGrid, data, "AvgDurationSec", ListSortDirection.Descending);
LongRunningQueryPatternsNoDataMessage.Visibility = data.Count == 0 ? Visibility.Visible : Visibility.Collapsed;
SetInitialSort(LongRunningQueryPatternsDataGrid, "AvgDurationSec", ListSortDirection.Descending);
SetStatus($"Loaded {data.Count} long running query pattern records");
}
catch (Exception ex)
Expand Down
23 changes: 13 additions & 10 deletions Lite/Controls/ServerTab.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -653,7 +653,7 @@
}

var tz = ServerTimeHelper.GetTimezoneLabel(ServerTimeHelper.CurrentDisplayMode);
ConnectionStatusText.Text = $"{_server.ServerNameDisplay} - Last refresh: {DateTime.Now:HH:mm:ss} ({tz})";

Check warning on line 656 in Lite/Controls/ServerTab.xaml.cs

View workflow job for this annotation

GitHub Actions / build

Dereference of a possibly null reference.

Check warning on line 656 in Lite/Controls/ServerTab.xaml.cs

View workflow job for this annotation

GitHub Actions / build

Dereference of a possibly null reference.
}
catch (Exception ex)
{
Expand Down Expand Up @@ -777,13 +777,13 @@
_querySnapshotsFilterMgr!.UpdateData(snapshotsTask.Result);
LiveSnapshotIndicator.Text = "";
_queryStatsFilterMgr!.UpdateData(queryStatsTask.Result);
SetInitialSort(QueryStatsGrid, "TotalElapsedMs", ListSortDirection.Descending);
SetDefaultSortIfNone(QueryStatsGrid, "TotalElapsedMs", ListSortDirection.Descending);
_procStatsFilterMgr!.UpdateData(procStatsTask.Result);
SetInitialSort(ProcedureStatsGrid, "TotalElapsedMs", ListSortDirection.Descending);
SetDefaultSortIfNone(ProcedureStatsGrid, "TotalElapsedMs", ListSortDirection.Descending);
_blockedProcessFilterMgr!.UpdateData(blockedProcessTask.Result);
_deadlockFilterMgr!.UpdateData(DeadlockProcessDetail.ParseFromRows(deadlockTask.Result));
_queryStoreFilterMgr!.UpdateData(queryStoreTask.Result);
SetInitialSort(QueryStoreGrid, "TotalDurationMs", ListSortDirection.Descending);
SetDefaultSortIfNone(QueryStoreGrid, "TotalDurationMs", ListSortDirection.Descending);
_serverConfigFilterMgr!.UpdateData(serverConfigTask.Result);
_databaseConfigFilterMgr!.UpdateData(databaseConfigTask.Result);
_dbScopedConfigFilterMgr!.UpdateData(databaseScopedConfigTask.Result);
Expand Down Expand Up @@ -892,19 +892,19 @@
case 2: // Top Queries by Duration
var queryStats = await _dataService.GetTopQueriesByCpuAsync(_serverId, hoursBack, 50, fromDate, toDate, UtcOffsetMinutes);
_queryStatsFilterMgr!.UpdateData(queryStats);
SetInitialSort(QueryStatsGrid, "TotalElapsedMs", ListSortDirection.Descending);
SetDefaultSortIfNone(QueryStatsGrid, "TotalElapsedMs", ListSortDirection.Descending);
_ = LoadQueryStatsSlicerAsync();
break;
case 3: // Top Procedures by Duration
var procStats = await _dataService.GetTopProceduresByCpuAsync(_serverId, hoursBack, 50, fromDate, toDate, UtcOffsetMinutes);
_procStatsFilterMgr!.UpdateData(procStats);
SetInitialSort(ProcedureStatsGrid, "TotalElapsedMs", ListSortDirection.Descending);
SetDefaultSortIfNone(ProcedureStatsGrid, "TotalElapsedMs", ListSortDirection.Descending);
_ = LoadProcStatsSlicerAsync();
break;
case 4: // Query Store by Duration
var qsData = await _dataService.GetQueryStoreTopQueriesAsync(_serverId, hoursBack, 50, fromDate, toDate);
_queryStoreFilterMgr!.UpdateData(qsData);
SetInitialSort(QueryStoreGrid, "TotalDurationMs", ListSortDirection.Descending);
SetDefaultSortIfNone(QueryStoreGrid, "TotalDurationMs", ListSortDirection.Descending);
_ = LoadQueryStoreSlicerAsync();
break;
}
Expand All @@ -931,13 +931,13 @@
_ = LoadActiveQueriesSlicerAsync();

_queryStatsFilterMgr!.UpdateData(queryStatsTask.Result);
SetInitialSort(QueryStatsGrid, "TotalElapsedMs", ListSortDirection.Descending);
SetDefaultSortIfNone(QueryStatsGrid, "TotalElapsedMs", ListSortDirection.Descending);
_ = LoadQueryStatsSlicerAsync();
_procStatsFilterMgr!.UpdateData(procStatsTask.Result);
SetInitialSort(ProcedureStatsGrid, "TotalElapsedMs", ListSortDirection.Descending);
SetDefaultSortIfNone(ProcedureStatsGrid, "TotalElapsedMs", ListSortDirection.Descending);
_ = LoadProcStatsSlicerAsync();
_queryStoreFilterMgr!.UpdateData(queryStoreTask.Result);
SetInitialSort(QueryStoreGrid, "TotalDurationMs", ListSortDirection.Descending);
SetDefaultSortIfNone(QueryStoreGrid, "TotalDurationMs", ListSortDirection.Descending);
_ = LoadQueryStoreSlicerAsync();

UpdateQueryDurationTrendChart(queryDurationTrendTask.Result);
Expand Down Expand Up @@ -4376,8 +4376,11 @@
return null;
}

private static void SetInitialSort(DataGrid grid, string bindingPath, ListSortDirection direction)
private static void SetDefaultSortIfNone(DataGrid grid, string bindingPath, ListSortDirection direction)
{
if (grid.Items.SortDescriptions.Count > 0) return;

grid.Items.SortDescriptions.Add(new SortDescription(bindingPath, direction));
foreach (var column in grid.Columns)
{
if (column is DataGridBoundColumn bc &&
Expand Down
33 changes: 29 additions & 4 deletions Lite/Services/DataGridFilterManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Media;
using PerformanceMonitorLite.Models;

Expand Down Expand Up @@ -45,15 +47,15 @@ public DataGridFilterManager(DataGrid dataGrid)

/// <summary>
/// Called when new data arrives (refresh cycle). Captures unfiltered data,
/// then re-applies any active filters.
/// then re-applies any active filters. Preserves user sort order.
/// </summary>
public void UpdateData(List<T> newData)
{
_unfilteredData = newData;

if (!HasActiveFilters())
{
_dataGrid.ItemsSource = newData;
SetItemsSourcePreservingSort(newData);
return;
}

Expand Down Expand Up @@ -85,7 +87,7 @@ private void ApplyFilters()

if (!HasActiveFilters())
{
_dataGrid.ItemsSource = _unfilteredData;
SetItemsSourcePreservingSort(_unfilteredData);
return;
}

Expand All @@ -99,7 +101,30 @@ private void ApplyFilters()
return true;
}).ToList();

_dataGrid.ItemsSource = filteredData;
SetItemsSourcePreservingSort(filteredData);
}

private void SetItemsSourcePreservingSort(System.Collections.IEnumerable? newSource)
{
var savedSorts = _dataGrid.Items.SortDescriptions.ToList();

_dataGrid.ItemsSource = newSource;

if (savedSorts.Count > 0)
{
foreach (var sort in savedSorts)
_dataGrid.Items.SortDescriptions.Add(sort);

foreach (var column in _dataGrid.Columns)
{
if (column is DataGridBoundColumn bc &&
bc.Binding is Binding b)
{
var match = savedSorts.FirstOrDefault(s => s.PropertyName == b.Path.Path);
column.SortDirection = match.PropertyName != null ? match.Direction : null;
}
}
}
}

/// <summary>
Expand Down
Loading