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
2 changes: 2 additions & 0 deletions Dashboard/MainWindow.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -886,6 +886,7 @@ private void CloseTab_Click(object sender, RoutedEventArgs e)
serverTab.AlertAcknowledged -= handler;
_alertAcknowledgedHandlers.Remove(tabId);
}
serverTab.CleanupOnClose();
}
_openTabs.Remove(tabId);
_tabBadges.Remove(tabId);
Expand Down Expand Up @@ -1092,6 +1093,7 @@ private async void RemoveServer_Click(object sender, RoutedEventArgs e)

if (_openTabs.TryGetValue(server.Id, out var tabItem))
{
if (tabItem.Content is ServerTab st) st.CleanupOnClose();
_openTabs.Remove(server.Id);
ServerTabControl.Items.Remove(tabItem);
}
Expand Down
22 changes: 18 additions & 4 deletions Dashboard/ServerTab.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,8 @@ private async void StartAutoRefreshLoop(int intervalSeconds)
if (cts.Token.IsCancellationRequested) break;
if (_isRefreshing) continue;

_isRefreshing = true;
_refreshStartedUtc = DateTime.UtcNow;
try
{
var sw = System.Diagnostics.Stopwatch.StartNew();
Expand All @@ -387,14 +389,17 @@ private async void StartAutoRefreshLoop(int intervalSeconds)
}
catch (OperationCanceledException) when (!cts.Token.IsCancellationRequested)
{
// SQL query cancelled or timed out, but our loop CTS is still alive — keep going
Logger.Error($"Auto-refresh query cancelled for {_serverConnection.DisplayName}, continuing loop");
}
catch (Exception ex) when (ex is not OperationCanceledException)
{
Logger.Error($"Auto-refresh error: {ex.Message}", ex);
StatusText.Text = "Auto-refresh error";
}
finally
{
_isRefreshing = false;
}
}
}
catch (OperationCanceledException)
Expand All @@ -405,9 +410,18 @@ private async void StartAutoRefreshLoop(int intervalSeconds)

private void ServerTab_Unloaded(object sender, RoutedEventArgs e)
{
// Don't cancel auto-refresh on tab switch — WPF fires Unloaded when
// a TabItem is deselected, not just when the control is destroyed.
// The loop is lightweight and should keep ticking in the background.
// WPF fires Unloaded on tab switch, not just destruction.
// Don't tear down state here — the auto-refresh loop and chart
// state must survive tab switches. Cleanup happens when the tab
// is actually removed from the TabControl (via CleanupOnClose).
}

/// <summary>
/// Full cleanup — call when the server tab is permanently removed, not on tab switch.
/// </summary>
public void CleanupOnClose()
{
_autoRefreshCts?.Cancel();
_autoRefreshTimer?.Stop();
_autoRefreshTimer = null;

Expand Down
Loading